/*
This file is part of Ext JS 4.2

Copyright (c) 2011-2013 Sencha Inc

Contact:  http://www.sencha.com/contact

GNU General Public License Usage
This file may be used under the terms of the GNU General Public License version 3.0 as
published by the Free Software Foundation and appearing in the file LICENSE included in the
packaging of this file.

Please review the following information to ensure the GNU General Public License version 3.0
requirements will be met: http://www.gnu.org/copyleft/gpl.html.

If you are unsure which license is appropriate for your use, please contact the sales department
at http://www.sencha.com/contact.

Build date: 2013-05-16 14:36:50 (f9be68accb407158ba2b1be2c226a6ce1f649314)
*/





var Ext = Ext || {};
Ext._startTime = new Date().getTime();
(function() {
    var global = this,
        objectPrototype = Object.prototype,
        toString = objectPrototype.toString,
        enumerables = true,
        enumerablesTest = {toString: 1},
        emptyFn = function () {},
        
        
        callOverrideParent = function () {
            var method = callOverrideParent.caller.caller; 
            return method.$owner.prototype[method.$name].apply(this, arguments);
        },
        i,
        nonWhitespaceRe = /\S/,
        ExtApp,
        iterableRe = /\[object\s*(?:Array|Arguments|\w*Collection|\w*List|HTML\s+document\.all\s+class)\]/;

    Function.prototype.$extIsFunction = true;

    Ext.global = global;

    for (i in enumerablesTest) {
        enumerables = null;
    }

    if (enumerables) {
        enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
                       'toLocaleString', 'toString', 'constructor'];
    }

    
    Ext.enumerables = enumerables;

    
    Ext.apply = function(object, config, defaults) {
        if (defaults) {
            Ext.apply(object, defaults);
        }

        if (object && config && typeof config === 'object') {
            var i, j, k;

            for (i in config) {
                object[i] = config[i];
            }

            if (enumerables) {
                for (j = enumerables.length; j--;) {
                    k = enumerables[j];
                    if (config.hasOwnProperty(k)) {
                        object[k] = config[k];
                    }
                }
            }
        }

        return object;
    };

    Ext.buildSettings = Ext.apply({
        baseCSSPrefix: 'x-'
    }, Ext.buildSettings || {});

    Ext.apply(Ext, {

        
        name: Ext.sandboxName || 'Ext',

        
        emptyFn: emptyFn,
        
        
        identityFn: function(o) {
            return o;
        },

        
        emptyString: new String(),

        baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,

        
        applyIf: function(object, config) {
            var property;

            if (object) {
                for (property in config) {
                    if (object[property] === undefined) {
                        object[property] = config[property];
                    }
                }
            }

            return object;
        },

        
        iterate: function(object, fn, scope) {
            if (Ext.isEmpty(object)) {
                return;
            }

            if (scope === undefined) {
                scope = object;
            }

            if (Ext.isIterable(object)) {
                Ext.Array.each.call(Ext.Array, object, fn, scope);
            }
            else {
                Ext.Object.each.call(Ext.Object, object, fn, scope);
            }
        }
    });

    Ext.apply(Ext, {

        
        extend: (function() {
            
            var objectConstructor = objectPrototype.constructor,
                inlineOverrides = function(o) {
                for (var m in o) {
                    if (!o.hasOwnProperty(m)) {
                        continue;
                    }
                    this[m] = o[m];
                }
            };

            return function(subclass, superclass, overrides) {
                
                if (Ext.isObject(superclass)) {
                    overrides = superclass;
                    superclass = subclass;
                    subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
                        superclass.apply(this, arguments);
                    };
                }


                
                var F = function() {},
                    subclassProto, superclassProto = superclass.prototype;

                F.prototype = superclassProto;
                subclassProto = subclass.prototype = new F();
                subclassProto.constructor = subclass;
                subclass.superclass = superclassProto;

                if (superclassProto.constructor === objectConstructor) {
                    superclassProto.constructor = superclass;
                }

                subclass.override = function(overrides) {
                    Ext.override(subclass, overrides);
                };

                subclassProto.override = inlineOverrides;
                subclassProto.proto = subclassProto;

                subclass.override(overrides);
                subclass.extend = function(o) {
                    return Ext.extend(subclass, o);
                };

                return subclass;
            };
        }()),

        
        override: function (target, overrides) {
            if (target.$isClass) {
                target.override(overrides);
            } else if (typeof target == 'function') {
                Ext.apply(target.prototype, overrides);
            } else {
                var owner = target.self,
                    name, value;

                if (owner && owner.$isClass) { 
                    for (name in overrides) {
                        if (overrides.hasOwnProperty(name)) {
                            value = overrides[name];

                            if (typeof value == 'function') {

                                value.$name = name;
                                value.$owner = owner;
                                value.$previous = target.hasOwnProperty(name)
                                    ? target[name] 
                                    : callOverrideParent; 
                            }

                            target[name] = value;
                        }
                    }
                } else {
                    Ext.apply(target, overrides);
                }
            }

            return target;
        }
    });

    
    Ext.apply(Ext, {

        
        valueFrom: function(value, defaultValue, allowBlank){
            return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
        },

        
        typeOf: function(value) {
            var type,
                typeToString;
            
            if (value === null) {
                return 'null';
            }

            type = typeof value;

            if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
                return type;
            }

            typeToString = toString.call(value);

            switch(typeToString) {
                case '[object Array]':
                    return 'array';
                case '[object Date]':
                    return 'date';
                case '[object Boolean]':
                    return 'boolean';
                case '[object Number]':
                    return 'number';
                case '[object RegExp]':
                    return 'regexp';
            }

            if (type === 'function') {
                return 'function';
            }

            if (type === 'object') {
                if (value.nodeType !== undefined) {
                    if (value.nodeType === 3) {
                        return (nonWhitespaceRe).test(value.nodeValue) ? 'textnode' : 'whitespace';
                    }
                    else {
                        return 'element';
                    }
                }

                return 'object';
            }

        },

        
        coerce: function(from, to) {
            var fromType = Ext.typeOf(from),
                toType = Ext.typeOf(to),
                isString = typeof from === 'string';

            if (fromType !== toType) {
                switch (toType) {
                    case 'string':
                        return String(from);
                    case 'number':
                        return Number(from);
                    case 'boolean':
                        return isString && (!from || from === 'false') ? false : Boolean(from);
                    case 'null':
                        return isString && (!from || from === 'null') ? null : from;
                    case 'undefined':
                        return isString && (!from || from === 'undefined') ? undefined : from;
                    case 'date':
                        return isString && isNaN(from) ? Ext.Date.parse(from, Ext.Date.defaultFormat) : Date(Number(from));
                }
            }
            return from;
        },

        
        isEmpty: function(value, allowEmptyString) {
            return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
        },

        
        isArray: ('isArray' in Array) ? Array.isArray : function(value) {
            return toString.call(value) === '[object Array]';
        },

        
        isDate: function(value) {
            return toString.call(value) === '[object Date]';
        },

        
        isObject: (toString.call(null) === '[object Object]') ?
        function(value) {
            
            return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
        } :
        function(value) {
            return toString.call(value) === '[object Object]';
        },

        
        isSimpleObject: function(value) {
            return value instanceof Object && value.constructor === Object;
        },
        
        isPrimitive: function(value) {
            var type = typeof value;

            return type === 'string' || type === 'number' || type === 'boolean';
        },

        
        isFunction: function(value) {
            return !!(value && value.$extIsFunction);
        },

        
        isNumber: function(value) {
            return typeof value === 'number' && isFinite(value);
        },

        
        isNumeric: function(value) {
            return !isNaN(parseFloat(value)) && isFinite(value);
        },

        
        isString: function(value) {
            return typeof value === 'string';
        },

        
        isBoolean: function(value) {
            return typeof value === 'boolean';
        },

        
        isElement: function(value) {
            return value ? value.nodeType === 1 : false;
        },

        
        isTextNode: function(value) {
            return value ? value.nodeName === "#text" : false;
        },

        
        isDefined: function(value) {
            return typeof value !== 'undefined';
        },

        
        isIterable: function(value) {
            
            if (!value || typeof value.length !== 'number' || typeof value === 'string' || value.$extIsFunction) {
                return false;
            }

            
            
            
            if (!value.propertyIsEnumerable) {
                return !!value.item;
            }

            
            
            if (value.hasOwnProperty('length') && !value.propertyIsEnumerable('length')) {
                return true;
            }

            
            return iterableRe.test(toString.call(value));
        }
    });

    Ext.apply(Ext, {

        
        clone: function(item) {
            var type,
                i,
                j,
                k,
                clone,
                key;
            
            if (item === null || item === undefined) {
                return item;
            }

            
            
            
            if (item.nodeType && item.cloneNode) {
                return item.cloneNode(true);
            }

            type = toString.call(item);

            
            if (type === '[object Date]') {
                return new Date(item.getTime());
            }


            
            if (type === '[object Array]') {
                i = item.length;

                clone = [];

                while (i--) {
                    clone[i] = Ext.clone(item[i]);
                }
            }
            
            else if (type === '[object Object]' && item.constructor === Object) {
                clone = {};

                for (key in item) {
                    clone[key] = Ext.clone(item[key]);
                }

                if (enumerables) {
                    for (j = enumerables.length; j--;) {
                        k = enumerables[j];
                        if (item.hasOwnProperty(k)) {
                            clone[k] = item[k];
                        }
                    }
                }
            }

            return clone || item;
        },

        
        getUniqueGlobalNamespace: function() {
            var uniqueGlobalNamespace = this.uniqueGlobalNamespace,
                i;

            if (uniqueGlobalNamespace === undefined) {
                i = 0;

                do {
                    uniqueGlobalNamespace = 'ExtBox' + (++i);
                } while (Ext.global[uniqueGlobalNamespace] !== undefined);

                Ext.global[uniqueGlobalNamespace] = Ext;
                this.uniqueGlobalNamespace = uniqueGlobalNamespace;
            }

            return uniqueGlobalNamespace;
        },
        
        
        functionFactoryCache: {},
        
        cacheableFunctionFactory: function() {
            var me = this,
                args = Array.prototype.slice.call(arguments),
                cache = me.functionFactoryCache,
                idx, fn, ln;
                
             if (Ext.isSandboxed) {
                ln = args.length;
                if (ln > 0) {
                    ln--;
                    args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
                }
            }
            idx = args.join('');
            fn = cache[idx];
            if (!fn) {
                fn = Function.prototype.constructor.apply(Function.prototype, args);
                
                cache[idx] = fn;
            }
            return fn;
        },
        
        functionFactory: function() {
            var me = this,
                args = Array.prototype.slice.call(arguments),
                ln;
                
            if (Ext.isSandboxed) {
                ln = args.length;
                if (ln > 0) {
                    ln--;
                    args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
                }
            }
     
            return Function.prototype.constructor.apply(Function.prototype, args);
        },

        
        Logger: {
            verbose: emptyFn,
            log: emptyFn,
            info: emptyFn,
            warn: emptyFn,
            error: function(message) {
                throw new Error(message);
            },
            deprecate: emptyFn
        }
    });

    
    Ext.type = Ext.typeOf;
    
    
    
    
    ExtApp = Ext.app;
    if (!ExtApp) {
        ExtApp = Ext.app = {};
    }
    Ext.apply(ExtApp, {
        namespaces: {},
        
        
        collectNamespaces: function(paths) {
            var namespaces = Ext.app.namespaces,
                path;
            
            for (path in paths) {
                if (paths.hasOwnProperty(path)) {
                    namespaces[path] = true;
                }
            }
        },

        
        addNamespaces: function(ns) {
            var namespaces = Ext.app.namespaces,
                i, l;

            if (!Ext.isArray(ns)) {
                ns = [ns];
            }

            for (i = 0, l = ns.length; i < l; i++) {
                namespaces[ns[i]] = true;
            }
        },

        
        clearNamespaces: function() {
            Ext.app.namespaces = {};
        },

        
        getNamespace: function(className) {
            var namespaces    = Ext.app.namespaces,
                deepestPrefix = '',
                prefix;

            for (prefix in namespaces) {
                if (namespaces.hasOwnProperty(prefix)    &&
                    prefix.length > deepestPrefix.length &&
                    (prefix + '.' === className.substring(0, prefix.length + 1))) {
                    deepestPrefix = prefix;
                }
            }

            return deepestPrefix === '' ? undefined : deepestPrefix;
        }
    });
}());


Ext.globalEval = Ext.global.execScript
    ? function(code) {
        execScript(code);
    }
    : function($$code) {
        
        
        (function(){
            
            
            
            var Ext = this.Ext;
            eval($$code);
        }());
    };






(function() {



var version = '4.2.1.883', Version;
    Ext.Version = Version = Ext.extend(Object, {

        
        constructor: function(version) {
            var parts, releaseStartIndex;

            if (version instanceof Version) {
                return version;
            }

            this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');

            releaseStartIndex = this.version.search(/([^\d\.])/);

            if (releaseStartIndex !== -1) {
                this.release = this.version.substr(releaseStartIndex, version.length);
                this.shortVersion = this.version.substr(0, releaseStartIndex);
            }

            this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');

            parts = this.version.split('.');

            this.major = parseInt(parts.shift() || 0, 10);
            this.minor = parseInt(parts.shift() || 0, 10);
            this.patch = parseInt(parts.shift() || 0, 10);
            this.build = parseInt(parts.shift() || 0, 10);

            return this;
        },

        
        toString: function() {
            return this.version;
        },

        
        valueOf: function() {
            return this.version;
        },

        
        getMajor: function() {
            return this.major || 0;
        },

        
        getMinor: function() {
            return this.minor || 0;
        },

        
        getPatch: function() {
            return this.patch || 0;
        },

        
        getBuild: function() {
            return this.build || 0;
        },

        
        getRelease: function() {
            return this.release || '';
        },

        
        isGreaterThan: function(target) {
            return Version.compare(this.version, target) === 1;
        },

        
        isGreaterThanOrEqual: function(target) {
            return Version.compare(this.version, target) >= 0;
        },

        
        isLessThan: function(target) {
            return Version.compare(this.version, target) === -1;
        },

        
        isLessThanOrEqual: function(target) {
            return Version.compare(this.version, target) <= 0;
        },

        
        equals: function(target) {
            return Version.compare(this.version, target) === 0;
        },

        
        match: function(target) {
            target = String(target);
            return this.version.substr(0, target.length) === target;
        },

        
        toArray: function() {
            return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
        },

        
        getShortVersion: function() {
            return this.shortVersion;
        },

        
        gt: function() {
            return this.isGreaterThan.apply(this, arguments);
        },

        
        lt: function() {
            return this.isLessThan.apply(this, arguments);
        },

        
        gtEq: function() {
            return this.isGreaterThanOrEqual.apply(this, arguments);
        },

        
        ltEq: function() {
            return this.isLessThanOrEqual.apply(this, arguments);
        }
    });

    Ext.apply(Version, {
        
        releaseValueMap: {
            'dev': -6,
            'alpha': -5,
            'a': -5,
            'beta': -4,
            'b': -4,
            'rc': -3,
            '#': -2,
            'p': -1,
            'pl': -1
        },

        
        getComponentValue: function(value) {
            return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
        },

        
        compare: function(current, target) {
            var currentValue, targetValue, i;

            current = new Version(current).toArray();
            target = new Version(target).toArray();

            for (i = 0; i < Math.max(current.length, target.length); i++) {
                currentValue = this.getComponentValue(current[i]);
                targetValue = this.getComponentValue(target[i]);

                if (currentValue < targetValue) {
                    return -1;
                } else if (currentValue > targetValue) {
                    return 1;
                }
            }

            return 0;
        }
    });

    
    Ext.apply(Ext, {
        
        versions: {},

        
        lastRegisteredVersion: null,

        
        setVersion: function(packageName, version) {
            Ext.versions[packageName] = new Version(version);
            Ext.lastRegisteredVersion = Ext.versions[packageName];

            return this;
        },

        
        getVersion: function(packageName) {
            if (packageName === undefined) {
                return Ext.lastRegisteredVersion;
            }

            return Ext.versions[packageName];
        },

        
        deprecate: function(packageName, since, closure, scope) {
            if (Version.compare(Ext.getVersion(packageName), since) < 1) {
                closure.call(scope);
            }
        }
    }); 

    Ext.setVersion('core', version);

}());







Ext.String = (function() {
    var trimRegex     = /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
        escapeRe      = /('|\\)/g,
        formatRe      = /\{(\d+)\}/g,
        escapeRegexRe = /([-.*+?\^${}()|\[\]\/\\])/g,
        basicTrimRe   = /^\s+|\s+$/g,
        whitespaceRe  = /\s+/,
        varReplace    = /(^[^a-z]*|[^\w])/gi,
        charToEntity,
        entityToChar,
        charToEntityRegex,
        entityToCharRegex,
        htmlEncodeReplaceFn = function(match, capture) {
            return charToEntity[capture];
        },
        htmlDecodeReplaceFn = function(match, capture) {
            return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
        },
        boundsCheck = function(s, other){
            if (s === null || s === undefined || other === null || other === undefined) {
                return false;
            }
            
            return other.length <= s.length; 
        };

    return {
        
        
        insert: function(s, value, index) {
            if (!s) {
                return value;
            }
            
            if (!value) {
                return s;
            }
            
            var len = s.length;
            
            if (!index && index !== 0) {
                index = len;
            }
            
            if (index < 0) {
                index *= -1;
                if (index >= len) {
                    
                    index = 0;
                } else {
                    index = len - index;
                }
            }
            
            if (index === 0) {
                s = value + s;
            } else if (index >= s.length) {
                s += value;
            } else {
                s = s.substr(0, index) + value + s.substr(index);
            }
            return s;
        },
        
        
        startsWith: function(s, start, ignoreCase){
            var result = boundsCheck(s, start);
            
            if (result) {
                if (ignoreCase) {
                    s = s.toLowerCase();
                    start = start.toLowerCase();
                }
                result = s.lastIndexOf(start, 0) === 0;
            }
            return result;
        },
        
        
        endsWith: function(s, end, ignoreCase){
            var result = boundsCheck(s, end);
            
            if (result) {
                if (ignoreCase) {
                    s = s.toLowerCase();
                    end = end.toLowerCase();
                }
                result = s.indexOf(end, s.length - end.length) !== -1;
            }
            return result;
        },

        
        createVarName: function(s) {
            return s.replace(varReplace, '');
        },

        
        htmlEncode: function(value) {
            return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
        },

        
        htmlDecode: function(value) {
            return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
        },

        
        addCharacterEntities: function(newEntities) {
            var charKeys = [],
                entityKeys = [],
                key, echar;
            for (key in newEntities) {
                echar = newEntities[key];
                entityToChar[key] = echar;
                charToEntity[echar] = key;
                charKeys.push(echar);
                entityKeys.push(key);
            }
            charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
            entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
        },

        
        resetCharacterEntities: function() {
            charToEntity = {};
            entityToChar = {};
            
            this.addCharacterEntities({
                '&amp;'     :   '&',
                '&gt;'      :   '>',
                '&lt;'      :   '<',
                '&quot;'    :   '"',
                '&#39;'     :   "'"
            });
        },

        
        urlAppend : function(url, string) {
            if (!Ext.isEmpty(string)) {
                return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
            }

            return url;
        },

        
        trim: function(string) {
            return string.replace(trimRegex, "");
        },

        
        capitalize: function(string) {
            return string.charAt(0).toUpperCase() + string.substr(1);
        },

        
        uncapitalize: function(string) {
            return string.charAt(0).toLowerCase() + string.substr(1);
        },

        
        ellipsis: function(value, len, word) {
            if (value && value.length > len) {
                if (word) {
                    var vs = value.substr(0, len - 2),
                    index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
                    if (index !== -1 && index >= (len - 15)) {
                        return vs.substr(0, index) + "...";
                    }
                }
                return value.substr(0, len - 3) + "...";
            }
            return value;
        },

        
        escapeRegex: function(string) {
            return string.replace(escapeRegexRe, "\\$1");
        },

        
        escape: function(string) {
            return string.replace(escapeRe, "\\$1");
        },

        
        toggle: function(string, value, other) {
            return string === value ? other : value;
        },

        
        leftPad: function(string, size, character) {
            var result = String(string);
            character = character || " ";
            while (result.length < size) {
                result = character + result;
            }
            return result;
        },

        
        format: function(format) {
            var args = Ext.Array.toArray(arguments, 1);
            return format.replace(formatRe, function(m, i) {
                return args[i];
            });
        },

        
        repeat: function(pattern, count, sep) {
            if (count < 1) {
                count = 0;
            }
            for (var buf = [], i = count; i--; ) {
                buf.push(pattern);
            }
            return buf.join(sep || '');
        },

        
        splitWords: function (words) {
            if (words && typeof words == 'string') {
                return words.replace(basicTrimRe, '').split(whitespaceRe);
            }
            return words || [];
        }
    };
}());


Ext.String.resetCharacterEntities();


Ext.htmlEncode = Ext.String.htmlEncode;



Ext.htmlDecode = Ext.String.htmlDecode;


Ext.urlAppend = Ext.String.urlAppend;







Ext.Number = new function() {

    var me = this,
        isToFixedBroken = (0.9).toFixed() !== '1',
        math = Math;

    Ext.apply(this, {
        
        constrain: function(number, min, max) {
            var x = parseFloat(number);

            
            

            
            
            
            
            
            return (x < min) ? min : ((x > max) ? max : x);
        },

        
        snap : function(value, increment, minValue, maxValue) {
            var m;

            
            
            if (value === undefined || value < minValue) {
                return minValue || 0;
            }

            if (increment) {
                m = value % increment;
                if (m !== 0) {
                    value -= m;
                    if (m * 2 >= increment) {
                        value += increment;
                    } else if (m * 2 < -increment) {
                        value -= increment;
                    }
                }
            }
            return me.constrain(value, minValue,  maxValue);
        },

        
        snapInRange : function(value, increment, minValue, maxValue) {
            var tween;

            
            minValue = (minValue || 0);

            
            if (value === undefined || value < minValue) {
                return minValue;
            }

            
            if (increment && (tween = ((value - minValue) % increment))) {
                value -= tween;
                tween *= 2;
                if (tween >= increment) {
                    value += increment;
                }
            }

            
            if (maxValue !== undefined) {
                if (value > (maxValue = me.snapInRange(maxValue, increment, minValue))) {
                    value = maxValue;
                }
            }

            return value;
        },

        
        toFixed: isToFixedBroken ? function(value, precision) {
            precision = precision || 0;
            var pow = math.pow(10, precision);
            return (math.round(value * pow) / pow).toFixed(precision);
        } : function(value, precision) {
            return value.toFixed(precision);
        },

        
        from: function(value, defaultValue) {
            if (isFinite(value)) {
                value = parseFloat(value);
            }

            return !isNaN(value) ? value : defaultValue;
        },

        
        randomInt: function (from, to) {
           return math.floor(math.random() * (to - from + 1) + from);
        },
        
        
        correctFloat: function(n) {
            
            
            
            return parseFloat(n.toPrecision(14));
        }
    });

    
    Ext.num = function() {
        return me.from.apply(this, arguments);
    };
};






(function() {

    var arrayPrototype = Array.prototype,
        slice = arrayPrototype.slice,
        supportsSplice = (function () {
            var array = [],
                lengthBefore,
                j = 20;

            if (!array.splice) {
                return false;
            }

            
            

            while (j--) {
                array.push("A");
            }

            array.splice(15, 0, "F", "F", "F", "F", "F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F");

            lengthBefore = array.length; 
            array.splice(13, 0, "XXX"); 

            if (lengthBefore+1 != array.length) {
                return false;
            }
            

            return true;
        }()),
        supportsForEach = 'forEach' in arrayPrototype,
        supportsMap = 'map' in arrayPrototype,
        supportsIndexOf = 'indexOf' in arrayPrototype,
        supportsEvery = 'every' in arrayPrototype,
        supportsSome = 'some' in arrayPrototype,
        supportsFilter = 'filter' in arrayPrototype,
        supportsSort = (function() {
            var a = [1,2,3,4,5].sort(function(){ return 0; });
            return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
        }()),
        supportsSliceOnNodeList = true,
        ExtArray,
        erase,
        replace,
        splice;

    try {
        
        if (typeof document !== 'undefined') {
            slice.call(document.getElementsByTagName('body'));
        }
    } catch (e) {
        supportsSliceOnNodeList = false;
    }

    function fixArrayIndex (array, index) {
        return (index < 0) ? Math.max(0, array.length + index)
                           : Math.min(array.length, index);
    }

    
    function replaceSim (array, index, removeCount, insert) {
        var add = insert ? insert.length : 0,
            length = array.length,
            pos = fixArrayIndex(array, index),
            remove,
            tailOldPos,
            tailNewPos,
            tailCount,
            lengthAfterRemove,
            i;

        
        if (pos === length) {
            if (add) {
                array.push.apply(array, insert);
            }
        } else {
            remove = Math.min(removeCount, length - pos);
            tailOldPos = pos + remove;
            tailNewPos = tailOldPos + add - remove;
            tailCount = length - tailOldPos;
            lengthAfterRemove = length - remove;

            if (tailNewPos < tailOldPos) { 
                for (i = 0; i < tailCount; ++i) {
                    array[tailNewPos+i] = array[tailOldPos+i];
                }
            } else if (tailNewPos > tailOldPos) { 
                for (i = tailCount; i--; ) {
                    array[tailNewPos+i] = array[tailOldPos+i];
                }
            } 

            if (add && pos === lengthAfterRemove) {
                array.length = lengthAfterRemove; 
                array.push.apply(array, insert);
            } else {
                array.length = lengthAfterRemove + add; 
                for (i = 0; i < add; ++i) {
                    array[pos+i] = insert[i];
                }
            }
        }

        return array;
    }

    function replaceNative (array, index, removeCount, insert) {
        if (insert && insert.length) {
            
            if (index === 0 && !removeCount) {
                array.unshift.apply(array, insert);
            }
            
            else if (index < array.length) {
                array.splice.apply(array, [index, removeCount].concat(insert));
            }
            
            else {
                array.push.apply(array, insert);
            }
        } else {
            array.splice(index, removeCount);
        }
        return array;
    }

    function eraseSim (array, index, removeCount) {
        return replaceSim(array, index, removeCount);
    }

    function eraseNative (array, index, removeCount) {
        array.splice(index, removeCount);
        return array;
    }

    function spliceSim (array, index, removeCount) {
        var pos = fixArrayIndex(array, index),
            removed = array.slice(index, fixArrayIndex(array, pos+removeCount));

        if (arguments.length < 4) {
            replaceSim(array, pos, removeCount);
        } else {
            replaceSim(array, pos, removeCount, slice.call(arguments, 3));
        }

        return removed;
    }

    function spliceNative (array) {
        return array.splice.apply(array, slice.call(arguments, 1));
    }

    erase = supportsSplice ? eraseNative : eraseSim;
    replace = supportsSplice ? replaceNative : replaceSim;
    splice = supportsSplice ? spliceNative : spliceSim;

    

    ExtArray = Ext.Array = {
        
        each: function(array, fn, scope, reverse) {
            array = ExtArray.from(array);

            var i,
                ln = array.length;

            if (reverse !== true) {
                for (i = 0; i < ln; i++) {
                    if (fn.call(scope || array[i], array[i], i, array) === false) {
                        return i;
                    }
                }
            }
            else {
                for (i = ln - 1; i > -1; i--) {
                    if (fn.call(scope || array[i], array[i], i, array) === false) {
                        return i;
                    }
                }
            }

            return true;
        },

        
        forEach: supportsForEach ? function(array, fn, scope) {
            array.forEach(fn, scope);
        } : function(array, fn, scope) {
            var i = 0,
                ln = array.length;

            for (; i < ln; i++) {
                fn.call(scope, array[i], i, array);
            }
        },

        
        indexOf: supportsIndexOf ? function(array, item, from) {
            return arrayPrototype.indexOf.call(array, item, from);
         } : function(array, item, from) {
            var i, length = array.length;

            for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
                if (array[i] === item) {
                    return i;
                }
            }

            return -1;
        },

        
        contains: supportsIndexOf ? function(array, item) {
            return arrayPrototype.indexOf.call(array, item) !== -1;
        } : function(array, item) {
            var i, ln;

            for (i = 0, ln = array.length; i < ln; i++) {
                if (array[i] === item) {
                    return true;
                }
            }

            return false;
        },

        
        toArray: function(iterable, start, end){
            if (!iterable || !iterable.length) {
                return [];
            }

            if (typeof iterable === 'string') {
                iterable = iterable.split('');
            }

            if (supportsSliceOnNodeList) {
                return slice.call(iterable, start || 0, end || iterable.length);
            }

            var array = [],
                i;

            start = start || 0;
            end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;

            for (i = start; i < end; i++) {
                array.push(iterable[i]);
            }

            return array;
        },

        
        pluck: function(array, propertyName) {
            var ret = [],
                i, ln, item;

            for (i = 0, ln = array.length; i < ln; i++) {
                item = array[i];

                ret.push(item[propertyName]);
            }

            return ret;
        },

        
        map: supportsMap ? function(array, fn, scope) {
            return array.map(fn, scope);
        } : function(array, fn, scope) {
            var results = [],
                i = 0,
                len = array.length;

            for (; i < len; i++) {
                results[i] = fn.call(scope, array[i], i, array);
            }

            return results;
        },

        
        every: supportsEvery ? function(array, fn, scope) {
            return array.every(fn, scope);
        } : function(array, fn, scope) {
            var i = 0,
                ln = array.length;

            for (; i < ln; ++i) {
                if (!fn.call(scope, array[i], i, array)) {
                    return false;
                }
            }

            return true;
        },

        
        some: supportsSome ? function(array, fn, scope) {
            return array.some(fn, scope);
        } : function(array, fn, scope) {
            var i = 0,
                ln = array.length;

            for (; i < ln; ++i) {
                if (fn.call(scope, array[i], i, array)) {
                    return true;
                }
            }

            return false;
        },
        
        
        equals: function(array1, array2) {
            var len1 = array1.length,
                len2 = array2.length,
                i;
                
            
            if (array1 === array2) {
                return true;
            }
                
            if (len1 !== len2) {
                return false;
            }
            
            for (i = 0; i < len1; ++i) {
                if (array1[i] !== array2[i]) {
                    return false;
                }
            }
            
            return true;
        },

        
        clean: function(array) {
            var results = [],
                i = 0,
                ln = array.length,
                item;

            for (; i < ln; i++) {
                item = array[i];

                if (!Ext.isEmpty(item)) {
                    results.push(item);
                }
            }

            return results;
        },

        
        unique: function(array) {
            var clone = [],
                i = 0,
                ln = array.length,
                item;

            for (; i < ln; i++) {
                item = array[i];

                if (ExtArray.indexOf(clone, item) === -1) {
                    clone.push(item);
                }
            }

            return clone;
        },

        
        filter: supportsFilter ? function(array, fn, scope) {
            return array.filter(fn, scope);
        } : function(array, fn, scope) {
            var results = [],
                i = 0,
                ln = array.length;

            for (; i < ln; i++) {
                if (fn.call(scope, array[i], i, array)) {
                    results.push(array[i]);
                }
            }

            return results;
        },

        
        findBy : function(array, fn, scope) {
            var i = 0,
                len = array.length;

            for (; i < len; i++) {
                if (fn.call(scope || array, array[i], i)) {
                    return array[i];
                }
            }
            return null;
        },

        
        from: function(value, newReference) {
            if (value === undefined || value === null) {
                return [];
            }

            if (Ext.isArray(value)) {
                return (newReference) ? slice.call(value) : value;
            }

            var type = typeof value;
            
            
            if (value && value.length !== undefined && type !== 'string' && (type !== 'function' || !value.apply)) {
                return ExtArray.toArray(value);
            }

            return [value];
        },

        
        remove: function(array, item) {
            var index = ExtArray.indexOf(array, item);

            if (index !== -1) {
                erase(array, index, 1);
            }

            return array;
        },

        
        include: function(array, item) {
            if (!ExtArray.contains(array, item)) {
                array.push(item);
            }
        },

        
        clone: function(array) {
            return slice.call(array);
        },

        
        merge: function() {
            var args = slice.call(arguments),
                array = [],
                i, ln;

            for (i = 0, ln = args.length; i < ln; i++) {
                array = array.concat(args[i]);
            }

            return ExtArray.unique(array);
        },

        
        intersect: function() {
            var intersection = [],
                arrays = slice.call(arguments),
                arraysLength,
                array,
                arrayLength,
                minArray,
                minArrayIndex,
                minArrayCandidate,
                minArrayLength,
                element,
                elementCandidate,
                elementCount,
                i, j, k;

            if (!arrays.length) {
                return intersection;
            }

            
            arraysLength = arrays.length;
            for (i = minArrayIndex = 0; i < arraysLength; i++) {
                minArrayCandidate = arrays[i];
                if (!minArray || minArrayCandidate.length < minArray.length) {
                    minArray = minArrayCandidate;
                    minArrayIndex = i;
                }
            }

            minArray = ExtArray.unique(minArray);
            erase(arrays, minArrayIndex, 1);

            
            
            
            minArrayLength = minArray.length;
            arraysLength = arrays.length;
            for (i = 0; i < minArrayLength; i++) {
                element = minArray[i];
                elementCount = 0;

                for (j = 0; j < arraysLength; j++) {
                    array = arrays[j];
                    arrayLength = array.length;
                    for (k = 0; k < arrayLength; k++) {
                        elementCandidate = array[k];
                        if (element === elementCandidate) {
                            elementCount++;
                            break;
                        }
                    }
                }

                if (elementCount === arraysLength) {
                    intersection.push(element);
                }
            }

            return intersection;
        },

        
        difference: function(arrayA, arrayB) {
            var clone = slice.call(arrayA),
                ln = clone.length,
                i, j, lnB;

            for (i = 0,lnB = arrayB.length; i < lnB; i++) {
                for (j = 0; j < ln; j++) {
                    if (clone[j] === arrayB[i]) {
                        erase(clone, j, 1);
                        j--;
                        ln--;
                    }
                }
            }

            return clone;
        },

        
        
        slice: ([1,2].slice(1, undefined).length ?
            function (array, begin, end) {
                return slice.call(array, begin, end);
            } :
            
            function (array, begin, end) {
                
                
                if (typeof begin === 'undefined') {
                    return slice.call(array);
                }
                if (typeof end === 'undefined') {
                    return slice.call(array, begin);
                }
                return slice.call(array, begin, end);
            }
        ),

        
        sort: supportsSort ? function(array, sortFn) {
            if (sortFn) {
                return array.sort(sortFn);
            } else {
                return array.sort();
            }
         } : function(array, sortFn) {
            var length = array.length,
                i = 0,
                comparison,
                j, min, tmp;

            for (; i < length; i++) {
                min = i;
                for (j = i + 1; j < length; j++) {
                    if (sortFn) {
                        comparison = sortFn(array[j], array[min]);
                        if (comparison < 0) {
                            min = j;
                        }
                    } else if (array[j] < array[min]) {
                        min = j;
                    }
                }
                if (min !== i) {
                    tmp = array[i];
                    array[i] = array[min];
                    array[min] = tmp;
                }
            }

            return array;
        },

        
        flatten: function(array) {
            var worker = [];

            function rFlatten(a) {
                var i, ln, v;

                for (i = 0, ln = a.length; i < ln; i++) {
                    v = a[i];

                    if (Ext.isArray(v)) {
                        rFlatten(v);
                    } else {
                        worker.push(v);
                    }
                }

                return worker;
            }

            return rFlatten(array);
        },

        
        min: function(array, comparisonFn) {
            var min = array[0],
                i, ln, item;

            for (i = 0, ln = array.length; i < ln; i++) {
                item = array[i];

                if (comparisonFn) {
                    if (comparisonFn(min, item) === 1) {
                        min = item;
                    }
                }
                else {
                    if (item < min) {
                        min = item;
                    }
                }
            }

            return min;
        },

        
        max: function(array, comparisonFn) {
            var max = array[0],
                i, ln, item;

            for (i = 0, ln = array.length; i < ln; i++) {
                item = array[i];

                if (comparisonFn) {
                    if (comparisonFn(max, item) === -1) {
                        max = item;
                    }
                }
                else {
                    if (item > max) {
                        max = item;
                    }
                }
            }

            return max;
        },

        
        mean: function(array) {
            return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
        },

        
        sum: function(array) {
            var sum = 0,
                i, ln, item;

            for (i = 0,ln = array.length; i < ln; i++) {
                item = array[i];

                sum += item;
            }

            return sum;
        },

        
        toMap: function(array, getKey, scope) {
            var map = {},
                i = array.length;

            if (!getKey) {
                while (i--) {
                    map[array[i]] = i+1;
                }
            } else if (typeof getKey == 'string') {
                while (i--) {
                    map[array[i][getKey]] = i+1;
                }
            } else {
                while (i--) {
                    map[getKey.call(scope, array[i])] = i+1;
                }
            }

            return map;
        },

        
        toValueMap: function(array, getKey, scope) {
            var map = {},
                i = array.length;

            if (!getKey) {
                while (i--) {
                    map[array[i]] = array[i];
                }
            } else if (typeof getKey == 'string') {
                while (i--) {
                    map[array[i][getKey]] = array[i];
                }
            } else {
                while (i--) {
                    map[getKey.call(scope, array[i])] = array[i];
                }
            }

            return map;
        },


        
        erase: erase,

        
        insert: function (array, index, items) {
            return replace(array, index, 0, items);
        },

        
        replace: replace,

        
        splice: splice,

        
        push: function(array) {
            var len = arguments.length,
                i = 1,
                newItem;

            if (array === undefined) {
                array = [];
            } else if (!Ext.isArray(array)) {
                array = [array];
            }
            for (; i < len; i++) {
                newItem = arguments[i];
                Array.prototype.push[Ext.isIterable(newItem) ? 'apply' : 'call'](array, newItem);
            }
            return array;
        }
    };

    
    Ext.each = ExtArray.each;

    
    ExtArray.union = ExtArray.merge;

    
    Ext.min = ExtArray.min;

    
    Ext.max = ExtArray.max;

    
    Ext.sum = ExtArray.sum;

    
    Ext.mean = ExtArray.mean;

    
    Ext.flatten = ExtArray.flatten;

    
    Ext.clean = ExtArray.clean;

    
    Ext.unique = ExtArray.unique;

    
    Ext.pluck = ExtArray.pluck;

    
    Ext.toArray = function() {
        return ExtArray.toArray.apply(ExtArray, arguments);
    };
}());






Ext.Function = {

    
    flexSetter: function(fn) {
        return function(a, b) {
            var k, i;

            if (a === null) {
                return this;
            }

            if (typeof a !== 'string') {
                for (k in a) {
                    if (a.hasOwnProperty(k)) {
                        fn.call(this, k, a[k]);
                    }
                }

                if (Ext.enumerables) {
                    for (i = Ext.enumerables.length; i--;) {
                        k = Ext.enumerables[i];
                        if (a.hasOwnProperty(k)) {
                            fn.call(this, k, a[k]);
                        }
                    }
                }
            } else {
                fn.call(this, a, b);
            }

            return this;
        };
    },

    
    bind: function(fn, scope, args, appendArgs) {
        if (arguments.length === 2) {
            return function() {
                return fn.apply(scope, arguments);
            };
        }

        var method = fn,
            slice = Array.prototype.slice;

        return function() {
            var callArgs = args || arguments;

            if (appendArgs === true) {
                callArgs = slice.call(arguments, 0);
                callArgs = callArgs.concat(args);
            }
            else if (typeof appendArgs == 'number') {
                callArgs = slice.call(arguments, 0); 
                Ext.Array.insert(callArgs, appendArgs, args);
            }

            return method.apply(scope || Ext.global, callArgs);
        };
    },

    
    pass: function(fn, args, scope) {
        if (!Ext.isArray(args)) {
            if (Ext.isIterable(args)) {
                args = Ext.Array.clone(args);
            } else {
                args = args !== undefined ? [args] : [];
            }
        }

        return function() {
            var fnArgs = [].concat(args);
            fnArgs.push.apply(fnArgs, arguments);
            return fn.apply(scope || this, fnArgs);
        };
    },

    
    alias: function(object, methodName) {
        return function() {
            return object[methodName].apply(object, arguments);
        };
    },

    
    clone: function(method) {
        return function() {
            return method.apply(this, arguments);
        };
    },

    
    createInterceptor: function(origFn, newFn, scope, returnValue) {
        var method = origFn;
        if (!Ext.isFunction(newFn)) {
            return origFn;
        } else {
            returnValue = Ext.isDefined(returnValue) ? returnValue : null;
            return function() {
                var me = this,
                    args = arguments;
                    
                newFn.target = me;
                newFn.method = origFn;
                return (newFn.apply(scope || me || Ext.global, args) !== false) ? origFn.apply(me || Ext.global, args) : returnValue;
            };
        }
    },

    
    createDelayed: function(fn, delay, scope, args, appendArgs) {
        if (scope || args) {
            fn = Ext.Function.bind(fn, scope, args, appendArgs);
        }

        return function() {
            var me = this,
                args = Array.prototype.slice.call(arguments);

            setTimeout(function() {
                fn.apply(me, args);
            }, delay);
        };
    },

    
    defer: function(fn, millis, scope, args, appendArgs) {
        fn = Ext.Function.bind(fn, scope, args, appendArgs);
        if (millis > 0) {
            return setTimeout(Ext.supports.TimeoutActualLateness ? function () {
                fn();
            } : fn, millis);
        }
        fn();
        return 0;
    },

    
    createSequence: function(originalFn, newFn, scope) {
        if (!newFn) {
            return originalFn;
        }
        else {
            return function() {
                var result = originalFn.apply(this, arguments);
                newFn.apply(scope || this, arguments);
                return result;
            };
        }
    },

    
    createBuffered: function(fn, buffer, scope, args) {
        var timerId;

        return function() {
            var callArgs = args || Array.prototype.slice.call(arguments, 0),
                me = scope || this;

            if (timerId) {
                clearTimeout(timerId);
            }

            timerId = setTimeout(function(){
                fn.apply(me, callArgs);
            }, buffer);
        };
    },

    
    createThrottled: function(fn, interval, scope) {
        var lastCallTime, elapsed, lastArgs, timer, execute = function() {
            fn.apply(scope || this, lastArgs);
            lastCallTime = Ext.Date.now();
        };

        return function() {
            elapsed = Ext.Date.now() - lastCallTime;
            lastArgs = arguments;

            clearTimeout(timer);
            if (!lastCallTime || (elapsed >= interval)) {
                execute();
            } else {
                timer = setTimeout(execute, interval - elapsed);
            }
        };
    },


    
    interceptBefore: function(object, methodName, fn, scope) {
        var method = object[methodName] || Ext.emptyFn;

        return (object[methodName] = function() {
            var ret = fn.apply(scope || this, arguments);
            method.apply(this, arguments);

            return ret;
        });
    },

    
    interceptAfter: function(object, methodName, fn, scope) {
        var method = object[methodName] || Ext.emptyFn;

        return (object[methodName] = function() {
            method.apply(this, arguments);
            return fn.apply(scope || this, arguments);
        });
    }
};


Ext.defer = Ext.Function.alias(Ext.Function, 'defer');


Ext.pass = Ext.Function.alias(Ext.Function, 'pass');


Ext.bind = Ext.Function.alias(Ext.Function, 'bind');







(function() {


var TemplateClass = function(){},
    ExtObject = Ext.Object = {

    
    chain: Object.create || function (object) {
        TemplateClass.prototype = object;
        var result = new TemplateClass();
        TemplateClass.prototype = null;
        return result;
    },

    
    toQueryObjects: function(name, value, recursive) {
        var self = ExtObject.toQueryObjects,
            objects = [],
            i, ln;

        if (Ext.isArray(value)) {
            for (i = 0, ln = value.length; i < ln; i++) {
                if (recursive) {
                    objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                }
                else {
                    objects.push({
                        name: name,
                        value: value[i]
                    });
                }
            }
        }
        else if (Ext.isObject(value)) {
            for (i in value) {
                if (value.hasOwnProperty(i)) {
                    if (recursive) {
                        objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                    }
                    else {
                        objects.push({
                            name: name,
                            value: value[i]
                        });
                    }
                }
            }
        }
        else {
            objects.push({
                name: name,
                value: value
            });
        }

        return objects;
    },

    
    toQueryString: function(object, recursive) {
        var paramObjects = [],
            params = [],
            i, j, ln, paramObject, value;

        for (i in object) {
            if (object.hasOwnProperty(i)) {
                paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
            }
        }

        for (j = 0, ln = paramObjects.length; j < ln; j++) {
            paramObject = paramObjects[j];
            value = paramObject.value;

            if (Ext.isEmpty(value)) {
                value = '';
            } else if (Ext.isDate(value)) {
                value = Ext.Date.toString(value);
            }

            params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
        }

        return params.join('&');
    },

    
    fromQueryString: function(queryString, recursive) {
        var parts = queryString.replace(/^\?/, '').split('&'),
            object = {},
            temp, components, name, value, i, ln,
            part, j, subLn, matchedKeys, matchedName,
            keys, key, nextKey;

        for (i = 0, ln = parts.length; i < ln; i++) {
            part = parts[i];

            if (part.length > 0) {
                components = part.split('=');
                name = decodeURIComponent(components[0]);
                value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';

                if (!recursive) {
                    if (object.hasOwnProperty(name)) {
                        if (!Ext.isArray(object[name])) {
                            object[name] = [object[name]];
                        }

                        object[name].push(value);
                    }
                    else {
                        object[name] = value;
                    }
                }
                else {
                    matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
                    matchedName = name.match(/^([^\[]+)/);


                    name = matchedName[0];
                    keys = [];

                    if (matchedKeys === null) {
                        object[name] = value;
                        continue;
                    }

                    for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
                        key = matchedKeys[j];
                        key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
                        keys.push(key);
                    }

                    keys.unshift(name);

                    temp = object;

                    for (j = 0, subLn = keys.length; j < subLn; j++) {
                        key = keys[j];

                        if (j === subLn - 1) {
                            if (Ext.isArray(temp) && key === '') {
                                temp.push(value);
                            }
                            else {
                                temp[key] = value;
                            }
                        }
                        else {
                            if (temp[key] === undefined || typeof temp[key] === 'string') {
                                nextKey = keys[j+1];

                                temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
                            }

                            temp = temp[key];
                        }
                    }
                }
            }
        }

        return object;
    },

    
    each: function(object, fn, scope) {
        for (var property in object) {
            if (object.hasOwnProperty(property)) {
                if (fn.call(scope || object, property, object[property], object) === false) {
                    return;
                }
            }
        }
    },

    
    merge: function(destination) {
        var i = 1,
            ln = arguments.length,
            mergeFn = ExtObject.merge,
            cloneFn = Ext.clone,
            object, key, value, sourceKey;

        for (; i < ln; i++) {
            object = arguments[i];

            for (key in object) {
                value = object[key];
                if (value && value.constructor === Object) {
                    sourceKey = destination[key];
                    if (sourceKey && sourceKey.constructor === Object) {
                        mergeFn(sourceKey, value);
                    }
                    else {
                        destination[key] = cloneFn(value);
                    }
                }
                else {
                    destination[key] = value;
                }
            }
        }

        return destination;
    },

    
    mergeIf: function(destination) {
        var i = 1,
            ln = arguments.length,
            cloneFn = Ext.clone,
            object, key, value;

        for (; i < ln; i++) {
            object = arguments[i];

            for (key in object) {
                if (!(key in destination)) {
                    value = object[key];

                    if (value && value.constructor === Object) {
                        destination[key] = cloneFn(value);
                    }
                    else {
                        destination[key] = value;
                    }
                }
            }
        }

        return destination;
    },

    
    getKey: function(object, value) {
        for (var property in object) {
            if (object.hasOwnProperty(property) && object[property] === value) {
                return property;
            }
        }

        return null;
    },

    
    getValues: function(object) {
        var values = [],
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                values.push(object[property]);
            }
        }

        return values;
    },

    
    getKeys: (typeof Object.keys == 'function')
        ? function(object){
            if (!object) {
                return [];
            }
            return Object.keys(object);
        }
        : function(object) {
            var keys = [],
                property;

            for (property in object) {
                if (object.hasOwnProperty(property)) {
                    keys.push(property);
                }
            }

            return keys;
        },

    
    getSize: function(object) {
        var size = 0,
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                size++;
            }
        }

        return size;
    },
    
    
    isEmpty: function(object){
        for (var key in object) {
            if (object.hasOwnProperty(key)) {
                return false;
            }
        }
        return true;    
    },
    
    
    equals: (function() {
        var check = function(o1, o2) {
            var key;
        
            for (key in o1) {
                if (o1.hasOwnProperty(key)) {
                    if (o1[key] !== o2[key]) {
                        return false;
                    }    
                }
            }    
            return true;
        };
        
        return function(object1, object2) {
            
            
            if (object1 === object2) {
                return true;
            } if (object1 && object2) {
                
                
                return check(object1, object2) && check(object2, object1);  
            } else if (!object1 && !object2) {
                return object1 === object2;
            } else {
                return false;
            }
        };
    })(),

    
    classify: function(object) {
        var prototype = object,
            objectProperties = [],
            propertyClassesMap = {},
            objectClass = function() {
                var i = 0,
                    ln = objectProperties.length,
                    property;

                for (; i < ln; i++) {
                    property = objectProperties[i];
                    this[property] = new propertyClassesMap[property]();
                }
            },
            key, value;

        for (key in object) {
            if (object.hasOwnProperty(key)) {
                value = object[key];

                if (value && value.constructor === Object) {
                    objectProperties.push(key);
                    propertyClassesMap[key] = ExtObject.classify(value);
                }
            }
        }

        objectClass.prototype = prototype;

        return objectClass;
    }
};


Ext.merge = Ext.Object.merge;


Ext.mergeIf = Ext.Object.mergeIf;


Ext.urlEncode = function() {
    var args = Ext.Array.from(arguments),
        prefix = '';

    
    if ((typeof args[1] === 'string')) {
        prefix = args[1] + '&';
        args[1] = false;
    }

    return prefix + ExtObject.toQueryString.apply(ExtObject, args);
};


Ext.urlDecode = function() {
    return ExtObject.fromQueryString.apply(ExtObject, arguments);
};

}());









Ext.Date = new function() {
  var utilDate = this,
      stripEscapeRe = /(\\.)/g,
      hourInfoRe = /([gGhHisucUOPZ]|MS)/,
      dateInfoRe = /([djzmnYycU]|MS)/,
      slashRe = /\\/gi,
      numberTokenRe = /\{(\d+)\}/g,
      MSFormatRe = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/'),
      code = [
        
        "var me = this, dt, y, m, d, h, i, s, ms, o, O, z, zz, u, v, W, year, jan4, week1monday, daysInMonth, dayMatched,",
            "def = me.defaults,",
            "from = Ext.Number.from,",
            "results = String(input).match(me.parseRegexes[{0}]);", 

        "if(results){",
            "{1}",

            "if(u != null){", 
                "v = new Date(u * 1000);", 
            "}else{",
                
                
                
                "dt = me.clearTime(new Date);",

                "y = from(y, from(def.y, dt.getFullYear()));",
                "m = from(m, from(def.m - 1, dt.getMonth()));",
                "dayMatched = d !== undefined;",
                "d = from(d, from(def.d, dt.getDate()));",
                
                
                
                
                
                
                
                
                "if (!dayMatched) {", 
                    "dt.setDate(1);",
                    "dt.setMonth(m);",
                    "dt.setFullYear(y);",
                
                    "daysInMonth = me.getDaysInMonth(dt);",
                    "if (d > daysInMonth) {",
                        "d = daysInMonth;",
                    "}",
                "}",

                "h  = from(h, from(def.h, dt.getHours()));",
                "i  = from(i, from(def.i, dt.getMinutes()));",
                "s  = from(s, from(def.s, dt.getSeconds()));",
                "ms = from(ms, from(def.ms, dt.getMilliseconds()));",

                "if(z >= 0 && y >= 0){",
                    
                    

                    
                    
                    "v = me.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);",

                    
                    "v = !strict? v : (strict === true && (z <= 364 || (me.isLeapYear(v) && z <= 365))? me.add(v, me.DAY, z) : null);",
                "}else if(strict === true && !me.isValid(y, m + 1, d, h, i, s, ms)){", 
                    "v = null;", 
                "}else{",
                    "if (W) {", 
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        "year = y || (new Date()).getFullYear(),",
                        "jan4 = new Date(year, 0, 4, 0, 0, 0),",
                        "week1monday = new Date(jan4.getTime() - ((jan4.getDay() - 1) * 86400000));",
                        "v = Ext.Date.clearTime(new Date(week1monday.getTime() + ((W - 1) * 604800000)));",
                    "} else {",
                        
                        
                        "v = me.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);",
                    "}",
                "}",
            "}",
        "}",

        "if(v){",
            
            "if(zz != null){",
                
                "v = me.add(v, me.SECOND, -v.getTimezoneOffset() * 60 - zz);",
            "}else if(o){",
                
                "v = me.add(v, me.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
            "}",
        "}",

        "return v;"
      ].join('\n');

  
  
  
  function xf(format) {
      var args = Array.prototype.slice.call(arguments, 1);
      return format.replace(numberTokenRe, function(m, i) {
          return args[i];
      });
  }

  Ext.apply(utilDate, {
    
    now: Date.now || function() {
        return +new Date();
    },

    
    toString: function(date) {
        var pad = Ext.String.leftPad;

        return date.getFullYear() + "-"
            + pad(date.getMonth() + 1, 2, '0') + "-"
            + pad(date.getDate(), 2, '0') + "T"
            + pad(date.getHours(), 2, '0') + ":"
            + pad(date.getMinutes(), 2, '0') + ":"
            + pad(date.getSeconds(), 2, '0');
    },

    
    getElapsed: function(dateA, dateB) {
        return Math.abs(dateA - (dateB || utilDate.now()));
    },

    
    useStrict: false,

    
    formatCodeToRegex: function(character, currentGroup) {
        
        var p = utilDate.parseCodes[character];

        if (p) {
          p = typeof p == 'function'? p() : p;
          utilDate.parseCodes[character] = p; 
        }

        return p ? Ext.applyIf({
          c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
        }, p) : {
            g: 0,
            c: null,
            s: Ext.String.escapeRegex(character) 
        };
    },

    
    parseFunctions: {
        "MS": function(input, strict) {
            
            
            var r = (input || '').match(MSFormatRe);
            return r ? new Date(((r[1] || '') + r[2]) * 1) : null;
        },
        "time": function(input, strict) {
            var num = parseInt(input, 10);
            if (num || num === 0) {
                return new Date(num);
            }
            return null;
        },
        "timestamp": function(input, strict) {
            var num = parseInt(input, 10);
            if (num || num === 0) {
                return new Date(num * 1000);
            }
            return null;
        }
    },
    parseRegexes: [],

    
    formatFunctions: {
        "MS": function() {
            
            return '\\/Date(' + this.getTime() + ')\\/';
        },
        "time": function(){
            return this.getTime().toString();
        },
        "timestamp": function(){
            return utilDate.format(this, 'U');
        }
    },

    y2kYear : 50,

    
    MILLI : "ms",

    
    SECOND : "s",

    
    MINUTE : "mi",

    
    HOUR : "h",

    
    DAY : "d",

    
    MONTH : "mo",

    
    YEAR : "y",

    
    defaults: {},

    
    
    dayNames : [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday"
    ],
    

    
    
    monthNames : [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    ],
    

    
    
    monthNumbers : {
        January: 0,
        Jan: 0,
        February: 1,
        Feb: 1,
        March: 2,
        Mar: 2,
        April: 3,
        Apr: 3,
        May: 4,
        June: 5,
        Jun: 5,
        July: 6,
        Jul: 6,
        August: 7,
        Aug: 7,
        September: 8,
        Sep: 8,
        October: 9,
        Oct: 9,
        November: 10,
        Nov: 10,
        December: 11,
        Dec: 11
    },
    
    
    
    
    defaultFormat : "m/d/Y",
    
    
    
    getShortMonthName : function(month) {
        return Ext.Date.monthNames[month].substring(0, 3);
    },
    

    
    
    getShortDayName : function(day) {
        return Ext.Date.dayNames[day].substring(0, 3);
    },
    

    
    
    getMonthNumber : function(name) {
        
        return Ext.Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
    },
    

    
    formatContainsHourInfo : function(format){
        return hourInfoRe.test(format.replace(stripEscapeRe, ''));
    },

    
    formatContainsDateInfo : function(format){
        return dateInfoRe.test(format.replace(stripEscapeRe, ''));
    },
    
    
    unescapeFormat: function(format) {
        
        
        
        return format.replace(slashRe, '');
    },

    
    formatCodes : {
        d: "Ext.String.leftPad(this.getDate(), 2, '0')",
        D: "Ext.Date.getShortDayName(this.getDay())", 
        j: "this.getDate()",
        l: "Ext.Date.dayNames[this.getDay()]",
        N: "(this.getDay() ? this.getDay() : 7)",
        S: "Ext.Date.getSuffix(this)",
        w: "this.getDay()",
        z: "Ext.Date.getDayOfYear(this)",
        W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
        F: "Ext.Date.monthNames[this.getMonth()]",
        m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
        M: "Ext.Date.getShortMonthName(this.getMonth())", 
        n: "(this.getMonth() + 1)",
        t: "Ext.Date.getDaysInMonth(this)",
        L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
        o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
        Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
        y: "('' + this.getFullYear()).substring(2, 4)",
        a: "(this.getHours() < 12 ? 'am' : 'pm')",
        A: "(this.getHours() < 12 ? 'AM' : 'PM')",
        g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
        G: "this.getHours()",
        h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
        H: "Ext.String.leftPad(this.getHours(), 2, '0')",
        i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
        s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
        u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
        O: "Ext.Date.getGMTOffset(this)",
        P: "Ext.Date.getGMTOffset(this, true)",
        T: "Ext.Date.getTimezone(this)",
        Z: "(this.getTimezoneOffset() * -60)",

        c: function() { 
            var c, code, i, l, e;
            for (c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
                e = c.charAt(i);
                code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); 
            }
            return code.join(" + ");
        },
        

        U: "Math.round(this.getTime() / 1000)"
    },

    
    isValid : function(y, m, d, h, i, s, ms) {
        
        h = h || 0;
        i = i || 0;
        s = s || 0;
        ms = ms || 0;

        
        var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);

        return y == dt.getFullYear() &&
            m == dt.getMonth() + 1 &&
            d == dt.getDate() &&
            h == dt.getHours() &&
            i == dt.getMinutes() &&
            s == dt.getSeconds() &&
            ms == dt.getMilliseconds();
    },

    
    parse : function(input, format, strict) {
        var p = utilDate.parseFunctions;
        if (p[format] == null) {
            utilDate.createParser(format);
        }
        return p[format].call(utilDate, input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
    },

    
    parseDate: function(input, format, strict){
        return utilDate.parse(input, format, strict);
    },


    
    getFormatCode : function(character) {
        var f = utilDate.formatCodes[character];

        if (f) {
          f = typeof f == 'function'? f() : f;
          utilDate.formatCodes[character] = f; 
        }

        
        return f || ("'" + Ext.String.escape(character) + "'");
    },

    
    createFormat : function(format) {
        var code = [],
            special = false,
            ch = '',
            i;

        for (i = 0; i < format.length; ++i) {
            ch = format.charAt(i);
            if (!special && ch == "\\") {
                special = true;
            } else if (special) {
                special = false;
                code.push("'" + Ext.String.escape(ch) + "'");
            } else {
                code.push(utilDate.getFormatCode(ch));
            }
        }
        utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
    },

    
    createParser : function(format) {
        var regexNum = utilDate.parseRegexes.length,
            currentGroup = 1,
            calc = [],
            regex = [],
            special = false,
            ch = "",
            i = 0,
            len = format.length,
            atEnd = [],
            obj;

        for (; i < len; ++i) {
            ch = format.charAt(i);
            if (!special && ch == "\\") {
                special = true;
            } else if (special) {
                special = false;
                regex.push(Ext.String.escape(ch));
            } else {
                obj = utilDate.formatCodeToRegex(ch, currentGroup);
                currentGroup += obj.g;
                regex.push(obj.s);
                if (obj.g && obj.c) {
                    if (obj.calcAtEnd) {
                        atEnd.push(obj.c);
                    } else {
                        calc.push(obj.c);
                    }
                }
            }
        }

        calc = calc.concat(atEnd);

        utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
        utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
    },

    
    parseCodes : {
        
        d: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(3[0-1]|[1-2][0-9]|0[1-9])" 
        },
        j: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(3[0-1]|[1-2][0-9]|[1-9])" 
        },
        D: function() {
            for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); 
            return {
                g:0,
                c:null,
                s:"(?:" + a.join("|") +")"
            };
        },
        l: function() {
            return {
                g:0,
                c:null,
                s:"(?:" + utilDate.dayNames.join("|") + ")"
            };
        },
        N: {
            g:0,
            c:null,
            s:"[1-7]" 
        },
        
        S: {
            g:0,
            c:null,
            s:"(?:st|nd|rd|th)"
        },
        
        w: {
            g:0,
            c:null,
            s:"[0-6]" 
        },
        z: {
            g:1,
            c:"z = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,3})" 
        },
        W: {
            g:1,
            c:"W = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        F: function() {
            return {
                g:1,
                c:"m = parseInt(me.getMonthNumber(results[{0}]), 10);\n", 
                s:"(" + utilDate.monthNames.join("|") + ")"
            };
        },
        M: function() {
            for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); 
            return Ext.applyIf({
                s:"(" + a.join("|") + ")"
            }, utilDate.formatCodeToRegex("F"));
        },
        m: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(1[0-2]|0[1-9])" 
        },
        n: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(1[0-2]|[1-9])" 
        },
        t: {
            g:0,
            c:null,
            s:"(?:\\d{2})" 
        },
        L: {
            g:0,
            c:null,
            s:"(?:1|0)"
        },
        o: { 
            g: 1,
            c: "y = parseInt(results[{0}], 10);\n",
            s: "(\\d{4})" 

        },
        Y: {
            g:1,
            c:"y = parseInt(results[{0}], 10);\n",
            s:"(\\d{4})" 
        },
        y: {
            g:1,
            c:"var ty = parseInt(results[{0}], 10);\n"
                + "y = ty > me.y2kYear ? 1900 + ty : 2000 + ty;\n", 
            s:"(\\d{1,2})"
        },
        
        
        a: {
            g:1,
            c:"if (/(am)/i.test(results[{0}])) {\n"
                + "if (!h || h == 12) { h = 0; }\n"
                + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
            s:"(am|pm|AM|PM)",
            calcAtEnd: true
        },
        
        
        A: {
            g:1,
            c:"if (/(am)/i.test(results[{0}])) {\n"
                + "if (!h || h == 12) { h = 0; }\n"
                + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
            s:"(AM|PM|am|pm)",
            calcAtEnd: true
        },
        
        g: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(1[0-2]|[0-9])" 
        },
        G: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(2[0-3]|1[0-9]|[0-9])" 
        },
        h: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(1[0-2]|0[1-9])" 
        },
        H: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(2[0-3]|[0-1][0-9])" 
        },
        i: {
            g:1,
            c:"i = parseInt(results[{0}], 10);\n",
            s:"([0-5][0-9])" 
        },
        s: {
            g:1,
            c:"s = parseInt(results[{0}], 10);\n",
            s:"([0-5][0-9])" 
        },
        u: {
            g:1,
            c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
            s:"(\\d+)" 
        },
        O: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", 
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", 
                    "mn = o.substring(3,5) % 60;", 
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" 
            ].join("\n"),
            s: "([+-]\\d{4})" 
        },
        P: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", 
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", 
                    "mn = o.substring(4,6) % 60;", 
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" 
            ].join("\n"),
            s: "([+-]\\d{2}:\\d{2})" 
        },
        T: {
            g:0,
            c:null,
            s:"[A-Z]{1,5}" 
        },
        Z: {
            g:1,
            c:"zz = results[{0}] * 1;\n" 
                  + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
            s:"([+-]?\\d{1,5})" 
        },
        c: function() {
            var calc = [],
                arr = [
                    utilDate.formatCodeToRegex("Y", 1), 
                    utilDate.formatCodeToRegex("m", 2), 
                    utilDate.formatCodeToRegex("d", 3), 
                    utilDate.formatCodeToRegex("H", 4), 
                    utilDate.formatCodeToRegex("i", 5), 
                    utilDate.formatCodeToRegex("s", 6), 
                    {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, 
                    {c:[ 
                        "if(results[8]) {", 
                            "if(results[8] == 'Z'){",
                                "zz = 0;", 
                            "}else if (results[8].indexOf(':') > -1){",
                                utilDate.formatCodeToRegex("P", 8).c, 
                            "}else{",
                                utilDate.formatCodeToRegex("O", 8).c, 
                            "}",
                        "}"
                    ].join('\n')}
                ],
                i,
                l;

            for (i = 0, l = arr.length; i < l; ++i) {
                calc.push(arr[i].c);
            }

            return {
                g:1,
                c:calc.join(""),
                s:[
                    arr[0].s, 
                    "(?:", "-", arr[1].s, 
                        "(?:", "-", arr[2].s, 
                            "(?:",
                                "(?:T| )?", 
                                arr[3].s, ":", arr[4].s,  
                                "(?::", arr[5].s, ")?", 
                                "(?:(?:\\.|,)(\\d+))?", 
                                "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", 
                            ")?",
                        ")?",
                    ")?"
                ].join("")
            };
        },
        U: {
            g:1,
            c:"u = parseInt(results[{0}], 10);\n",
            s:"(-?\\d+)" 
        }
    },

    
    
    dateFormat: function(date, format) {
        return utilDate.format(date, format);
    },

    
    isEqual: function(date1, date2) {
        
        if (date1 && date2) {
            return (date1.getTime() === date2.getTime());
        }
        
        return !(date1 || date2);
    },

    
    format: function(date, format) {
        var formatFunctions = utilDate.formatFunctions;

        if (!Ext.isDate(date)) {
            return '';
        }

        if (formatFunctions[format] == null) {
            utilDate.createFormat(format);
        }

        return formatFunctions[format].call(date) + '';
    },

    
    getTimezone : function(date) {
        
        
        
        
        
        
        
        
        
        
        
        
        return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,5})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
    },

    
    getGMTOffset : function(date, colon) {
        var offset = date.getTimezoneOffset();
        return (offset > 0 ? "-" : "+")
            + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
            + (colon ? ":" : "")
            + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
    },

    
    getDayOfYear: function(date) {
        var num = 0,
            d = Ext.Date.clone(date),
            m = date.getMonth(),
            i;

        for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
            num += utilDate.getDaysInMonth(d);
        }
        return num + date.getDate() - 1;
    },

    
    getWeekOfYear : (function() {
        
        var ms1d = 864e5, 
            ms7d = 7 * ms1d; 

        return function(date) { 
            var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, 
                AWN = Math.floor(DC3 / 7), 
                Wyr = new Date(AWN * ms7d).getUTCFullYear();

            return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
        };
    }()),

    
    isLeapYear : function(date) {
        var year = date.getFullYear();
        return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
    },

    
    getFirstDayOfMonth : function(date) {
        var day = (date.getDay() - (date.getDate() - 1)) % 7;
        return (day < 0) ? (day + 7) : day;
    },

    
    getLastDayOfMonth : function(date) {
        return utilDate.getLastDateOfMonth(date).getDay();
    },


    
    getFirstDateOfMonth : function(date) {
        return new Date(date.getFullYear(), date.getMonth(), 1);
    },

    
    getLastDateOfMonth : function(date) {
        return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
    },

    
    getDaysInMonth: (function() {
        var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

        return function(date) { 
            var m = date.getMonth();

            return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
        };
    }()),

    
    
    getSuffix : function(date) {
        switch (date.getDate()) {
            case 1:
            case 21:
            case 31:
                return "st";
            case 2:
            case 22:
                return "nd";
            case 3:
            case 23:
                return "rd";
            default:
                return "th";
        }
    },
    

    
    clone : function(date) {
        return new Date(date.getTime());
    },

    
    isDST : function(date) {
        
        
        return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
    },

    
    clearTime : function(date, clone) {
        if (clone) {
            return Ext.Date.clearTime(Ext.Date.clone(date));
        }

        
        var d = date.getDate(),
            hr,
            c;

        
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);

        if (date.getDate() != d) { 
            
            

            
            for (hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));

            date.setDate(d);
            date.setHours(c.getHours());
        }

        return date;
    },

    
    add : function(date, interval, value) {
        var d = Ext.Date.clone(date),
            Date = Ext.Date,
            day, decimalValue, base = 0;
        if (!interval || value === 0) {
            return d;
        }

        decimalValue = value - parseInt(value, 10);
        value = parseInt(value, 10);

        if (value) {
            switch(interval.toLowerCase()) {
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                case Ext.Date.MILLI:
                    d.setTime(d.getTime() + value);
                    break;
                case Ext.Date.SECOND:
                    d.setTime(d.getTime() + value * 1000);
                    break;
                case Ext.Date.MINUTE:
                    d.setTime(d.getTime() + value * 60 * 1000);
                    break;
                case Ext.Date.HOUR:
                    d.setTime(d.getTime() + value * 60 * 60 * 1000);
                    break;
                case Ext.Date.DAY:
                    d.setDate(d.getDate() + value);
                    break;
                case Ext.Date.MONTH:
                    day = date.getDate();
                    if (day > 28) {
                        day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), Ext.Date.MONTH, value)).getDate());
                    }
                    d.setDate(day);
                    d.setMonth(date.getMonth() + value);
                    break;
                case Ext.Date.YEAR:
                    day = date.getDate();
                    if (day > 28) {
                        day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), Ext.Date.YEAR, value)).getDate());
                    }
                    d.setDate(day);
                    d.setFullYear(date.getFullYear() + value);
                    break;
            }
        }

        if (decimalValue) {
            switch (interval.toLowerCase()) {
                case Ext.Date.MILLI:    base = 1;               break;
                case Ext.Date.SECOND:   base = 1000;            break;
                case Ext.Date.MINUTE:   base = 1000*60;         break;
                case Ext.Date.HOUR:     base = 1000*60*60;      break;
                case Ext.Date.DAY:      base = 1000*60*60*24;   break;

                case Ext.Date.MONTH:
                    day = utilDate.getDaysInMonth(d);
                    base = 1000*60*60*24*day;
                    break;

                case Ext.Date.YEAR:
                    day = (utilDate.isLeapYear(d) ? 366 : 365);
                    base = 1000*60*60*24*day;
                    break;
            }
            if (base) {
                d.setTime(d.getTime() + base * decimalValue); 
            }
        }

        return d;
    },
    
    
    subtract: function(date, interval, value){
        return utilDate.add(date, interval, -value);
    },

    
    between : function(date, start, end) {
        var t = date.getTime();
        return start.getTime() <= t && t <= end.getTime();
    },

    
    compat: function() {
        var nativeDate = window.Date,
            p,
            statics = ['useStrict', 'formatCodeToRegex', 'parseFunctions', 'parseRegexes', 'formatFunctions', 'y2kYear', 'MILLI', 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'MONTH', 'YEAR', 'defaults', 'dayNames', 'monthNames', 'monthNumbers', 'getShortMonthName', 'getShortDayName', 'getMonthNumber', 'formatCodes', 'isValid', 'parseDate', 'getFormatCode', 'createFormat', 'createParser', 'parseCodes'],
            proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'],
            sLen    = statics.length,
            pLen    = proto.length,
            stat, prot, s;

        
        for (s = 0; s < sLen; s++) {
            stat = statics[s];
            nativeDate[stat] = utilDate[stat];
        }

        
        for (p = 0; p < pLen; p++) {
            prot = proto[p];
            nativeDate.prototype[prot] = function() {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(this);
                return utilDate[prot].apply(utilDate, args);
            };
        }
    }
  });
};






(function(flexSetter) {

var noArgs = [],
    Base = function(){},
    hookFunctionFactory = function(hookFunction, underriddenFunction, methodName, owningClass) {
        var result = function() {
            var result = this.callParent(arguments);
            hookFunction.apply(this, arguments);
            return result;
        };
        result.$name = methodName;
        result.$owner = owningClass;
        if (underriddenFunction) {
            result.$previous = underriddenFunction.$previous;
            underriddenFunction.$previous = result;
        }
        return result;
    };

    
    Ext.apply(Base, {
        $className: 'Ext.Base',

        $isClass: true,

        
        create: function() {
            return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
        },

        
        extend: function(parent) {
            var parentPrototype = parent.prototype,
                basePrototype, prototype, i, ln, name, statics;

            prototype = this.prototype = Ext.Object.chain(parentPrototype);
            prototype.self = this;

            this.superclass = prototype.superclass = parentPrototype;

            if (!parent.$isClass) {
                basePrototype = Ext.Base.prototype;

                for (i in basePrototype) {
                    if (i in prototype) {
                        prototype[i] = basePrototype[i];
                    }
                }
            }

            
            statics = parentPrototype.$inheritableStatics;

            if (statics) {
                for (i = 0,ln = statics.length; i < ln; i++) {
                    name = statics[i];

                    if (!this.hasOwnProperty(name)) {
                        this[name] = parent[name];
                    }
                }
            }

            if (parent.$onExtended) {
                this.$onExtended = parent.$onExtended.slice();
            }

            prototype.config = new prototype.configClass();
            prototype.initConfigList = prototype.initConfigList.slice();
            prototype.initConfigMap = Ext.clone(prototype.initConfigMap);
            prototype.configMap = Ext.Object.chain(prototype.configMap);
        },

        
        $onExtended: [],

        
        triggerExtended: function() {
        
            var callbacks = this.$onExtended,
                ln = callbacks.length,
                i, callback;

            if (ln > 0) {
                for (i = 0; i < ln; i++) {
                    callback = callbacks[i];
                    callback.fn.apply(callback.scope || this, arguments);
                }
            }
        },

        
        onExtended: function(fn, scope) {
            this.$onExtended.push({
                fn: fn,
                scope: scope
            });

            return this;
        },

        
        addConfig: function(config, fullMerge) {
            var prototype = this.prototype,
                configNameCache = Ext.Class.configNameCache,
                hasConfig = prototype.configMap,
                initConfigList = prototype.initConfigList,
                initConfigMap = prototype.initConfigMap,
                defaultConfig = prototype.config,
                initializedName, name, value;

            for (name in config) {
                if (config.hasOwnProperty(name)) {
                    if (!hasConfig[name]) {
                        hasConfig[name] = true;
                    }

                    value = config[name];

                    initializedName = configNameCache[name].initialized;

                    if (!initConfigMap[name] && value !== null && !prototype[initializedName]) {
                        initConfigMap[name] = true;
                        initConfigList.push(name);
                    }
                }
            }

            if (fullMerge) {
                Ext.merge(defaultConfig, config);
            }
            else {
                Ext.mergeIf(defaultConfig, config);
            }

            prototype.configClass = Ext.Object.classify(defaultConfig);
        },

        
        addStatics: function(members) {
            var member, name;

            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn && member !== Ext.identityFn) {
                        member.$owner = this;
                        member.$name = name;
                    }
                    this[name] = member;
                }
            }

            return this;
        },

        
        addInheritableStatics: function(members) {
            var inheritableStatics,
                hasInheritableStatics,
                prototype = this.prototype,
                name, member;

            inheritableStatics = prototype.$inheritableStatics;
            hasInheritableStatics = prototype.$hasInheritableStatics;

            if (!inheritableStatics) {
                inheritableStatics = prototype.$inheritableStatics = [];
                hasInheritableStatics = prototype.$hasInheritableStatics = {};
            }

            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    this[name] = member;

                    if (!hasInheritableStatics[name]) {
                        hasInheritableStatics[name] = true;
                        inheritableStatics.push(name);
                    }
                }
            }

            return this;
        },

        
        addMembers: function(members) {
            var prototype = this.prototype,
                enumerables = Ext.enumerables,
                names = [],
                i, ln, name, member;

            for (name in members) {
                names.push(name);
            }

            if (enumerables) {
                names.push.apply(names, enumerables);
            }

            for (i = 0,ln = names.length; i < ln; i++) {
                name = names[i];

                if (members.hasOwnProperty(name)) {
                    member = members[name];

                    if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn && member !== Ext.identityFn) {
                        member.$owner = this;
                        member.$name = name;
                    }

                    prototype[name] = member;
                }
            }

            return this;
        },

        
        addMember: function(name, member) {            
            if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn && member !== Ext.identityFn) {
                member.$owner = this;
                member.$name = name;
            }

            this.prototype[name] = member;
            return this;
        },

        
        implement: function() {
            this.addMembers.apply(this, arguments);
        },

        
        borrow: function(fromClass, members) {
            
            var prototype = this.prototype,
                fromPrototype = fromClass.prototype,
                i, ln, name, fn, toBorrow;

            members = Ext.Array.from(members);

            for (i = 0,ln = members.length; i < ln; i++) {
                name = members[i];

                toBorrow = fromPrototype[name];

                if (typeof toBorrow == 'function') {
                    fn = Ext.Function.clone(toBorrow);


                    fn.$owner = this;
                    fn.$name = name;

                    prototype[name] = fn;
                }
                else {
                    prototype[name] = toBorrow;
                }
            }

            return this;
        },

        
        override: function(members) {
            var me = this,
                enumerables = Ext.enumerables,
                target = me.prototype,
                cloneFunction = Ext.Function.clone,
                name, index, member, statics, names, previous;

            if (arguments.length === 2) {
                name = members;
                members = {};
                members[name] = arguments[1];
                enumerables = null;
            }

            do {
                names = []; 
                statics = null; 

                for (name in members) { 
                    if (name == 'statics') {
                        statics = members[name];
                    } else if (name == 'inheritableStatics'){
                        me.addInheritableStatics(members[name]);
                    } else if (name == 'config') {
                        me.addConfig(members[name], true);
                    } else {
                        names.push(name);
                    }
                }

                if (enumerables) {
                    names.push.apply(names, enumerables);
                }

                for (index = names.length; index--; ) {
                    name = names[index];

                    if (members.hasOwnProperty(name)) {
                        member = members[name];

                        if (typeof member == 'function' && !member.$className && member !== Ext.emptyFn && member !== Ext.identityFn) {
                            if (typeof member.$owner != 'undefined') {
                                member = cloneFunction(member);
                            }


                            member.$owner = me;
                            member.$name = name;

                            previous = target[name];
                            if (previous) {
                                member.$previous = previous;
                            }
                        }

                        target[name] = member;
                    }
                }

                target = me; 
                members = statics; 
            } while (members);

            return this;
        },

        
        callParent: function(args) {
            var method;

            
            return (method = this.callParent.caller) && (method.$previous ||
                  ((method = method.$owner ? method : method.caller) &&
                        method.$owner.superclass.self[method.$name])).apply(this, args || noArgs);
        },

        
        callSuper: function(args) {
            var method;

            
            return (method = this.callSuper.caller) &&
                    ((method = method.$owner ? method : method.caller) &&
                      method.$owner.superclass.self[method.$name]).apply(this, args || noArgs);
        },

        
        mixin: function(name, mixinClass) {
            var me = this,
                mixin = mixinClass.prototype,
                prototype = me.prototype,
                key, statics, i, ln, staticName,
                mixinValue, hookKey, hookFunction;

            if (typeof mixin.onClassMixedIn != 'undefined') {
                mixin.onClassMixedIn.call(mixinClass, me);
            }

            if (!prototype.hasOwnProperty('mixins')) {
                if ('mixins' in prototype) {
                    prototype.mixins = Ext.Object.chain(prototype.mixins);
                }
                else {
                    prototype.mixins = {};
                }
            }

            for (key in mixin) {
                mixinValue = mixin[key];
                if (key === 'mixins') {
                    Ext.merge(prototype.mixins, mixinValue);
                }
                else if (key === 'xhooks') {
                    for (hookKey in mixinValue) {
                        hookFunction = mixinValue[hookKey];

                        
                        hookFunction.$previous = Ext.emptyFn;

                        if (prototype.hasOwnProperty(hookKey)) {

                            
                            
                            
                            hookFunctionFactory(hookFunction, prototype[hookKey], hookKey, me);
                        } else {
                            
                            
                            prototype[hookKey] = hookFunctionFactory(hookFunction, null, hookKey, me);
                        }
                    }
                }
                else if (!(key === 'mixinId' || key === 'config') && (prototype[key] === undefined)) {
                    prototype[key] = mixinValue;
                }
            }

            
            statics = mixin.$inheritableStatics;

            if (statics) {
                for (i = 0, ln = statics.length; i < ln; i++) {
                    staticName = statics[i];

                    if (!me.hasOwnProperty(staticName)) {
                        me[staticName] = mixinClass[staticName];
                    }
                }
            }

            if ('config' in mixin) {
                me.addConfig(mixin.config, false);
            }

            prototype.mixins[name] = mixin;
            return me;
        },

        
        getName: function() {
            return Ext.getClassName(this);
        },

        
        createAlias: flexSetter(function(alias, origin) {
            this.override(alias, function() {
                return this[origin].apply(this, arguments);
            });
        }),

        
        addXtype: function(xtype) {
            var prototype = this.prototype,
                xtypesMap = prototype.xtypesMap,
                xtypes = prototype.xtypes,
                xtypesChain = prototype.xtypesChain;

            if (!prototype.hasOwnProperty('xtypesMap')) {
                xtypesMap = prototype.xtypesMap = Ext.merge({}, prototype.xtypesMap || {});
                xtypes = prototype.xtypes = prototype.xtypes ? [].concat(prototype.xtypes) : [];
                xtypesChain = prototype.xtypesChain = prototype.xtypesChain ? [].concat(prototype.xtypesChain) : [];
                prototype.xtype = xtype;
            }

            if (!xtypesMap[xtype]) {
                xtypesMap[xtype] = true;
                xtypes.push(xtype);
                xtypesChain.push(xtype);
                Ext.ClassManager.setAlias(this, 'widget.' + xtype);
            }

            return this;
        }
    });

    Base.implement({
        
        isInstance: true,

        
        $className: 'Ext.Base',

        
        configClass: Ext.emptyFn,

        
        initConfigList: [],

        
        configMap: {},

        
        initConfigMap: {},

        
        statics: function() {
            var method = this.statics.caller,
                self = this.self;

            if (!method) {
                return self;
            }

            return method.$owner;
        },

        
        callParent: function(args) {
            
            
            
            
            var method,
                superMethod = (method = this.callParent.caller) && (method.$previous ||
                        ((method = method.$owner ? method : method.caller) &&
                                method.$owner.superclass[method.$name]));


            return superMethod.apply(this, args || noArgs);
        },

        
        callSuper: function(args) {
            
            
            
            
            var method,
                superMethod = (method = this.callSuper.caller) &&
                        ((method = method.$owner ? method : method.caller) &&
                          method.$owner.superclass[method.$name]);


            return superMethod.apply(this, args || noArgs);
        },

        
        self: Base,

        
        constructor: function() {
            return this;
        },

        
        initConfig: function(config) {
            var instanceConfig = config,
                configNameCache = Ext.Class.configNameCache,
                defaultConfig = new this.configClass(),
                defaultConfigList = this.initConfigList,
                hasConfig = this.configMap,
                nameMap, i, ln, name, initializedName;

            this.initConfig = Ext.emptyFn;

            this.initialConfig = instanceConfig || {};

            this.config = config = (instanceConfig) ? Ext.merge(defaultConfig, config) : defaultConfig;

            if (instanceConfig) {
                defaultConfigList = defaultConfigList.slice();

                for (name in instanceConfig) {
                    if (hasConfig[name]) {
                        if (instanceConfig[name] !== null) {
                            defaultConfigList.push(name);
                            this[configNameCache[name].initialized] = false;
                        }
                    }
                }
            }

            for (i = 0,ln = defaultConfigList.length; i < ln; i++) {
                name = defaultConfigList[i];
                nameMap = configNameCache[name];
                initializedName = nameMap.initialized;

                if (!this[initializedName]) {
                    this[initializedName] = true;
                    this[nameMap.set].call(this, config[name]);
                }
            }

            return this;
        },

        
        hasConfig: function(name) {
            return Boolean(this.configMap[name]);
        },

        
        setConfig: function(config, applyIfNotSet) {
            if (!config) {
                return this;
            }

            var configNameCache = Ext.Class.configNameCache,
                currentConfig = this.config,
                hasConfig = this.configMap,
                initialConfig = this.initialConfig,
                name, value;

            applyIfNotSet = Boolean(applyIfNotSet);

            for (name in config) {
                if (applyIfNotSet && initialConfig.hasOwnProperty(name)) {
                    continue;
                }

                value = config[name];
                currentConfig[name] = value;

                if (hasConfig[name]) {
                    this[configNameCache[name].set](value);
                }
            }

            return this;
        },

        
        getConfig: function(name) {
            var configNameCache = Ext.Class.configNameCache;

            return this[configNameCache[name].get]();
        },

        
        getInitialConfig: function(name) {
            var config = this.config;

            if (!name) {
                return config;
            }
            else {
                return config[name];
            }
        },

        
        onConfigUpdate: function(names, callback, scope) {
            var self = this.self,
                i, ln, name,
                updaterName, updater, newUpdater;

            names = Ext.Array.from(names);

            scope = scope || this;

            for (i = 0,ln = names.length; i < ln; i++) {
                name = names[i];
                updaterName = 'update' + Ext.String.capitalize(name);
                updater = this[updaterName] || Ext.emptyFn;
                newUpdater = function() {
                    updater.apply(this, arguments);
                    scope[callback].apply(scope, arguments);
                };
                newUpdater.$name = updaterName;
                newUpdater.$owner = self;

                this[updaterName] = newUpdater;
            }
        },

        
        destroy: function() {
            this.destroy = Ext.emptyFn;
        }
    });

    
    Base.prototype.callOverridden = Base.prototype.callParent;

    Ext.Base = Base;

}(Ext.Function.flexSetter));






(function() {
    var ExtClass,
        Base = Ext.Base,
        baseStaticMembers = [],
        baseStaticMember, baseStaticMemberLength;

    for (baseStaticMember in Base) {
        if (Base.hasOwnProperty(baseStaticMember)) {
            baseStaticMembers.push(baseStaticMember);
        }
    }

    baseStaticMemberLength = baseStaticMembers.length;

    
    function makeCtor (className) {
        function constructor () {
            
            
            return this.constructor.apply(this, arguments) || null;
        }
        return constructor;
    }

    
    Ext.Class = ExtClass = function(Class, data, onCreated) {
        if (typeof Class != 'function') {
            onCreated = data;
            data = Class;
            Class = null;
        }

        if (!data) {
            data = {};
        }

        Class = ExtClass.create(Class, data);

        ExtClass.process(Class, data, onCreated);

        return Class;
    };

    Ext.apply(ExtClass, {
        
        onBeforeCreated: function(Class, data, hooks) {
        
            Class.addMembers(data);

            hooks.onCreated.call(Class, Class);
            
        },

        
        create: function(Class, data) {
            var name, i;

            if (!Class) {
                Class = makeCtor(
                );
            }

            for (i = 0; i < baseStaticMemberLength; i++) {
                name = baseStaticMembers[i];
                Class[name] = Base[name];
            }

            return Class;
        },

        
        process: function(Class, data, onCreated) {
            var preprocessorStack = data.preprocessors || ExtClass.defaultPreprocessors,
                registeredPreprocessors = this.preprocessors,
                hooks = {
                    onBeforeCreated: this.onBeforeCreated
                },
                preprocessors = [],
                preprocessor, preprocessorsProperties,
                i, ln, j, subLn, preprocessorProperty;

            delete data.preprocessors;

            for (i = 0,ln = preprocessorStack.length; i < ln; i++) {
                preprocessor = preprocessorStack[i];

                if (typeof preprocessor == 'string') {
                    preprocessor = registeredPreprocessors[preprocessor];
                    preprocessorsProperties = preprocessor.properties;

                    if (preprocessorsProperties === true) {
                        preprocessors.push(preprocessor.fn);
                    }
                    else if (preprocessorsProperties) {
                        for (j = 0,subLn = preprocessorsProperties.length; j < subLn; j++) {
                            preprocessorProperty = preprocessorsProperties[j];

                            if (data.hasOwnProperty(preprocessorProperty)) {
                                preprocessors.push(preprocessor.fn);
                                break;
                            }
                        }
                    }
                }
                else {
                    preprocessors.push(preprocessor);
                }
            }

            hooks.onCreated = onCreated ? onCreated : Ext.emptyFn;
            hooks.preprocessors = preprocessors;

            this.doProcess(Class, data, hooks);
        },
        
        doProcess: function(Class, data, hooks) {
            var me = this,
                preprocessors = hooks.preprocessors,
                preprocessor = preprocessors.shift(),
                doProcess = me.doProcess;

            for ( ; preprocessor ; preprocessor = preprocessors.shift()) {
                
                if (preprocessor.call(me, Class, data, hooks, doProcess) === false) {
                    return;
                }
            }
            hooks.onBeforeCreated.apply(me, arguments);
        },

        
        preprocessors: {},

        
        registerPreprocessor: function(name, fn, properties, position, relativeTo) {
            if (!position) {
                position = 'last';
            }

            if (!properties) {
                properties = [name];
            }

            this.preprocessors[name] = {
                name: name,
                properties: properties || false,
                fn: fn
            };

            this.setDefaultPreprocessorPosition(name, position, relativeTo);

            return this;
        },

        
        getPreprocessor: function(name) {
            return this.preprocessors[name];
        },

        
        getPreprocessors: function() {
            return this.preprocessors;
        },

        
        defaultPreprocessors: [],

        
        getDefaultPreprocessors: function() {
            return this.defaultPreprocessors;
        },

        
        setDefaultPreprocessors: function(preprocessors) {
            this.defaultPreprocessors = Ext.Array.from(preprocessors);

            return this;
        },

        
        setDefaultPreprocessorPosition: function(name, offset, relativeName) {
            var defaultPreprocessors = this.defaultPreprocessors,
                index;

            if (typeof offset == 'string') {
                if (offset === 'first') {
                    defaultPreprocessors.unshift(name);

                    return this;
                }
                else if (offset === 'last') {
                    defaultPreprocessors.push(name);

                    return this;
                }

                offset = (offset === 'after') ? 1 : -1;
            }

            index = Ext.Array.indexOf(defaultPreprocessors, relativeName);

            if (index !== -1) {
                Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
            }

            return this;
        },

        configNameCache: {},

        getConfigNameMap: function(name) {
            var cache = this.configNameCache,
                map = cache[name],
                capitalizedName;

            if (!map) {
                capitalizedName = name.charAt(0).toUpperCase() + name.substr(1);

                map = cache[name] = {
                    internal: name,
                    initialized: '_is' + capitalizedName + 'Initialized',
                    apply: 'apply' + capitalizedName,
                    update: 'update' + capitalizedName,
                    'set': 'set' + capitalizedName,
                    'get': 'get' + capitalizedName,
                    doSet : 'doSet' + capitalizedName,
                    changeEvent: name.toLowerCase() + 'change'
                };
            }

            return map;
        }
    });

    
    ExtClass.registerPreprocessor('extend', function(Class, data, hooks) {
        
        var Base = Ext.Base,
            basePrototype = Base.prototype,
            extend = data.extend,
            Parent, parentPrototype, i;

        delete data.extend;

        if (extend && extend !== Object) {
            Parent = extend;
        }
        else {
            Parent = Base;
        }

        parentPrototype = Parent.prototype;

        if (!Parent.$isClass) {
            for (i in basePrototype) {
                if (!parentPrototype[i]) {
                    parentPrototype[i] = basePrototype[i];
                }
            }
        }

        Class.extend(Parent);

        Class.triggerExtended.apply(Class, arguments);

        if (data.onClassExtended) {
            Class.onExtended(data.onClassExtended, Class);
            delete data.onClassExtended;
        }

    }, true);

    
    ExtClass.registerPreprocessor('statics', function(Class, data) {
        
        Class.addStatics(data.statics);

        delete data.statics;
    });

    
    ExtClass.registerPreprocessor('inheritableStatics', function(Class, data) {
        
        Class.addInheritableStatics(data.inheritableStatics);

        delete data.inheritableStatics;
    });

    
    ExtClass.registerPreprocessor('config', function(Class, data) {
        
        var config = data.config,
            prototype = Class.prototype;

        delete data.config;

        Ext.Object.each(config, function(name, value) {
            var nameMap = ExtClass.getConfigNameMap(name),
                internalName = nameMap.internal,
                initializedName = nameMap.initialized,
                applyName = nameMap.apply,
                updateName = nameMap.update,
                setName = nameMap.set,
                getName = nameMap.get,
                hasOwnSetter = (setName in prototype) || data.hasOwnProperty(setName),
                hasOwnApplier = (applyName in prototype) || data.hasOwnProperty(applyName),
                hasOwnUpdater = (updateName in prototype) || data.hasOwnProperty(updateName),
                optimizedGetter, customGetter;

            if (value === null || (!hasOwnSetter && !hasOwnApplier && !hasOwnUpdater)) {
                prototype[internalName] = value;
                prototype[initializedName] = true;
            }
            else {
                prototype[initializedName] = false;
            }

            if (!hasOwnSetter) {
                data[setName] = function(value) {
                    var oldValue = this[internalName],
                        applier = this[applyName],
                        updater = this[updateName];

                    if (!this[initializedName]) {
                        this[initializedName] = true;
                    }

                    if (applier) {
                        value = applier.call(this, value, oldValue);
                    }

                    if (typeof value != 'undefined') {
                        this[internalName] = value;

                        if (updater && value !== oldValue) {
                            updater.call(this, value, oldValue);
                        }
                    }

                    return this;
                };
            }

            if (!(getName in prototype) || data.hasOwnProperty(getName)) {
                customGetter = data[getName] || false;

                if (customGetter) {
                    optimizedGetter = function() {
                        return customGetter.apply(this, arguments);
                    };
                }
                else {
                    optimizedGetter = function() {
                        return this[internalName];
                    };
                }

                data[getName] = function() {
                    var currentGetter;

                    if (!this[initializedName]) {
                        this[initializedName] = true;
                        this[setName](this.config[name]);
                    }

                    currentGetter = this[getName];

                    if ('$previous' in currentGetter) {
                        currentGetter.$previous = optimizedGetter;
                    }
                    else {
                        this[getName] = optimizedGetter;
                    }

                    return optimizedGetter.apply(this, arguments);
                };
            }
        });

        Class.addConfig(config, true);
    });

    
    ExtClass.registerPreprocessor('mixins', function(Class, data, hooks) {
        
        var mixins = data.mixins,
            name, mixin, i, ln;

        delete data.mixins;

        Ext.Function.interceptBefore(hooks, 'onCreated', function() {
        
            if (mixins instanceof Array) {
                for (i = 0,ln = mixins.length; i < ln; i++) {
                    mixin = mixins[i];
                    name = mixin.prototype.mixinId || mixin.$className;

                    Class.mixin(name, mixin);
                }
            }
            else {
                for (var mixinName in mixins) {
                    if (mixins.hasOwnProperty(mixinName)) {
                        Class.mixin(mixinName, mixins[mixinName]);
                    }
                }
            }
        });
    });

    
    Ext.extend = function(Class, Parent, members) {
            
        if (arguments.length === 2 && Ext.isObject(Parent)) {
            members = Parent;
            Parent = Class;
            Class = null;
        }

        var cls;

        if (!Parent) {
            throw new Error("[Ext.extend] Attempting to extend from a class which has not been loaded on the page.");
        }

        members.extend = Parent;
        members.preprocessors = [
            'extend'
            ,'statics'
            ,'inheritableStatics'
            ,'mixins'
            ,'config'
        ];

        if (Class) {
            cls = new ExtClass(Class, members);
            
            cls.prototype.constructor = Class;
        } else {
            cls = new ExtClass(members);
        }

        cls.prototype.override = function(o) {
            for (var m in o) {
                if (o.hasOwnProperty(m)) {
                    this[m] = o[m];
                }
            }
        };

        return cls;
    };
}());






(function(Class, alias, arraySlice, arrayFrom, global) {

    
    function makeCtor () {
        function constructor () {
            
            
            return this.constructor.apply(this, arguments) || null;
        }
        return constructor;
    }

    var Manager = Ext.ClassManager = {

        
        classes: {},

        
        existCache: {},

        
        namespaceRewrites: [{
            from: 'Ext.',
            to: Ext
        }],

        
        maps: {
            alternateToName: {},
            aliasToName: {},
            nameToAliases: {},
            nameToAlternates: {}
        },

        
        enableNamespaceParseCache: true,

        
        namespaceParseCache: {},

        
        instantiators: [],

        
        isCreated: function(className) {
            var existCache = this.existCache,
                i, ln, part, root, parts;


            if (this.classes[className] || existCache[className]) {
                return true;
            }

            root = global;
            parts = this.parseNamespace(className);

            for (i = 0, ln = parts.length; i < ln; i++) {
                part = parts[i];

                if (typeof part != 'string') {
                    root = part;
                } else {
                    if (!root || !root[part]) {
                        return false;
                    }

                    root = root[part];
                }
            }

            existCache[className] = true;

            this.triggerCreated(className);

            return true;
        },

        
        createdListeners: [],

        
        nameCreatedListeners: {},

        
        triggerCreated: function(className) {
            var listeners = this.createdListeners,
                nameListeners = this.nameCreatedListeners,
                alternateNames = this.maps.nameToAlternates[className],
                names = [className],
                i, ln, j, subLn, listener, name;

            for (i = 0,ln = listeners.length; i < ln; i++) {
                listener = listeners[i];
                listener.fn.call(listener.scope, className);
            }

            if (alternateNames) {
                names.push.apply(names, alternateNames);
            }

            for (i = 0,ln = names.length; i < ln; i++) {
                name = names[i];
                listeners = nameListeners[name];

                if (listeners) {
                    for (j = 0,subLn = listeners.length; j < subLn; j++) {
                        listener = listeners[j];
                        listener.fn.call(listener.scope, name);
                    }
                    delete nameListeners[name];
                }
            }
        },

        
        onCreated: function(fn, scope, className) {
            
            var listeners = this.createdListeners,
                nameListeners = this.nameCreatedListeners,
                listener = {
                    fn: fn,
                    scope: scope
                };

            if (className) {
                if (this.isCreated(className)) {
                    fn.call(scope, className);
                    return;
                }

                if (!nameListeners[className]) {
                    nameListeners[className] = [];
                }

                nameListeners[className].push(listener);
            }
            else {
                listeners.push(listener);
            }
        },

        
        parseNamespace: function(namespace) {

            var cache = this.namespaceParseCache,
                parts,
                rewrites,
                root,
                name,
                rewrite, from, to, i, ln;

            if (this.enableNamespaceParseCache) {
                if (cache.hasOwnProperty(namespace)) {
                    return cache[namespace];
                }
            }

            parts = [];
            rewrites = this.namespaceRewrites;
            root = global;
            name = namespace;

            for (i = 0, ln = rewrites.length; i < ln; i++) {
                rewrite = rewrites[i];
                from = rewrite.from;
                to = rewrite.to;

                if (name === from || name.substring(0, from.length) === from) {
                    name = name.substring(from.length);

                    if (typeof to != 'string') {
                        root = to;
                    } else {
                        parts = parts.concat(to.split('.'));
                    }

                    break;
                }
            }

            parts.push(root);

            parts = parts.concat(name.split('.'));

            if (this.enableNamespaceParseCache) {
                cache[namespace] = parts;
            }

            return parts;
        },

        
        setNamespace: function(name, value) {
            var root = global,
                parts = this.parseNamespace(name),
                ln = parts.length - 1,
                leaf = parts[ln],
                i, part;

            for (i = 0; i < ln; i++) {
                part = parts[i];

                if (typeof part != 'string') {
                    root = part;
                } else {
                    if (!root[part]) {
                        root[part] = {};
                    }

                    root = root[part];
                }
            }

            root[leaf] = value;

            return root[leaf];
        },

        
        createNamespaces: function() {
            var root = global,
                parts, part, i, j, ln, subLn;

            for (i = 0, ln = arguments.length; i < ln; i++) {
                parts = this.parseNamespace(arguments[i]);

                for (j = 0, subLn = parts.length; j < subLn; j++) {
                    part = parts[j];

                    if (typeof part != 'string') {
                        root = part;
                    } else {
                        if (!root[part]) {
                            root[part] = {};
                        }

                        root = root[part];
                    }
                }
            }

            return root;
        },

        
        set: function(name, value) {
            var me = this,
                maps = me.maps,
                nameToAlternates = maps.nameToAlternates,
                targetName = me.getName(value),
                alternates;

            me.classes[name] = me.setNamespace(name, value);

            if (targetName && targetName !== name) {
                maps.alternateToName[name] = targetName;
                alternates = nameToAlternates[targetName] || (nameToAlternates[targetName] = []);
                alternates.push(name);
            }

            return this;
        },

        
        get: function(name) {
            var classes = this.classes,
                root,
                parts,
                part, i, ln;

            if (classes[name]) {
                return classes[name];
            }

            root = global;
            parts = this.parseNamespace(name);

            for (i = 0, ln = parts.length; i < ln; i++) {
                part = parts[i];

                if (typeof part != 'string') {
                    root = part;
                } else {
                    if (!root || !root[part]) {
                        return null;
                    }

                    root = root[part];
                }
            }

            return root;
        },

        
        setAlias: function(cls, alias) {
            var aliasToNameMap = this.maps.aliasToName,
                nameToAliasesMap = this.maps.nameToAliases,
                className;

            if (typeof cls == 'string') {
                className = cls;
            } else {
                className = this.getName(cls);
            }

            if (alias && aliasToNameMap[alias] !== className) {

                aliasToNameMap[alias] = className;
            }

            if (!nameToAliasesMap[className]) {
                nameToAliasesMap[className] = [];
            }

            if (alias) {
                Ext.Array.include(nameToAliasesMap[className], alias);
            }

            return this;
        },

        
        addNameAliasMappings: function(aliases){
            var aliasToNameMap = this.maps.aliasToName,
                nameToAliasesMap = this.maps.nameToAliases,
                className, aliasList, alias, i;

            for (className in aliases) {
                aliasList = nameToAliasesMap[className] ||
                    (nameToAliasesMap[className] = []);

                for (i = 0; i < aliases[className].length; i++) {
                    alias = aliases[className][i];
                    if (!aliasToNameMap[alias]) {
                        aliasToNameMap[alias] = className;
                        aliasList.push(alias);
                    }
                }

            }
            return this;
        },

        
        addNameAlternateMappings: function(alternates) {
            var alternateToName = this.maps.alternateToName,
                nameToAlternates = this.maps.nameToAlternates,
                className, aliasList, alternate, i;

            for (className in alternates) {
                aliasList = nameToAlternates[className] ||
                    (nameToAlternates[className] = []);

                for (i  = 0; i < alternates[className].length; i++) {
                    alternate = alternates[className][i];
                    if (!alternateToName[alternate]) {
                        alternateToName[alternate] = className;
                        aliasList.push(alternate);
                    }
                }

            }
            return this;
        },

        
        getByAlias: function(alias) {
            return this.get(this.getNameByAlias(alias));
        },

        
        getNameByAlias: function(alias) {
            return this.maps.aliasToName[alias] || '';
        },

        
        getNameByAlternate: function(alternate) {
            return this.maps.alternateToName[alternate] || '';
        },

        
        getAliasesByName: function(name) {
            return this.maps.nameToAliases[name] || [];
        },

        
        getName: function(object) {
            return object && object.$className || '';
        },

        
        getClass: function(object) {
            return object && object.self || null;
        },

        
        create: function(className, data, createdFn) {

            var ctor = makeCtor();
            if (typeof data == 'function') {
                data = data(ctor);
            }


            data.$className = className;

            return new Class(ctor, data, function() {
                var postprocessorStack = data.postprocessors || Manager.defaultPostprocessors,
                    registeredPostprocessors = Manager.postprocessors,
                    postprocessors = [],
                    postprocessor, i, ln, j, subLn, postprocessorProperties, postprocessorProperty;

                delete data.postprocessors;

                for (i = 0,ln = postprocessorStack.length; i < ln; i++) {
                    postprocessor = postprocessorStack[i];

                    if (typeof postprocessor == 'string') {
                        postprocessor = registeredPostprocessors[postprocessor];
                        postprocessorProperties = postprocessor.properties;

                        if (postprocessorProperties === true) {
                            postprocessors.push(postprocessor.fn);
                        }
                        else if (postprocessorProperties) {
                            for (j = 0,subLn = postprocessorProperties.length; j < subLn; j++) {
                                postprocessorProperty = postprocessorProperties[j];

                                if (data.hasOwnProperty(postprocessorProperty)) {
                                    postprocessors.push(postprocessor.fn);
                                    break;
                                }
                            }
                        }
                    }
                    else {
                        postprocessors.push(postprocessor);
                    }
                }

                data.postprocessors = postprocessors;
                data.createdFn = createdFn;
                Manager.processCreate(className, this, data);
            });
        },

        processCreate: function(className, cls, clsData){
            var me = this,
                postprocessor = clsData.postprocessors.shift(),
                createdFn = clsData.createdFn;

            if (!postprocessor) {
                
                if (className) {
                    me.set(className, cls);
                }

                if (createdFn) {
                    createdFn.call(cls, cls);
                }

                if (className) {
                    me.triggerCreated(className);
                }
                return;
            }

            if (postprocessor.call(me, className, cls, clsData, me.processCreate) !== false) {
                me.processCreate(className, cls, clsData);
            }
        },

        createOverride: function (className, data, createdFn) {
            var me = this,
                overriddenClassName = data.override,
                requires = data.requires,
                uses = data.uses,
                classReady = function () {
                    var cls, temp;

                    if (requires) {
                        temp = requires;
                        requires = null; 

                        
                        
                        
                        Ext.Loader.require(temp, classReady);
                    } else {
                        
                        
                        cls = me.get(overriddenClassName);

                        
                        delete data.override;
                        delete data.requires;
                        delete data.uses;

                        Ext.override(cls, data);

                        
                        
                        
                        me.triggerCreated(className);

                        if (uses) {
                            Ext.Loader.addUsedClasses(uses); 
                        }

                        if (createdFn) {
                            createdFn.call(cls); 
                        }
                    }
                };

            me.existCache[className] = true;

            
            me.onCreated(classReady, me, overriddenClassName);

            return me;
        },

        
        instantiateByAlias: function() {
            var alias = arguments[0],
                args = arraySlice.call(arguments),
                className = this.getNameByAlias(alias);

            if (!className) {
                className = this.maps.aliasToName[alias];



                Ext.syncRequire(className);
            }

            args[0] = className;

            return this.instantiate.apply(this, args);
        },

        
        instantiate: function() {
            var name = arguments[0],
                nameType = typeof name,
                args = arraySlice.call(arguments, 1),
                alias = name,
                possibleName, cls;

            if (nameType != 'function') {
                if (nameType != 'string' && args.length === 0) {
                    args = [name];
                    name = name.xclass;
                }


                cls = this.get(name);
            }
            else {
                cls = name;
            }

            
            if (!cls) {
                possibleName = this.getNameByAlias(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            
            if (!cls) {
                possibleName = this.getNameByAlternate(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            
            if (!cls) {

                Ext.syncRequire(name);

                cls = this.get(name);
            }


            return this.getInstantiator(args.length)(cls, args);
        },

        
        dynInstantiate: function(name, args) {
            args = arrayFrom(args, true);
            args.unshift(name);

            return this.instantiate.apply(this, args);
        },

        
        getInstantiator: function(length) {
            var instantiators = this.instantiators,
                instantiator,
                i,
                args;

            instantiator = instantiators[length];

            if (!instantiator) {
                i = length;
                args = [];

                for (i = 0; i < length; i++) {
                    args.push('a[' + i + ']');
                }

                instantiator = instantiators[length] = new Function('c', 'a', 'return new c(' + args.join(',') + ')');
            }

            return instantiator;
        },

        
        postprocessors: {},

        
        defaultPostprocessors: [],

        
        registerPostprocessor: function(name, fn, properties, position, relativeTo) {
            if (!position) {
                position = 'last';
            }

            if (!properties) {
                properties = [name];
            }

            this.postprocessors[name] = {
                name: name,
                properties: properties || false,
                fn: fn
            };

            this.setDefaultPostprocessorPosition(name, position, relativeTo);

            return this;
        },

        
        setDefaultPostprocessors: function(postprocessors) {
            this.defaultPostprocessors = arrayFrom(postprocessors);

            return this;
        },

        
        setDefaultPostprocessorPosition: function(name, offset, relativeName) {
            var defaultPostprocessors = this.defaultPostprocessors,
                index;

            if (typeof offset == 'string') {
                if (offset === 'first') {
                    defaultPostprocessors.unshift(name);

                    return this;
                }
                else if (offset === 'last') {
                    defaultPostprocessors.push(name);

                    return this;
                }

                offset = (offset === 'after') ? 1 : -1;
            }

            index = Ext.Array.indexOf(defaultPostprocessors, relativeName);

            if (index !== -1) {
                Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
            }

            return this;
        },

        
        getNamesByExpression: function(expression) {
            var nameToAliasesMap = this.maps.nameToAliases,
                names = [],
                name, alias, aliases, possibleName, regex, i, ln;


            if (expression.indexOf('*') !== -1) {
                expression = expression.replace(/\*/g, '(.*?)');
                regex = new RegExp('^' + expression + '$');

                for (name in nameToAliasesMap) {
                    if (nameToAliasesMap.hasOwnProperty(name)) {
                        aliases = nameToAliasesMap[name];

                        if (name.search(regex) !== -1) {
                            names.push(name);
                        }
                        else {
                            for (i = 0, ln = aliases.length; i < ln; i++) {
                                alias = aliases[i];

                                if (alias.search(regex) !== -1) {
                                    names.push(name);
                                    break;
                                }
                            }
                        }
                    }
                }

            } else {
                possibleName = this.getNameByAlias(expression);

                if (possibleName) {
                    names.push(possibleName);
                } else {
                    possibleName = this.getNameByAlternate(expression);

                    if (possibleName) {
                        names.push(possibleName);
                    } else {
                        names.push(expression);
                    }
                }
            }

            return names;
        }
    };

    
    Manager.registerPostprocessor('alias', function(name, cls, data) {
        
        var aliases = data.alias,
            i, ln;

        for (i = 0,ln = aliases.length; i < ln; i++) {
            alias = aliases[i];

            this.setAlias(cls, alias);
        }

    }, ['xtype', 'alias']);

    
    Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
        
        if (data.singleton) {
            fn.call(this, name, new cls(), data);
        }
        else {
            return true;
        }
        return false;
    });

    
    Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
        
        var alternates = data.alternateClassName,
            i, ln, alternate;

        if (!(alternates instanceof Array)) {
            alternates = [alternates];
        }

        for (i = 0, ln = alternates.length; i < ln; i++) {
            alternate = alternates[i];


            this.set(alternate, cls);
        }
    });

    Ext.apply(Ext, {
        
        create: alias(Manager, 'instantiate'),

        
        widget: function(name, config) {
            
            
            
            
            
            
            
            var xtype = name,
                alias, className, T, load;

            if (typeof xtype != 'string') { 
                
                config = name; 
                xtype = config.xtype;
            } else {
                config = config || {};
            }

            if (config.isComponent) {
                return config;
            }

            alias = 'widget.' + xtype;
            className = Manager.getNameByAlias(alias);

            
            if (!className) {
                load = true;
            }

            T = Manager.get(className);
            if (load || !T) {
                return Manager.instantiateByAlias(alias, config);
            }
            return new T(config);
        },

        
        createByAlias: alias(Manager, 'instantiateByAlias'),

        
        define: function (className, data, createdFn) {
            
            if (data.override) {
                return Manager.createOverride.apply(Manager, arguments);
            }

            return Manager.create.apply(Manager, arguments);
        },

        
        undefine: function(className) {
        
            var classes = Manager.classes,
                maps = Manager.maps,
                aliasToName = maps.aliasToName,
                nameToAliases = maps.nameToAliases,
                alternateToName = maps.alternateToName,
                nameToAlternates = maps.nameToAlternates,
                aliases = nameToAliases[className],
                alternates = nameToAlternates[className],
                parts, partCount, namespace, i;

            delete Manager.namespaceParseCache[className];
            delete nameToAliases[className];
            delete nameToAlternates[className];
            delete classes[className];

            if (aliases) {
                for (i = aliases.length; i--;) {
                    delete aliasToName[aliases[i]];
                }
            }

            if (alternates) {
                for (i = alternates.length; i--; ) {
                    delete alternateToName[alternates[i]];
                }
            }

            parts  = Manager.parseNamespace(className);
            partCount = parts.length - 1;
            namespace = parts[0];

            for (i = 1; i < partCount; i++) {
                namespace = namespace[parts[i]];
                if (!namespace) {
                    return;
                }
            }

            
            try {
                delete namespace[parts[partCount]];
            }
            catch (e) {
                namespace[parts[partCount]] = undefined;
            }
        },

        
        getClassName: alias(Manager, 'getName'),

        
        getDisplayName: function(object) {
            if (object) {
                if (object.displayName) {
                    return object.displayName;
                }

                if (object.$name && object.$class) {
                    return Ext.getClassName(object.$class) + '#' + object.$name;
                }

                if (object.$className) {
                    return object.$className;
                }
            }

            return 'Anonymous';
        },

        
        getClass: alias(Manager, 'getClass'),

        
        namespace: alias(Manager, 'createNamespaces')
    });

    
    Ext.createWidget = Ext.widget;

    
    Ext.ns = Ext.namespace;

    Class.registerPreprocessor('className', function(cls, data) {
        if (data.$className) {
            cls.$className = data.$className;
        }
        
    }, true, 'first');

    Class.registerPreprocessor('alias', function(cls, data) {
        
        var prototype = cls.prototype,
            xtypes = arrayFrom(data.xtype),
            aliases = arrayFrom(data.alias),
            widgetPrefix = 'widget.',
            widgetPrefixLength = widgetPrefix.length,
            xtypesChain = Array.prototype.slice.call(prototype.xtypesChain || []),
            xtypesMap = Ext.merge({}, prototype.xtypesMap || {}),
            i, ln, alias, xtype;

        for (i = 0,ln = aliases.length; i < ln; i++) {
            alias = aliases[i];


            if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
                xtype = alias.substring(widgetPrefixLength);
                Ext.Array.include(xtypes, xtype);
            }
        }

        cls.xtype = data.xtype = xtypes[0];
        data.xtypes = xtypes;

        for (i = 0,ln = xtypes.length; i < ln; i++) {
            xtype = xtypes[i];

            if (!xtypesMap[xtype]) {
                xtypesMap[xtype] = true;
                xtypesChain.push(xtype);
            }
        }

        data.xtypesChain = xtypesChain;
        data.xtypesMap = xtypesMap;

        Ext.Function.interceptAfter(data, 'onClassCreated', function() {
        
            var mixins = prototype.mixins,
                key, mixin;

            for (key in mixins) {
                if (mixins.hasOwnProperty(key)) {
                    mixin = mixins[key];

                    xtypes = mixin.xtypes;

                    if (xtypes) {
                        for (i = 0,ln = xtypes.length; i < ln; i++) {
                            xtype = xtypes[i];

                            if (!xtypesMap[xtype]) {
                                xtypesMap[xtype] = true;
                                xtypesChain.push(xtype);
                            }
                        }
                    }
                }
            }
        });

        for (i = 0,ln = xtypes.length; i < ln; i++) {
            xtype = xtypes[i];


            Ext.Array.include(aliases, widgetPrefix + xtype);
        }

        data.alias = aliases;

    }, ['xtype', 'alias']);

}(Ext.Class, Ext.Function.alias, Array.prototype.slice, Ext.Array.from, Ext.global));



if (Ext._alternatesMetadata) {
   Ext.ClassManager.addNameAlternateMappings(Ext._alternatesMetadata);
   Ext._alternatesMetadata = null;
}

if (Ext._aliasMetadata) {
    Ext.ClassManager.addNameAliasMappings(Ext._aliasMetadata);
    Ext._aliasMetadata = null;
}







Ext.Loader = new function() {
    var Loader = this,
        Manager = Ext.ClassManager,
        Class = Ext.Class,
        flexSetter = Ext.Function.flexSetter,
        alias = Ext.Function.alias,
        pass = Ext.Function.pass,
        defer = Ext.Function.defer,
        arrayErase = Ext.Array.erase,
        dependencyProperties = ['extend', 'mixins', 'requires'],
        isInHistory = {},
        history = [],
        slashDotSlashRe = /\/\.\//g,
        dotRe = /\./g,
        setPathCount = 0;

    Ext.apply(Loader, {

        
        isInHistory: isInHistory,

        
        history: history,

        
        config: {
            
            enabled: false,

            
            scriptChainDelay : false,

            
            disableCaching: true,

            
            disableCachingParam: '_dc',

            
            garbageCollect : false,

            
            paths: {
                'Ext': '.'
            },

            
            preserveScripts : true,

            
            scriptCharset : undefined
        },

        
        setConfig: function(name, value) {
            if (Ext.isObject(name) && arguments.length === 1) {
                Ext.merge(Loader.config, name);

                if ('paths' in name) {
                    Ext.app.collectNamespaces(name.paths);
                }
            }
            else {
                Loader.config[name] = (Ext.isObject(value)) ? Ext.merge(Loader.config[name], value) : value;

                if (name === 'paths') {
                    Ext.app.collectNamespaces(value);
                }
            }

            return Loader;
        },

        
        getConfig: function(name) {
            if (name) {
                return Loader.config[name];
            }

            return Loader.config;
        },

        
        setPath: flexSetter(function(name, path) {
            Loader.config.paths[name] = path;
            Ext.app.namespaces[name] = true;
            setPathCount++;

            return Loader;
        }),

        
        addClassPathMappings: function(paths) {
            var name;

            if(setPathCount == 0){
                Loader.config.paths = paths;
            } else {
                for(name in paths){
                    Loader.config.paths[name] = paths[name];
                }
            }
            setPathCount++;
            return Loader;
        },

        
        getPath: function(className) {
            var path = '',
                paths = Loader.config.paths,
                prefix = Loader.getPrefix(className);

            if (prefix.length > 0) {
                if (prefix === className) {
                    return paths[prefix];
                }

                path = paths[prefix];
                className = className.substring(prefix.length + 1);
            }

            if (path.length > 0) {
                path += '/';
            }

            return path.replace(slashDotSlashRe, '/') + className.replace(dotRe, "/") + '.js';
        },

        
        getPrefix: function(className) {
            var paths = Loader.config.paths,
                prefix, deepestPrefix = '';

            if (paths.hasOwnProperty(className)) {
                return className;
            }

            for (prefix in paths) {
                if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
                    if (prefix.length > deepestPrefix.length) {
                        deepestPrefix = prefix;
                    }
                }
            }

            return deepestPrefix;
        },

        
        isAClassNameWithAKnownPrefix: function(className) {
            var prefix = Loader.getPrefix(className);

            
            return prefix !== '' && prefix !== className;
        },

        
        require: function(expressions, fn, scope, excludes) {
            if (fn) {
                fn.call(scope);
            }
        },

        
        syncRequire: function() {},

        
        exclude: function(excludes) {
            return {
                require: function(expressions, fn, scope) {
                    return Loader.require(expressions, fn, scope, excludes);
                },

                syncRequire: function(expressions, fn, scope) {
                    return Loader.syncRequire(expressions, fn, scope, excludes);
                }
            };
        },

        
        onReady: function(fn, scope, withDomReady, options) {
            var oldFn;

            if (withDomReady !== false && Ext.onDocumentReady) {
                oldFn = fn;

                fn = function() {
                    Ext.onDocumentReady(oldFn, scope, options);
                };
            }

            fn.call(scope);
        }
    });

    var queue = [],
        isClassFileLoaded = {},
        isFileLoaded = {},
        classNameToFilePathMap = {},
        scriptElements = {},
        readyListeners = [],
        usedClasses = [],
        requiresMap = {},
        comparePriority = function(listenerA, listenerB) {
            return listenerB.priority - listenerA.priority;
        };

    Ext.apply(Loader, {
        
        documentHead: typeof document != 'undefined' && (document.head || document.getElementsByTagName('head')[0]),

        
        isLoading: false,

        
        queue: queue,

        
        isClassFileLoaded: isClassFileLoaded,

        
        isFileLoaded: isFileLoaded,

        
        readyListeners: readyListeners,

        
        optionalRequires: usedClasses,

        
        requiresMap: requiresMap,

        
        numPendingFiles: 0,

        
        numLoadedFiles: 0,

        
        hasFileLoadError: false,

        
        classNameToFilePathMap: classNameToFilePathMap,

        
        scriptsLoading: 0,

        
        syncModeEnabled: false,

        scriptElements: scriptElements,

        
        refreshQueue: function() {
            var ln = queue.length,
                i, item, j, requires;

            

            if (!ln && !Loader.scriptsLoading) {
                return Loader.triggerReady();
            }

            for (i = 0; i < ln; i++) {
                item = queue[i];

                if (item) {
                    requires = item.requires;

                    
                    
                    if (requires.length > Loader.numLoadedFiles) {
                        continue;
                    }

                    
                    for (j = 0; j < requires.length; ) {
                        if (Manager.isCreated(requires[j])) {
                            
                            arrayErase(requires, j, 1);
                        }
                        else {
                            j++;
                        }
                    }

                    
                    if (item.requires.length === 0) {
                        arrayErase(queue, i, 1);
                        item.callback.call(item.scope);
                        Loader.refreshQueue();
                        break;
                    }
                }
            }

            return Loader;
        },

        
        injectScriptElement: function(url, onLoad, onError, scope, charset) {
            var script = document.createElement('script'),
                dispatched = false,
                config = Loader.config,
                onLoadFn = function() {

                    if(!dispatched) {
                        dispatched = true;
                        script.onload = script.onreadystatechange = script.onerror = null;
                        if (typeof config.scriptChainDelay == 'number') {
                            
                            defer(onLoad, config.scriptChainDelay, scope);
                        } else {
                            onLoad.call(scope);
                        }
                        Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect);
                    }

                },
                onErrorFn = function(arg) {
                    defer(onError, 1, scope);   
                    Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect);
                };

            script.type = 'text/javascript';
            script.onerror = onErrorFn;
            charset = charset || config.scriptCharset;
            if (charset) {
                script.charset = charset;
            }

            
            if ('addEventListener' in script ) {
                script.onload = onLoadFn;
            } else if ('readyState' in script) {   
                script.onreadystatechange = function() {
                    if ( this.readyState == 'loaded' || this.readyState == 'complete' ) {
                        onLoadFn();
                    }
                };
            } else {
                 script.onload = onLoadFn;
            }

            script.src = url;
            (Loader.documentHead || document.getElementsByTagName('head')[0]).appendChild(script);

            return script;
        },

        
        removeScriptElement: function(url) {
            if (scriptElements[url]) {
                Loader.cleanupScriptElement(scriptElements[url], true, !!Loader.getConfig('garbageCollect'));
                delete scriptElements[url];
            }

            return Loader;
        },

        
        cleanupScriptElement: function(script, remove, collect) {
            var prop;
            script.onload = script.onreadystatechange = script.onerror = null;
            if (remove) {
                Ext.removeNode(script);       
                if (collect) {
                    for (prop in script) {
                        try {
                            if (prop != 'src') {
                                
                                
                                script[prop] = null;
                            }
                            delete script[prop];      
                        } catch (cleanEx) {
                            
                        }
                    }
                }
            }

            return Loader;
        },

        
        loadScript: function (options) {
            var config = Loader.getConfig(),
                isString = typeof options == 'string',
                url = isString ? options : options.url,
                onError = !isString && options.onError,
                onLoad = !isString && options.onLoad,
                scope = !isString && options.scope,
                onScriptError = function() {
                    Loader.numPendingFiles--;
                    Loader.scriptsLoading--;

                    if (onError) {
                        onError.call(scope, "Failed loading '" + url + "', please verify that the file exists");
                    }

                    if (Loader.numPendingFiles + Loader.scriptsLoading === 0) {
                        Loader.refreshQueue();
                    }
                },
                onScriptLoad = function () {
                    Loader.numPendingFiles--;
                    Loader.scriptsLoading--;

                    if (onLoad) {
                        onLoad.call(scope);
                    }

                    if (Loader.numPendingFiles + Loader.scriptsLoading === 0) {
                        Loader.refreshQueue();
                    }
                },
                src;

            Loader.isLoading = true;
            Loader.numPendingFiles++;
            Loader.scriptsLoading++;

            src = config.disableCaching ?
                (url + '?' + config.disableCachingParam + '=' + Ext.Date.now()) : url;

            scriptElements[url] = Loader.injectScriptElement(src, onScriptLoad, onScriptError);
        },

        
        loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
            if (isFileLoaded[url]) {
                return Loader;
            }

            var config = Loader.getConfig(),
                noCacheUrl = url + (config.disableCaching ? ('?' + config.disableCachingParam + '=' + Ext.Date.now()) : ''),
                isCrossOriginRestricted = false,
                xhr, status, onScriptError,
                debugSourceURL = "";

            scope = scope || Loader;

            Loader.isLoading = true;

            if (!synchronous) {
                onScriptError = function() {
                };

                scriptElements[url] = Loader.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
            } else {
                if (typeof XMLHttpRequest != 'undefined') {
                    xhr = new XMLHttpRequest();
                } else {
                    xhr = new ActiveXObject('Microsoft.XMLHTTP');
                }

                try {
                    xhr.open('GET', noCacheUrl, false);
                    xhr.send(null);
                } catch (e) {
                    isCrossOriginRestricted = true;
                }

                status = (xhr.status === 1223) ? 204 :
                    (xhr.status === 0 && ((self.location || {}).protocol == 'file:' || (self.location || {}).protocol == 'ionp:')) ? 200 : xhr.status;

                isCrossOriginRestricted = isCrossOriginRestricted || (status === 0);

                if (isCrossOriginRestricted
                ) {
                }
                else if ((status >= 200 && status < 300) || (status === 304)
                ) {
                    
                    
                    if (!Ext.isIE) {
                        debugSourceURL = "\n//@ sourceURL=" + url;
                    }

                    Ext.globalEval(xhr.responseText + debugSourceURL);

                    onLoad.call(scope);
                }
                else {
                }

                
                xhr = null;
            }
        },

        
        syncRequire: function() {
            var syncModeEnabled = Loader.syncModeEnabled;

            if (!syncModeEnabled) {
                Loader.syncModeEnabled = true;
            }

            Loader.require.apply(Loader, arguments);

            if (!syncModeEnabled) {
                Loader.syncModeEnabled = false;
            }

            Loader.refreshQueue();
        },

        
        require: function(expressions, fn, scope, excludes) {
            var excluded = {},
                included = {},
                excludedClassNames = [],
                possibleClassNames = [],
                classNames = [],
                references = [],
                callback,
                syncModeEnabled,
                filePath, expression, exclude, className,
                possibleClassName, i, j, ln, subLn;

            if (excludes) {
                
                excludes = (typeof excludes === 'string') ? [ excludes ] : excludes;

                for (i = 0,ln = excludes.length; i < ln; i++) {
                    exclude = excludes[i];

                    if (typeof exclude == 'string' && exclude.length > 0) {
                        excludedClassNames = Manager.getNamesByExpression(exclude);

                        for (j = 0,subLn = excludedClassNames.length; j < subLn; j++) {
                            excluded[excludedClassNames[j]] = true;
                        }
                    }
                }
            }

            
            expressions = (typeof expressions === 'string') ? [ expressions ] : (expressions ? expressions : []);

            if (fn) {
                if (fn.length > 0) {
                    callback = function() {
                        var classes = [],
                            i, ln;

                        for (i = 0,ln = references.length; i < ln; i++) {
                            classes.push(Manager.get(references[i]));
                        }

                        return fn.apply(this, classes);
                    };
                }
                else {
                    callback = fn;
                }
            }
            else {
                callback = Ext.emptyFn;
            }

            scope = scope || Ext.global;

            for (i = 0,ln = expressions.length; i < ln; i++) {
                expression = expressions[i];

                if (typeof expression == 'string' && expression.length > 0) {
                    possibleClassNames = Manager.getNamesByExpression(expression);
                    subLn = possibleClassNames.length;

                    for (j = 0; j < subLn; j++) {
                        possibleClassName = possibleClassNames[j];

                        if (excluded[possibleClassName] !== true) {
                            references.push(possibleClassName);

                            if (!Manager.isCreated(possibleClassName) && !included[possibleClassName]) {
                                included[possibleClassName] = true;
                                classNames.push(possibleClassName);
                            }
                        }
                    }
                }
            }

            
            
            if (classNames.length > 0) {
                if (!Loader.config.enabled) {
                    throw new Error("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
                             "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', '));
                }
            }
            else {
                callback.call(scope);
                return Loader;
            }

            syncModeEnabled = Loader.syncModeEnabled;

            if (!syncModeEnabled) {
                queue.push({
                    requires: classNames.slice(), 
                                                  
                    callback: callback,
                    scope: scope
                });
            }

            ln = classNames.length;

            for (i = 0; i < ln; i++) {
                className = classNames[i];

                filePath = Loader.getPath(className);

                
                
                
                if (syncModeEnabled && isClassFileLoaded.hasOwnProperty(className)) {
                    if (!isClassFileLoaded[className]) {
                        Loader.numPendingFiles--;
                        Loader.removeScriptElement(filePath);
                        delete isClassFileLoaded[className];
                    }
                }

                if (!isClassFileLoaded.hasOwnProperty(className)) {
                    isClassFileLoaded[className] = false;
                    classNameToFilePathMap[className] = filePath;

                    Loader.numPendingFiles++;
                    Loader.loadScriptFile(
                        filePath,
                        pass(Loader.onFileLoaded, [className, filePath], Loader),
                        pass(Loader.onFileLoadError, [className, filePath], Loader),
                        Loader,
                        syncModeEnabled
                    );
                }
            }

            if (syncModeEnabled) {
                callback.call(scope);

                if (ln === 1) {
                    return Manager.get(className);
                }
            }

            return Loader;
        },

        
        onFileLoaded: function(className, filePath) {
            var loaded = isClassFileLoaded[className];
            Loader.numLoadedFiles++;

            isClassFileLoaded[className] = true;
            isFileLoaded[filePath] = true;

            
            
            if (!loaded) {
                Loader.numPendingFiles--;
            }

            if (Loader.numPendingFiles === 0) {
                Loader.refreshQueue();
            }

        },

        
        onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
            Loader.numPendingFiles--;
            Loader.hasFileLoadError = true;

        },

        
        addUsedClasses: function (classes) {
            var cls, i, ln;

            if (classes) {
                classes = (typeof classes == 'string') ? [classes] : classes;
                for (i = 0, ln = classes.length; i < ln; i++) {
                    cls = classes[i];
                    if (typeof cls == 'string' && !Ext.Array.contains(usedClasses, cls)) {
                        usedClasses.push(cls);
                    }
                }
            }

            return Loader;
        },

        
        triggerReady: function() {
            var listener,
                refClasses = usedClasses;

            if (Loader.isLoading) {
                Loader.isLoading = false;

                if (refClasses.length !== 0) {
                    
                    refClasses = refClasses.slice();
                    usedClasses.length = 0;
                    
                    
                    Loader.require(refClasses, Loader.triggerReady, Loader);
                    return Loader;
                }
            }

            Ext.Array.sort(readyListeners, comparePriority);

            
            
            
            while (readyListeners.length && !Loader.isLoading) {
                
                
                listener = readyListeners.shift();
                listener.fn.call(listener.scope);
            }

            return Loader;
        },

        
        onReady: function(fn, scope, withDomReady, options) {
            var oldFn;

            if (withDomReady !== false && Ext.onDocumentReady) {
                oldFn = fn;

                fn = function() {
                    Ext.onDocumentReady(oldFn, scope, options);
                };
            }

            if (!Loader.isLoading) {
                fn.call(scope);
            }
            else {
                readyListeners.push({
                    fn: fn,
                    scope: scope,
                    priority: (options && options.priority) || 0
                });
            }
        },

        
        historyPush: function(className) {
            if (className && isClassFileLoaded.hasOwnProperty(className) && !isInHistory[className]) {
                isInHistory[className] = true;
                history.push(className);
            }
            return Loader;
        }
    });

    
    Ext.disableCacheBuster = function (disable, path) {
        var date = new Date();
        date.setTime(date.getTime() + (disable ? 10*365 : -1) * 24*60*60*1000);
        date = date.toGMTString();
        document.cookie = 'ext-cache=1; expires=' + date + '; path='+(path || '/');
    };


    
    Ext.require = alias(Loader, 'require');

    
    Ext.syncRequire = alias(Loader, 'syncRequire');

    
    Ext.exclude = alias(Loader, 'exclude');

    
    Ext.onReady = function(fn, scope, options) {
        Loader.onReady(fn, scope, true, options);
    };

    
    Class.registerPreprocessor('loader', function(cls, data, hooks, continueFn) {
        
        var me = this,
            dependencies = [],
            dependency,
            className = Manager.getName(cls),
            i, j, ln, subLn, value, propertyName, propertyValue,
            requiredMap, requiredDep;

        

        for (i = 0,ln = dependencyProperties.length; i < ln; i++) {
            propertyName = dependencyProperties[i];

            if (data.hasOwnProperty(propertyName)) {
                propertyValue = data[propertyName];

                if (typeof propertyValue == 'string') {
                    dependencies.push(propertyValue);
                }
                else if (propertyValue instanceof Array) {
                    for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
                        value = propertyValue[j];

                        if (typeof value == 'string') {
                            dependencies.push(value);
                        }
                    }
                }
                else if (typeof propertyValue != 'function') {
                    for (j in propertyValue) {
                        if (propertyValue.hasOwnProperty(j)) {
                            value = propertyValue[j];

                            if (typeof value == 'string') {
                                dependencies.push(value);
                            }
                        }
                    }
                }
            }
        }

        if (dependencies.length === 0) {
            return;
        }


        Loader.require(dependencies, function() {
            for (i = 0,ln = dependencyProperties.length; i < ln; i++) {
                propertyName = dependencyProperties[i];

                if (data.hasOwnProperty(propertyName)) {
                    propertyValue = data[propertyName];

                    if (typeof propertyValue == 'string') {
                        data[propertyName] = Manager.get(propertyValue);
                    }
                    else if (propertyValue instanceof Array) {
                        for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
                            value = propertyValue[j];

                            if (typeof value == 'string') {
                                data[propertyName][j] = Manager.get(value);
                            }
                        }
                    }
                    else if (typeof propertyValue != 'function') {
                        for (var k in propertyValue) {
                            if (propertyValue.hasOwnProperty(k)) {
                                value = propertyValue[k];

                                if (typeof value == 'string') {
                                    data[propertyName][k] = Manager.get(value);
                                }
                            }
                        }
                    }
                }
            }

            continueFn.call(me, cls, data, hooks);
        });

        return false;
    }, true, 'after', 'className');

    
    Manager.registerPostprocessor('uses', function(name, cls, data) {
        
        var uses = data.uses;
        if (uses) {
            Loader.addUsedClasses(uses);
        }
    });

    Manager.onCreated(Loader.historyPush);
};



if (Ext._classPathMetadata) {
    Ext.Loader.addClassPathMappings(Ext._classPathMetadata);
    Ext._classPathMetadata = null;
}


(function() {
    var scripts = document.getElementsByTagName('script'),
        currentScript = scripts[scripts.length - 1],
        src = currentScript.src,
        path = src.substring(0, src.lastIndexOf('/') + 1),
        Loader = Ext.Loader;


    Loader.setConfig({
        enabled: true,
        disableCaching:
            true,
        paths: {
            'Ext': path + 'src'
        }
    });
})();



Ext._endTime = new Date().getTime();
if (Ext._beforereadyhandler){
    Ext._beforereadyhandler();
}






Ext.Error = Ext.extend(Error, {
    statics: {
        
        ignore: false,

        
        

        
        raise: function(err){
            err = err || {};
            if (Ext.isString(err)) {
                err = { msg: err };
            }

            var method = this.raise.caller,
                msg;

            if (method) {
                if (method.$name) {
                    err.sourceMethod = method.$name;
                }
                if (method.$owner) {
                    err.sourceClass = method.$owner.$className;
                }
            }

            if (Ext.Error.handle(err) !== true) {
                msg = Ext.Error.prototype.toString.call(err);

                Ext.log({
                    msg: msg,
                    level: 'error',
                    dump: err,
                    stack: true
                });

                throw new Ext.Error(err);
            }
        },

        
        handle: function(){
            return Ext.Error.ignore;
        }
    },

    
    name: 'Ext.Error',

    
    constructor: function(config){
        if (Ext.isString(config)) {
            config = { msg: config };
        }

        var me = this;

        Ext.apply(me, config);

        me.message = me.message || me.msg; 
        
    },

    
    toString: function(){
        var me = this,
            className = me.sourceClass ? me.sourceClass : '',
            methodName = me.sourceMethod ? '.' + me.sourceMethod + '(): ' : '',
            msg = me.msg || '(No description provided)';

        return className + methodName + msg;
    }
});


Ext.deprecated = function (suggestion) {
    return Ext.emptyFn;
};








Ext.JSON = (new(function() {
    var me = this,
    encodingFunction,
    decodingFunction,
    useNative = null,
    useHasOwn = !! {}.hasOwnProperty,
    isNative = function() {
        if (useNative === null) {
            useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
        }
        return useNative;
    },
    pad = function(n) {
        return n < 10 ? "0" + n : n;
    },
    doDecode = function(json) {
        return eval("(" + json + ')');
    },
    doEncode = function(o, newline) {
        
        if (o === null || o === undefined) {
            return "null";
        } else if (Ext.isDate(o)) {
            return Ext.JSON.encodeDate(o);
        } else if (Ext.isString(o)) {
            return Ext.JSON.encodeString(o);
        } else if (typeof o == "number") {
            
            return isFinite(o) ? String(o) : "null";
        } else if (Ext.isBoolean(o)) {
            return String(o);
        }
        
        
        else if (o.toJSON) {
            return o.toJSON();
        } else if (Ext.isArray(o)) {
            return encodeArray(o, newline);
        } else if (Ext.isObject(o)) {
            return encodeObject(o, newline);
        } else if (typeof o === "function") {
            return "null";
        }
        return 'undefined';
    },
    m = {
        "\b": '\\b',
        "\t": '\\t',
        "\n": '\\n',
        "\f": '\\f',
        "\r": '\\r',
        '"': '\\"',
        "\\": '\\\\',
        '\x0b': '\\u000b' 
    },
    charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
    encodeString = function(s) {
        return '"' + s.replace(charToReplace, function(a) {
            var c = m[a];
            return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"';
    },


    encodeArray = function(o, newline) {

        var a = ["[", ""], 
            len = o.length,
            i;
        for (i = 0; i < len; i += 1) {
            a.push(Ext.JSON.encodeValue(o[i]), ',');
        }
        
        a[a.length - 1] = ']';
        return a.join("");
    },

    encodeObject = function(o, newline) {

        var a = ["{", ""], 
            i, val;
        for (i in o) {
            val = o[i];
            if (!useHasOwn || o.hasOwnProperty(i)) {
                
                if (typeof val === 'function' || val === undefined) {
                    continue;
                }
                a.push(Ext.JSON.encodeValue(i), ":", Ext.JSON.encodeValue(val), ',');
                
            }
        }
        
        a[a.length - 1] = '}';
        return a.join("");
    };
    
    
    me.encodeString = encodeString;

    
    me.encodeValue = doEncode;

    
    me.encodeDate = function(o) {
        return '"' + o.getFullYear() + "-"
        + pad(o.getMonth() + 1) + "-"
        + pad(o.getDate()) + "T"
        + pad(o.getHours()) + ":"
        + pad(o.getMinutes()) + ":"
        + pad(o.getSeconds()) + '"';
    };

    
    me.encode = function(o) {
        if (!encodingFunction) {
            
            encodingFunction = isNative() ? JSON.stringify : me.encodeValue;
        }
        return encodingFunction(o);
    };

    
    me.decode = function(json, safe) {
        if (!decodingFunction) {
            
            decodingFunction = isNative() ? JSON.parse : doDecode;
        }
        try {
            return decodingFunction(json);
        } catch (e) {
            if (safe === true) {
                return null;
            }
            Ext.Error.raise({
                sourceClass: "Ext.JSON",
                sourceMethod: "decode",
                msg: "You're trying to decode an invalid JSON String: " + json
            });
        }
    };
})());

Ext.encode = Ext.JSON.encode;

Ext.decode = Ext.JSON.decode;






Ext.apply(Ext, {
    userAgent: navigator.userAgent.toLowerCase(),
    cache: {},
    idSeed: 1000,
    windowId: 'ext-window',
    documentId: 'ext-document',

    
    isReady: false,

    
    enableGarbageCollector: true,

    
    enableListenerCollection: true,

    
    rootHierarchyState: {},

    addCacheEntry: function(id, el, dom) {
        dom = dom || el.dom;


        var cache = Ext.cache,
            key = id || (el && el.id) || dom.id,
            entry = cache[key] || (cache[key] = {
                data: {},
                events: {},

                dom: dom,

                
                skipGarbageCollection: !!(dom.getElementById || dom.navigator)
            });

        if (el) {
            el.$cache = entry;
            
            
            entry.el = el;
        }

        return entry;
    },

    updateCacheEntry: function(cacheItem, dom){
        cacheItem.dom = dom;
        if (cacheItem.el) {
            cacheItem.el.dom = dom;
        }
        return cacheItem;
    },

    
    id: function(el, prefix) {
        var me = this,
            sandboxPrefix = '';
        el = Ext.getDom(el, true) || {};
        if (el === document) {
            el.id = me.documentId;
        }
        else if (el === window) {
            el.id = me.windowId;
        }
        if (!el.id) {
            if (me.isSandboxed) {
                sandboxPrefix = Ext.sandboxName.toLowerCase() + '-';
            }
            el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed);
        }
        return el.id;
    },

    escapeId: (function(){
        var validIdRe = /^[a-zA-Z_][a-zA-Z0-9_\-]*$/i,
            escapeRx = /([\W]{1})/g,
            leadingNumRx = /^(\d)/g,
            escapeFn = function(match, capture){
                return "\\" + capture;
            },
            numEscapeFn = function(match, capture){
                return '\\00' + capture.charCodeAt(0).toString(16) + ' ';
            };

        return function(id) {
            return validIdRe.test(id)
                ? id
                
                
                : id.replace(escapeRx, escapeFn)
                    .replace(leadingNumRx, numEscapeFn);
        };
    }()),

    
    getBody: (function() {
        var body;
        return function() {
            return body || (body = Ext.get(document.body));
        };
    }()),

    
    getHead: (function() {
        var head;
        return function() {
            return head || (head = Ext.get(document.getElementsByTagName("head")[0]));
        };
    }()),

    
    getDoc: (function() {
        var doc;
        return function() {
            return doc || (doc = Ext.get(document));
        };
    }()),

    
    getOrientation: function() {
        return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
    },

    
    destroy: function() {
        var ln = arguments.length,
        i, arg;

        for (i = 0; i < ln; i++) {
            arg = arguments[i];
            if (arg) {
                if (Ext.isArray(arg)) {
                    this.destroy.apply(this, arg);
                } else if (arg.isStore) {
                    arg.destroyStore();
                } else if (Ext.isFunction(arg.destroy)) {
                    arg.destroy();
                } else if (arg.dom) {
                    arg.remove();
                }
            }
        }
    },

    
    callback: function (callback, scope, args, delay) {
        var fn, ret;

        if (Ext.isFunction(callback)){
            fn = callback;
        } else if (scope && Ext.isString(callback)) {
            fn = scope[callback];
        }

        if (fn) {
            args = args || [];
            scope = scope || window;
            if (delay) {
                Ext.defer(fn, delay, scope, args);
            } else {
                ret = fn.apply(scope, args);
            }
        }

        return ret;
    },
    
    
    resolveMethod: function(fn, scope) {
        if (Ext.isFunction(fn)) {
            return fn;
        }
        
        
        return scope[fn];
    },

    
    htmlEncode : function(value) {
        return Ext.String.htmlEncode(value);
    },

    
    htmlDecode : function(value) {
         return Ext.String.htmlDecode(value);
    },

    
    urlAppend : function(url, s) {
        return Ext.String.urlAppend(url, s);
    }
});


Ext.ns = Ext.namespace;


window.undefined = window.undefined;


(function(){

    var check = function(regex){
            return regex.test(Ext.userAgent);
        },
        isStrict = document.compatMode == "CSS1Compat",
        version = function (is, regex) {
            var m;
            return (is && (m = regex.exec(Ext.userAgent))) ? parseFloat(m[1]) : 0;
        },
        docMode = document.documentMode,
        isOpera = check(/opera/),
        isOpera10_5 = isOpera && check(/version\/10\.5/),
        isChrome = check(/\bchrome\b/),
        isWebKit = check(/webkit/),
        isSafari = !isChrome && check(/safari/),
        isSafari2 = isSafari && check(/applewebkit\/4/), 
        isSafari3 = isSafari && check(/version\/3/),
        isSafari4 = isSafari && check(/version\/4/),
        isSafari5_0 = isSafari && check(/version\/5\.0/),
        isSafari5 = isSafari && check(/version\/5/),
        isIE = !isOpera && check(/msie/),
        isIE7 = isIE && ((check(/msie 7/) && docMode != 8 && docMode != 9 && docMode != 10) || docMode == 7),
        isIE8 = isIE && ((check(/msie 8/) && docMode != 7 && docMode != 9 && docMode != 10) || docMode == 8),
        isIE9 = isIE && ((check(/msie 9/) && docMode != 7 && docMode != 8 && docMode != 10) || docMode == 9),
        isIE10 = isIE && ((check(/msie 10/) && docMode != 7 && docMode != 8 && docMode != 9) || docMode == 10),
        isIE6 = isIE && check(/msie 6/),
        isGecko = !isWebKit && check(/gecko/),
        isGecko3 = isGecko && check(/rv:1\.9/),
        isGecko4 = isGecko && check(/rv:2\.0/),
        isGecko5 = isGecko && check(/rv:5\./),
        isGecko10 = isGecko && check(/rv:10\./),
        isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
        isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
        isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
        isWindows = check(/windows|win32/),
        isMac = check(/macintosh|mac os x/),
        isLinux = check(/linux/),
        scrollbarSize = null,
        chromeVersion = version(true, /\bchrome\/(\d+\.\d+)/),
        firefoxVersion = version(true, /\bfirefox\/(\d+\.\d+)/),
        ieVersion = version(isIE, /msie (\d+\.\d+)/),
        operaVersion = version(isOpera, /version\/(\d+\.\d+)/),
        safariVersion = version(isSafari, /version\/(\d+\.\d+)/),
        webKitVersion = version(isWebKit, /webkit\/(\d+\.\d+)/),
        isSecure = /^https/i.test(window.location.protocol),
        nullLog;

    
    try {
        document.execCommand("BackgroundImageCache", false, true);
    } catch(e) {}



    nullLog = function () {};
    nullLog.info = nullLog.warn = nullLog.error = Ext.emptyFn;

    
    Ext.setVersion('extjs', '4.2.1.883');
    Ext.apply(Ext, {
        
        SSL_SECURE_URL : isSecure && isIE ? 'javascript:\'\'' : 'about:blank',

        

        plainTableCls: Ext.buildSettings.baseCSSPrefix + 'table-plain', 

        plainListCls: Ext.buildSettings.baseCSSPrefix + 'list-plain', 

        
        enableNestedListenerRemoval : false,

        
        USE_NATIVE_JSON : false,

        
        getDom : function(el, strict) {
            if (!el || !document) {
                return null;
            }
            if (el.dom) {
                return el.dom;
            } else {
                if (typeof el == 'string') {
                    var e = Ext.getElementById(el);
                    
                    
                    if (e && isIE && strict) {
                        if (el == e.getAttribute('id')) {
                            return e;
                        } else {
                            return null;
                        }
                    }
                    return e;
                } else {
                    return el;
                }
            }
        },

        
        removeNode : isIE6 || isIE7 || isIE8
            ? (function() {
                var d;
                return function(n){
                    if(n && n.tagName.toUpperCase() != 'BODY'){
                        (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);

                        var cache = Ext.cache,
                            id = n.id;

                        if (cache[id]) {
                            delete cache[id].dom;
                            delete cache[id];
                        }

                        if (isIE8 && n.parentNode) {
                            n.parentNode.removeChild(n);
                        }
                        d = d || document.createElement('div');
                        d.appendChild(n);
                        d.innerHTML = '';
                    }
                };
            }())
            : function(n) {
                if (n && n.parentNode && n.tagName.toUpperCase() != 'BODY') {
                    (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);

                    var cache = Ext.cache,
                        id = n.id;

                    if (cache[id]) {
                        delete cache[id].dom;
                        delete cache[id];
                    }

                    n.parentNode.removeChild(n);
                }
            },

        isStrict: isStrict,

        
        isIEQuirks: isIE && (!isStrict && (isIE6 || isIE7 || isIE8 || isIE9)),

        
        isOpera : isOpera,

        
        isOpera10_5 : isOpera10_5,

        
        isWebKit : isWebKit,

        
        isChrome : isChrome,

        
        isSafari : isSafari,

        
        isSafari3 : isSafari3,

        
        isSafari4 : isSafari4,

        
        isSafari5 : isSafari5,

        
        isSafari5_0 : isSafari5_0,


        
        isSafari2 : isSafari2,

        
        isIE : isIE,

        
        isIE6 : isIE6,

        
        isIE7 : isIE7,

        
        isIE7m : isIE6 || isIE7,

        
        isIE7p : isIE && !isIE6,

        
        isIE8 : isIE8,

        
        isIE8m : isIE6 || isIE7 || isIE8,

        
        isIE8p : isIE && !(isIE6 || isIE7),

        
        isIE9 : isIE9,

        
        isIE9m : isIE6 || isIE7 || isIE8 || isIE9,

        
        isIE9p : isIE && !(isIE6 || isIE7 || isIE8),
        
        
        isIE10 : isIE10, 
 
        
        isIE10m : isIE6 || isIE7 || isIE8 || isIE9 || isIE10,
 
        
        isIE10p : isIE && !(isIE6 || isIE7 || isIE8 || isIE9),

        
        isGecko : isGecko,

        
        isGecko3 : isGecko3,

        
        isGecko4 : isGecko4,

        
        isGecko5 : isGecko5,

        
        isGecko10 : isGecko10,

        
        isFF3_0 : isFF3_0,

        
        isFF3_5 : isFF3_5,

        
        isFF3_6 : isFF3_6,

        
        isFF4 : 4 <= firefoxVersion && firefoxVersion < 5,

        
        isFF5 : 5 <= firefoxVersion && firefoxVersion < 6,

        
        isFF10 : 10 <= firefoxVersion && firefoxVersion < 11,

        
        isLinux : isLinux,

        
        isWindows : isWindows,

        
        isMac : isMac,

        
        chromeVersion: chromeVersion,

        
        firefoxVersion: firefoxVersion,

        
        ieVersion: ieVersion,

        
        operaVersion: operaVersion,

        
        safariVersion: safariVersion,

        
        webKitVersion: webKitVersion,

        
        isSecure: isSecure,

        
        BLANK_IMAGE_URL : (isIE6 || isIE7) ? '/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',

        
        value : function(v, defaultValue, allowBlank){
            return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
        },

        
        escapeRe : function(s) {
            return s.replace(/([-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
        },

        
        addBehaviors : function(o){
            if(!Ext.isReady){
                Ext.onReady(function(){
                    Ext.addBehaviors(o);
                });
            } else {
                var cache = {}, 
                    parts,
                    b,
                    s;
                for (b in o) {
                    if ((parts = b.split('@'))[1]) { 
                        s = parts[0];
                        if(!cache[s]){
                            cache[s] = Ext.select(s);
                        }
                        cache[s].on(parts[1], o[b]);
                    }
                }
                cache = null;
            }
        },

        
        getScrollbarSize: function (force) {
            if (!Ext.isReady) {
                return {};
            }

            if (force || !scrollbarSize) {
                var db = document.body,
                    div = document.createElement('div');

                div.style.width = div.style.height = '100px';
                div.style.overflow = 'scroll';
                div.style.position = 'absolute';

                db.appendChild(div); 

                
                scrollbarSize = {
                    width: div.offsetWidth - div.clientWidth,
                    height: div.offsetHeight - div.clientHeight
                };

                db.removeChild(div);
            }

            return scrollbarSize;
        },

        
        getScrollBarWidth: function(force){
            var size = Ext.getScrollbarSize(force);
            return size.width + 2; 
        },

        
        copyTo : function(dest, source, names, usePrototypeKeys){
            if(typeof names == 'string'){
                names = names.split(/[,;\s]/);
            }

            var n,
                nLen = names? names.length : 0,
                name;

            for(n = 0; n < nLen; n++) {
                name = names[n];

                if(usePrototypeKeys || source.hasOwnProperty(name)){
                    dest[name] = source[name];
                }
            }

            return dest;
        },

        
        destroyMembers : function(o){
            for (var i = 1, a = arguments, len = a.length; i < len; i++) {
                Ext.destroy(o[a[i]]);
                delete o[a[i]];
            }
        },

        
        log :
            nullLog,

        
        partition : function(arr, truth){
            var ret = [[],[]],
                a, v,
                aLen = arr.length;

            for (a = 0; a < aLen; a++) {
                v = arr[a];
                ret[ (truth && truth(v, a, arr)) || (!truth && v) ? 0 : 1].push(v);
            }

            return ret;
        },

        
        invoke : function(arr, methodName){
            var ret  = [],
                args = Array.prototype.slice.call(arguments, 2),
                a, v,
                aLen = arr.length;

            for (a = 0; a < aLen; a++) {
                v = arr[a];

                if (v && typeof v[methodName] == 'function') {
                    ret.push(v[methodName].apply(v, args));
                } else {
                    ret.push(undefined);
                }
            }

            return ret;
        },

        
        zip : function(){
            var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
                arrs = parts[0],
                fn = parts[1][0],
                len = Ext.max(Ext.pluck(arrs, "length")),
                ret = [],
                i,
                j,
                aLen;

            for (i = 0; i < len; i++) {
                ret[i] = [];
                if(fn){
                    ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
                }else{
                    for (j = 0, aLen = arrs.length; j < aLen; j++){
                        ret[i].push( arrs[j][i] );
                    }
                }
            }
            return ret;
        },

        
        toSentence: function(items, connector) {
            var length = items.length,
                head,
                tail;

            if (length <= 1) {
                return items[0];
            } else {
                head = items.slice(0, length - 1);
                tail = items[length - 1];

                return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
            }
        },

        
        setGlyphFontFamily: function(fontFamily) {
            Ext._glyphFontFamily = fontFamily;
        },

        
        useShims: isIE6
    });
}());


Ext.application = function(config) {
    var App, paths, ns;
    
    if (typeof config === "string") {
        Ext.require(config, function(){
            App = Ext.ClassManager.get(config);
        });
    }
    else {
        
        
        Ext.Loader.setPath(config.name, config.appFolder || 'app');
        
        if (paths = config.paths) {
            for (ns in paths) {
                if (paths.hasOwnProperty(ns)) {
                    Ext.Loader.setPath(ns, paths[ns]);
                }
            }
        }
        
        config['paths processed'] = true;
        
        
        
        Ext.define(config.name + ".$application", Ext.apply({
                extend: 'Ext.app.Application' 
            }, config),
            
            function () {
                App = this;
            });
    }

    Ext.onReady(function() {
        
        
        Ext.app.Application.instance = new App();
    });
};






(function() {
    Ext.ns('Ext.util');

    var UtilFormat     = Ext.util.Format = {},
        stripTagsRE    = /<\/?[^>]+>/gi,
        stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        nl2brRe        = /\r?\n/g,
        allHashes      = /^#+$/,

        
        formatPattern = /[\d,\.#]+/,

        
        formatCleanRe  = /[^\d\.#]/g,

        
        
        I18NFormatCleanRe,

        
        formatFns = {};

    Ext.apply(UtilFormat, {
        
        
        thousandSeparator: ',',
        

        
        
        decimalSeparator: '.',
        

        
        
        currencyPrecision: 2,
        

         
        
        currencySign: '$',
        

        
        
        currencyAtEnd: false,
        

        
        undef : function(value) {
            return value !== undefined ? value : "";
        },

        
        defaultValue : function(value, defaultValue) {
            return value !== undefined && value !== '' ? value : defaultValue;
        },

        
        substr : 'ab'.substr(-1) != 'b'
        ? function (value, start, length) {
            var str = String(value);
            return (start < 0)
                ? str.substr(Math.max(str.length + start, 0), length)
                : str.substr(start, length);
        }
        : function(value, start, length) {
            return String(value).substr(start, length);
        },

        
        lowercase : function(value) {
            return String(value).toLowerCase();
        },

        
        uppercase : function(value) {
            return String(value).toUpperCase();
        },

        
        usMoney : function(v) {
            return UtilFormat.currency(v, '$', 2);
        },

        
        currency: function(v, currencySign, decimals, end) {
            var negativeSign = '',
                format = ",0",
                i = 0;
            v = v - 0;
            if (v < 0) {
                v = -v;
                negativeSign = '-';
            }
            decimals = Ext.isDefined(decimals) ? decimals : UtilFormat.currencyPrecision;
            format += (decimals > 0 ? '.' : '');
            for (; i < decimals; i++) {
                format += '0';
            }
            v = UtilFormat.number(v, format);
            if ((end || UtilFormat.currencyAtEnd) === true) {
                return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
            } else {
                return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v);
            }
        },

        
        date: function(v, format) {
            if (!v) {
                return "";
            }
            if (!Ext.isDate(v)) {
                v = new Date(Date.parse(v));
            }
            return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
        },

        
        dateRenderer : function(format) {
            return function(v) {
                return UtilFormat.date(v, format);
            };
        },

        
        stripTags : function(v) {
            return !v ? v : String(v).replace(stripTagsRE, "");
        },

        
        stripScripts : function(v) {
            return !v ? v : String(v).replace(stripScriptsRe, "");
        },

        
        fileSize : (function(){
            var byteLimit = 1024,
                kbLimit = 1048576,
                mbLimit = 1073741824;
                
            return function(size) {
                var out;
                if (size < byteLimit) {
                    if (size === 1) {
                        out = '1 byte';    
                    } else {
                        out = size + ' bytes';
                    }
                } else if (size < kbLimit) {
                    out = (Math.round(((size*10) / byteLimit))/10) + ' KB';
                } else if (size < mbLimit) {
                    out = (Math.round(((size*10) / kbLimit))/10) + ' MB';
                } else {
                    out = (Math.round(((size*10) / mbLimit))/10) + ' GB';
                }
                return out;
            };
        })(),

        
        math : (function(){
            var fns = {};

            return function(v, a){
                if (!fns[a]) {
                    fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
                }
                return fns[a](v);
            };
        }()),

        
        round : function(value, precision) {
            var result = Number(value);
            if (typeof precision == 'number') {
                precision = Math.pow(10, precision);
                result = Math.round(value * precision) / precision;
            }
            return result;
        },

        
        number : function(v, formatString) {
            if (!formatString) {
                return v;
            }
            var formatFn = formatFns[formatString];

            
            
            if (!formatFn) {

                var originalFormatString = formatString,
                    comma = UtilFormat.thousandSeparator,
                    decimalSeparator = UtilFormat.decimalSeparator,
                    hasComma,
                    splitFormat,
                    extraChars,
                    precision = 0,
                    multiplier,
                    trimTrailingZeroes,
                    code;

                
                
                
                
                if (formatString.substr(formatString.length - 2) == '/i') {
                    if (!I18NFormatCleanRe) {
                        I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
                    }
                    formatString = formatString.substr(0, formatString.length - 2);
                    hasComma = formatString.indexOf(comma) != -1;
                    splitFormat = formatString.replace(I18NFormatCleanRe, '').split(decimalSeparator);
                } else {
                    hasComma = formatString.indexOf(',') != -1;
                    splitFormat = formatString.replace(formatCleanRe, '').split('.');
                }
                extraChars = formatString.replace(formatPattern, '');

                if (splitFormat.length > 2) {
                } else if (splitFormat.length === 2) {
                    precision = splitFormat[1].length;

                    
                    trimTrailingZeroes = allHashes.test(splitFormat[1]);
                }
                
                
                code = [
                    'var utilFormat=Ext.util.Format,extNumber=Ext.Number,neg,fnum,parts' +
                        (hasComma ? ',thousandSeparator,thousands=[],j,n,i' : '') +
                        (extraChars  ? ',formatString="' + formatString + '",formatPattern=/[\\d,\\.#]+/' : '') +
                        (trimTrailingZeroes ? ',trailingZeroes=/\\.?0+$/;' : ';') +
                    'return function(v){' +
                    'if(typeof v!=="number"&&isNaN(v=extNumber.from(v,NaN)))return"";' +
                    'neg=v<0;',
                    'fnum=Ext.Number.toFixed(Math.abs(v), ' + precision + ');'
                ];

                if (hasComma) {
                    
                    
                    
                    if (precision) {
                        code[code.length] = 'parts=fnum.split(".");';
                        code[code.length] = 'fnum=parts[0];';
                    }
                    code[code.length] =
                        'if(v>=1000) {';
                            code[code.length] = 'thousandSeparator=utilFormat.thousandSeparator;' +
                            'thousands.length=0;' +
                            'j=fnum.length;' +
                            'n=fnum.length%3||3;' +
                            'for(i=0;i<j;i+=n){' +
                                'if(i!==0){' +
                                    'n=3;' +
                                '}' +
                                'thousands[thousands.length]=fnum.substr(i,n);' +
                            '}' +
                            'fnum=thousands.join(thousandSeparator);' + 
                        '}';
                    if (precision) {
                        code[code.length] = 'fnum += utilFormat.decimalSeparator+parts[1];';
                    }
                    
                } else if (precision) {
                    
                    code[code.length] = 'if(utilFormat.decimalSeparator!=="."){' +
                        'parts=fnum.split(".");' +
                        'fnum=parts[0]+utilFormat.decimalSeparator+parts[1];' +
                    '}';
                }

                if (trimTrailingZeroes) {
                    code[code.length] = 'fnum=fnum.replace(trailingZeroes,"");';
                }

                
                code[code.length] = 'if(neg&&fnum!=="' + (precision ? '0.' + Ext.String.repeat('0', precision) : '0') + '")fnum="-"+fnum;';

                code[code.length] = 'return ';

                
                if (extraChars) {
                    code[code.length] = 'formatString.replace(formatPattern, fnum);';
                } else {
                    code[code.length] = 'fnum;';
                }
                code[code.length] = '};'

                formatFn = formatFns[originalFormatString] = Ext.functionFactory('Ext', code.join(''))(Ext);
            }
            return formatFn(v);
        },

        
        numberRenderer : function(format) {
            return function(v) {
                return UtilFormat.number(v, format);
            };
        },

        
        attributes: function(attributes) {
            if (typeof attributes === 'object') {
                var result = [],
                    name;

                for (name in attributes) {
                    result.push(name, '="', name === 'style' ? Ext.DomHelper.generateStyles(attributes[name]) : Ext.htmlEncode(attributes[name]), '"');
                }
                attributes = result.join('');
            }
            return attributes||'';
        },

        
        plural : function(v, s, p) {
            return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
        },

        
        nl2br : function(v) {
            return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
        },

        
        capitalize: Ext.String.capitalize,

        
        ellipsis: Ext.String.ellipsis,

        
        format: Ext.String.format,

        
        htmlDecode: Ext.String.htmlDecode,

        
        htmlEncode: Ext.String.htmlEncode,

        
        leftPad: Ext.String.leftPad,

        
        trim : Ext.String.trim,

        
        parseBox : function(box) {
            box = box || 0;

            if (typeof box === 'number') {
                return {
                    top   : box,
                    right : box,
                    bottom: box,
                    left  : box
                };
             }

            var parts  = box.split(' '),
                ln = parts.length;

            if (ln == 1) {
                parts[1] = parts[2] = parts[3] = parts[0];
            }
            else if (ln == 2) {
                parts[2] = parts[0];
                parts[3] = parts[1];
            }
            else if (ln == 3) {
                parts[3] = parts[1];
            }

            return {
                top   :parseInt(parts[0], 10) || 0,
                right :parseInt(parts[1], 10) || 0,
                bottom:parseInt(parts[2], 10) || 0,
                left  :parseInt(parts[3], 10) || 0
            };
        },

        
        escapeRegex : function(s) {
            return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
        }
    });
}());





Ext.define('Ext.util.TaskRunner', {
    

    
    interval: 10,

    
    timerId: null,

    constructor: function (interval) {
        var me = this;

        if (typeof interval == 'number') {
            me.interval = interval;
        } else if (interval) {
            Ext.apply(me, interval);
        }

        me.tasks = [];
        me.timerFn = Ext.Function.bind(me.onTick, me);
    },

    
    newTask: function (config) {
        var task = new Ext.util.TaskRunner.Task(config);
        task.manager = this;
        return task;
    },

    
    start: function(task) {
        var me = this,
            now = Ext.Date.now();

        if (!task.pending) {
            me.tasks.push(task);
            task.pending = true; 
        }

        task.stopped = false; 
        task.taskStartTime = now;
        task.taskRunTime = task.fireOnStart !== false ? 0 : task.taskStartTime;
        task.taskRunCount = 0;

        if (!me.firing) {
            if (task.fireOnStart !== false) {
                me.startTimer(0, now);
            } else {
                me.startTimer(task.interval, now);
            }
        }

        return task;
    },

    
    stop: function(task) {
        
        
        
        if (!task.stopped) {
            task.stopped = true;

            if (task.onStop) {
                task.onStop.call(task.scope || task, task);
            }
        }

        return task;
    },

    
    stopAll: function() {
        
        Ext.each(this.tasks, this.stop, this);
    },

    

    firing: false,

    nextExpires: 1e99,

    
    onTick: function () {
        var me = this,
            tasks = me.tasks,
            now = Ext.Date.now(),
            nextExpires = 1e99,
            len = tasks.length,
            expires, newTasks, i, task, rt, remove;

        me.timerId = null;
        me.firing = true; 

        
        
        
        
        for (i = 0; i < len || i < (len = tasks.length); ++i) {
            task = tasks[i];

            if (!(remove = task.stopped)) {
                expires = task.taskRunTime + task.interval;

                if (expires <= now) {
                    rt = 1; 
                    try {
                        rt = task.run.apply(task.scope || task, task.args || [++task.taskRunCount]);
                    } catch (taskError) {
                        try {
                            if (task.onError) {
                                rt = task.onError.call(task.scope || task, task, taskError);
                            }
                        } catch (ignore) { }
                        }
                    task.taskRunTime = now;
                    if (rt === false || task.taskRunCount === task.repeat) {
                        me.stop(task);
                        remove = true;
                    } else {
                        remove = task.stopped; 
                        expires = now + task.interval;
                    }
                }

                if (!remove && task.duration && task.duration <= (now - task.taskStartTime)) {
                    me.stop(task);
                    remove = true;
                }
            }

            if (remove) {
                task.pending = false; 

                
                
                
                
                
                if (!newTasks) {
                    newTasks = tasks.slice(0, i);
                    
                    
                    
                }
            } else {
                if (newTasks) {
                    newTasks.push(task); 
                }

                if (nextExpires > expires) {
                    nextExpires = expires; 
                }
            }
        }

        if (newTasks) {
            
            
            me.tasks = newTasks;
        }

        me.firing = false; 

        if (me.tasks.length) {
            
            
            
            me.startTimer(nextExpires - now, Ext.Date.now());
        }
        
        
        if (me.fireIdleEvent !== false) {
            Ext.EventManager.idleEvent.fire();
        }
   },

    
    startTimer: function (timeout, now) {
        var me = this,
            expires = now + timeout,
            timerId = me.timerId;

        
        
        if (timerId && me.nextExpires - expires > me.interval) {
            clearTimeout(timerId);
            timerId = null;
        }

        if (!timerId) {
            if (timeout < me.interval) {
                timeout = me.interval;
            }

            me.timerId = setTimeout(me.timerFn, timeout);
            me.nextExpires = expires;
        }
    }
},
function () {
    var me = this,
        proto = me.prototype;

    
    proto.destroy = proto.stopAll;

    
    Ext.util.TaskManager = Ext.TaskManager = new me();

    
    me.Task = new Ext.Class({
        isTask: true,

        
        stopped: true, 

        
        fireOnStart: false,

        constructor: function (config) {
            Ext.apply(this, config);
        },

        
        restart: function (interval) {
            if (interval !== undefined) {
                this.interval = interval;
            }

            this.manager.start(this);
        },

        
        start: function (interval) {
            if (this.stopped) {
                this.restart(interval);
            }
        },

        
        stop: function () {
            this.manager.stop(this);
        }
    });

    proto = me.Task.prototype;

    
    proto.destroy = proto.stop;
});












Ext.define('Ext.util.TaskManager', {
    extend:  Ext.util.TaskRunner ,

    alternateClassName: [
        'Ext.TaskManager'
    ],

    singleton: true
});





Ext.define('Ext.perf.Accumulator', (function () {
    var currentFrame = null,
        khrome = Ext.global['chrome'],
        formatTpl,
        
        
        getTimestamp = function () {

            getTimestamp = function () {
                return new Date().getTime();
            };
            
            var interval, toolbox;

            
            if (Ext.isChrome && khrome && khrome.Interval) {
                interval = new khrome.Interval();
                interval.start();
                getTimestamp = function () {
                    return interval.microseconds() / 1000;
                };
            } else if (window.ActiveXObject) {
                try {
                    
                    toolbox = new ActiveXObject('SenchaToolbox.Toolbox');
                    Ext.senchaToolbox = toolbox; 
                    getTimestamp = function () {
                        return toolbox.milliseconds;
                    };
                } catch (e) {
                    
                }
            } else if (Date.now) {
                getTimestamp = Date.now;
            }

            Ext.perf.getTimestamp = Ext.perf.Accumulator.getTimestamp = getTimestamp;
            return getTimestamp();
        };

    function adjustSet (set, time) {
        set.sum += time;
        set.min = Math.min(set.min, time);
        set.max = Math.max(set.max, time);
    }

    function leaveFrame (time) {
        var totalTime = time ? time : (getTimestamp() - this.time), 
            me = this, 
            accum = me.accum;

        ++accum.count;
        if (! --accum.depth) {
            adjustSet(accum.total, totalTime);
        }
        adjustSet(accum.pure, totalTime - me.childTime);

        currentFrame = me.parent;
        if (currentFrame) {
            ++currentFrame.accum.childCount;
            currentFrame.childTime += totalTime;
        }
    }

    function makeSet () {
        return {
            min: Number.MAX_VALUE,
            max: 0,
            sum: 0
        };
    }

    function makeTap (me, fn) {
        return function () {
            var frame = me.enter(),
                ret = fn.apply(this, arguments);

            frame.leave();
            return ret;
        };
    }

    function round (x) {
        return Math.round(x * 100) / 100;
    }

    function setToJSON (count, childCount, calibration, set) {
        var data = {
            avg: 0,
            min: set.min,
            max: set.max,
            sum: 0
        };

        if (count) {
            calibration = calibration || 0;
            data.sum = set.sum - childCount * calibration;
            data.avg = data.sum / count;
            
            
        }

        return data;
    }

    return {
        constructor: function (name) {
            var me = this;

            me.count = me.childCount = me.depth = me.maxDepth = 0;
            me.pure = makeSet();
            me.total = makeSet();
            me.name = name;
        },

        statics: {
            getTimestamp: getTimestamp
        },

        format: function (calibration) {
            if (!formatTpl) {
                formatTpl = new Ext.XTemplate([
                        '{name} - {count} call(s)',
                        '<tpl if="count">',
                            '<tpl if="childCount">',
                                ' ({childCount} children)',
                            '</tpl>',
                            '<tpl if="depth - 1">',
                                ' ({depth} deep)',
                            '</tpl>',
                            '<tpl for="times">',
                                ', {type}: {[this.time(values.sum)]} msec (',
                                     
                                     'avg={[this.time(values.sum / parent.count)]}',
                                     
                                     ')',
                            '</tpl>',
                        '</tpl>'
                    ].join(''), {
                        time: function (t) {
                            return Math.round(t * 100) / 100;
                        }
                    });
            }

            var data = this.getData(calibration);
            data.name = this.name;
            data.pure.type = 'Pure';
            data.total.type = 'Total';
            data.times = [data.pure, data.total];
            return formatTpl.apply(data);
        },

        getData: function (calibration) {
            var me = this;

            return {
                count: me.count,
                childCount: me.childCount,
                depth: me.maxDepth,
                pure: setToJSON(me.count, me.childCount, calibration, me.pure),
                total: setToJSON(me.count, me.childCount, calibration, me.total)
            };
        },

        enter: function () {
            var me = this,
                frame = {
                    accum: me,
                    leave: leaveFrame,
                    childTime: 0,
                    parent: currentFrame
                };

            ++me.depth;
            if (me.maxDepth < me.depth) {
                me.maxDepth = me.depth;
            }

            currentFrame = frame;
            frame.time = getTimestamp(); 
            return frame;
        },

        monitor: function (fn, scope, args) {
            var frame = this.enter();
            if (args) {
                fn.apply(scope, args);
            } else {
                fn.call(scope);
            }
            frame.leave();
        },

        report: function () {
            Ext.log(this.format());
        },

        tap: function (className, methodName) {
            var me = this,
                methods = typeof methodName == 'string' ? [methodName] : methodName,
                klass, statik, i, parts, length, name, src,
                tapFunc;

            tapFunc = function(){
                if (typeof className == 'string') {
                    klass = Ext.global;
                    parts = className.split('.');
                    for (i = 0, length = parts.length; i < length; ++i) {
                        klass = klass[parts[i]];
                    }
                } else {
                    klass = className;
                }

                for (i = 0, length = methods.length; i < length; ++i) {
                    name = methods[i];
                    statik = name.charAt(0) == '!';

                    if (statik) {
                        name = name.substring(1);
                    } else {
                        statik = !(name in klass.prototype);
                    }

                    src = statik ? klass : klass.prototype;
                    src[name] = makeTap(me, src[name]);
                }
            };

            Ext.ClassManager.onCreated(tapFunc, me, className);

            return me;
        }
    };
}()),

function () {
    Ext.perf.getTimestamp = this.getTimestamp;
});





Ext.define('Ext.perf.Monitor', {
    singleton: true,
    alternateClassName: 'Ext.Perf',

               
                              
      

    constructor: function () {
        this.accumulators = [];
        this.accumulatorsByName = {};
    },

    calibrate: function () {
        var accum = new Ext.perf.Accumulator('$'),
            total = accum.total,
            getTimestamp = Ext.perf.Accumulator.getTimestamp,
            count = 0,
            frame,
            endTime,
            startTime;

        startTime = getTimestamp();

        do {
            frame = accum.enter();
            frame.leave();
            ++count;
        } while (total.sum < 100);

        endTime = getTimestamp();

        return (endTime - startTime) / count;
    },

    get: function (name) {
        var me = this,
            accum = me.accumulatorsByName[name];

        if (!accum) {
            me.accumulatorsByName[name] = accum = new Ext.perf.Accumulator(name);
            me.accumulators.push(accum);
        }

        return accum;
    },

    enter: function (name) {
        return this.get(name).enter();
    },

    monitor: function (name, fn, scope) {
        this.get(name).monitor(fn, scope);
    },

    report: function () {
        var me = this,
            accumulators = me.accumulators,
            calibration = me.calibrate();

        accumulators.sort(function (a, b) {
            return (a.name < b.name) ? -1 : ((b.name < a.name) ? 1 : 0);
        });

        me.updateGC();

        Ext.log('Calibration: ' + Math.round(calibration * 100) / 100 + ' msec/sample');
        Ext.each(accumulators, function (accum) {
            Ext.log(accum.format(calibration));
        });
    },

    getData: function (all) {
        var ret = {},
            accumulators = this.accumulators;

        Ext.each(accumulators, function (accum) {
            if (all || accum.count) {
                ret[accum.name] = accum.getData();
            }
        });

        return ret;
    },

    reset: function(){
        Ext.each(this.accumulators, function(accum){
            var me = accum;
            me.count = me.childCount = me.depth = me.maxDepth = 0;
            me.pure = {
                min: Number.MAX_VALUE,
                max: 0,
                sum: 0
            };
            me.total = {
                min: Number.MAX_VALUE,
                max: 0,
                sum: 0
            };
        });
    },

    updateGC: function () {
        var accumGC = this.accumulatorsByName.GC,
            toolbox = Ext.senchaToolbox,
            bucket;

        if (accumGC) {
            accumGC.count = toolbox.garbageCollectionCounter || 0;

            if (accumGC.count) {
                bucket = accumGC.pure;
                accumGC.total.sum = bucket.sum = toolbox.garbageCollectionMilliseconds;
                bucket.min = bucket.max = bucket.sum / accumGC.count;
                bucket = accumGC.total;
                bucket.min = bucket.max = bucket.sum / accumGC.count;
            }
        }
    },

    watchGC: function () {
        Ext.perf.getTimestamp(); 

        var toolbox = Ext.senchaToolbox;

        if (toolbox) {
            this.get("GC");
            toolbox.watchGarbageCollector(false); 
        }
    },

    setup: function (config) {
        if (!config) {
            config = {
                
                






                render: {
                    'Ext.AbstractComponent': 'render'
                },

































                layout: {
                    'Ext.layout.Context': 'run'
                }
            };
        }

        this.currentConfig = config;

        var key, prop,
            accum, className, methods;
        for (key in config) {
            if (config.hasOwnProperty(key)) {
                prop = config[key];
                accum = Ext.Perf.get(key);

                for (className in prop) {
                    if (prop.hasOwnProperty(className)) {
                        methods = prop[className];
                        accum.tap(className, methods);
                    }
                }
            }
        }

        this.watchGC();
    }
});






Ext.is = {
    init : function(navigator) {
        var platforms = this.platforms,
            ln = platforms.length,
            i, platform;

        navigator = navigator || window.navigator;

        for (i = 0; i < ln; i++) {
            platform = platforms[i];
            this[platform.identity] = platform.regex.test(navigator[platform.property]);
        }

        
        this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
        
        this.Tablet = this.iPad;
        
        this.Phone = !this.Desktop && !this.Tablet;
        
        this.iOS = this.iPhone || this.iPad || this.iPod;
        
        
        this.Standalone = !!window.navigator.standalone;
    },
    
    
    platforms: [{
        property: 'platform',
        regex: /iPhone/i,
        identity: 'iPhone'
    },
    
    
    {
        property: 'platform',
        regex: /iPod/i,
        identity: 'iPod'
    },
    
    
    {
        property: 'userAgent',
        regex: /iPad/i,
        identity: 'iPad'
    },
    
    
    {
        property: 'userAgent',
        regex: /Blackberry/i,
        identity: 'Blackberry'
    },
    
    
    {
        property: 'userAgent',
        regex: /Android/i,
        identity: 'Android'
    },
    
    
    {
        property: 'platform',
        regex: /Mac/i,
        identity: 'Mac'
    },
    
    
    {
        property: 'platform',
        regex: /Win/i,
        identity: 'Windows'
    },
    
    
    {
        property: 'platform',
        regex: /Linux/i,
        identity: 'Linux'
    }]
};

Ext.is.init();


(function(){

    
    
    
    var getStyle = function(element, styleName){
        var view = element.ownerDocument.defaultView,
            style = (view ? view.getComputedStyle(element, null) : element.currentStyle) || element.style;
        return style[styleName];
    },
    supportsVectors = {
        'IE6-quirks':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0],
        'IE6-strict':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0],
        'IE7-quirks':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0],
        'IE7-strict':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,1,0,0,0],
        'IE8-quirks':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0],
        'IE8-strict':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,0,0,1,0,1,0,0,1],
        'IE9-quirks':  [0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0],
        'IE9-strict':  [0,1,0,0,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1],
        'IE10-quirks': [1,1,0,0,1,1,1,1,0,1,1,1,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,0,0,1],
        'IE10-strict': [1,1,0,0,1,1,1,1,0,1,1,1,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,0,0,1]
    };

function getBrowserKey() {
    var browser = Ext.isIE6 ? 'IE6' : Ext.isIE7 ? 'IE7' : Ext.isIE8 ? 'IE8' :
        Ext.isIE9 ? 'IE9': Ext.isIE10 ? 'IE10' : '';

    return browser ? browser + (Ext.isStrict ? '-strict' : '-quirks') : '';
}

Ext.supports = {
    
    init : function() {
        var me = this,
            doc = document,
            toRun = me.toRun || me.tests,
            n = toRun.length,
            div = n && Ext.isReady && doc.createElement('div'),
            notRun = [],
            browserKey = getBrowserKey(),
            test, vector, value;

        if (div) {
            div.innerHTML = [
                '<div style="height:30px;width:50px;">',
                    '<div style="height:20px;width:20px;"></div>',
                '</div>',
                '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
                    '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
                '</div>',
                '<div style="position: absolute; left: 10%; top: 10%;"></div>',
                '<div style="float:left; background-color:transparent;"></div>'
            ].join('');

            doc.body.appendChild(div);
        }

        vector = supportsVectors[browserKey];
        while (n--) {
            test = toRun[n];
            value = vector && vector[n];
            if (value !== undefined) {
                me[test.identity] = value;
            } else if (div || test.early) {
                me[test.identity] = test.fn.call(me, doc, div);
            } else {
                notRun.push(test);
            }
        }

        if (div) {
            doc.body.removeChild(div);
        }

        me.toRun = notRun;
    },


    
    PointerEvents: 'pointerEvents' in document.documentElement.style,

    
    
    
    LocalStorage: (function() {
        try {
            return 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
            return false;
        }
    })(),

    
    CSS3BoxShadow: 'boxShadow' in document.documentElement.style || 'WebkitBoxShadow' in document.documentElement.style || 'MozBoxShadow' in document.documentElement.style,

    
    ClassList: !!document.documentElement.classList,

    
    OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),

    
    DeviceMotion: ('ondevicemotion' in window),

    
    
    
    Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),

    
    TimeoutActualLateness: (function(){
        setTimeout(function(){
            Ext.supports.TimeoutActualLateness = arguments.length !== 0;
        }, 0);
    }()),

    tests: [
        
        {
            identity: 'Transitions',
            fn: function(doc, div) {
                var prefix = [
                        'webkit',
                        'Moz',
                        'o',
                        'ms',
                        'khtml'
                    ],
                    TE = 'TransitionEnd',
                    transitionEndName = [
                        prefix[0] + TE,
                        'transitionend', 
                        prefix[2] + TE,
                        prefix[3] + TE,
                        prefix[4] + TE
                    ],
                    ln = prefix.length,
                    i = 0,
                    out = false;

                for (; i < ln; i++) {
                    if (getStyle(div, prefix[i] + "TransitionProperty")) {
                        Ext.supports.CSS3Prefix = prefix[i];
                        Ext.supports.CSS3TransitionEnd = transitionEndName[i];
                        out = true;
                        break;
                    }
                }
                return out;
            }
        },

        
        {
            identity: 'RightMargin',
            fn: function(doc, div) {
                var view = doc.defaultView;
                return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
            }
        },

        
        {
            identity: 'DisplayChangeInputSelectionBug',
            early: true,
            fn: function() {
                var webKitVersion = Ext.webKitVersion;
                
                return 0 < webKitVersion && webKitVersion < 533;
            }
        },

        
        {
            identity: 'DisplayChangeTextAreaSelectionBug',
            early: true,
            fn: function() {
                var webKitVersion = Ext.webKitVersion;

                
                return 0 < webKitVersion && webKitVersion < 534.24;
            }
        },

        
        {
            identity: 'TransparentColor',
            fn: function(doc, div, view) {
                view = doc.defaultView;
                return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
            }
        },

        
        {
            identity: 'ComputedStyle',
            fn: function(doc, div, view) {
                view = doc.defaultView;
                return view && view.getComputedStyle;
            }
        },

        
        {
            identity: 'Svg',
            fn: function(doc) {
                return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
            }
        },

        
        {
            identity: 'Canvas',
            fn: function(doc) {
                return !!doc.createElement('canvas').getContext;
            }
        },

        
        {
            identity: 'Vml',
            fn: function(doc) {
                var d = doc.createElement("div");
                d.innerHTML = "<!--[if vml]><br/><br/><![endif]-->";
                return (d.childNodes.length == 2);
            }
        },

        
        {
            identity: 'Float',
            fn: function(doc, div) {
                return !!div.lastChild.style.cssFloat;
            }
        },

        
        {
            identity: 'AudioTag',
            fn: function(doc) {
                return !!doc.createElement('audio').canPlayType;
            }
        },

        
        {
            identity: 'History',
            fn: function() {
                var history = window.history;
                return !!(history && history.pushState);
            }
        },

        
        {
            identity: 'CSS3DTransform',
            fn: function() {
                return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
            }
        },

		
        {
            identity: 'CSS3LinearGradient',
            fn: function(doc, div) {
                var property = 'background-image:',
                    webkit   = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
                    w3c      = 'linear-gradient(left top, black, white)',
                    moz      = '-moz-' + w3c,
                    ms       = '-ms-' + w3c,
                    opera    = '-o-' + w3c,
                    options  = [property + webkit, property + w3c, property + moz, property + ms, property + opera];

                div.style.cssText = options.join(';');

                return (("" + div.style.backgroundImage).indexOf('gradient') !== -1) && !Ext.isIE9;
            }
        },

        
        {
            identity: 'CSS3BorderRadius',
            fn: function(doc, div) {
                var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
                    pass = false,
                    i;
                for (i = 0; i < domPrefixes.length; i++) {
                    if (document.body.style[domPrefixes[i]] !== undefined) {
                        return true;
                    }
                }
                return pass;
            }
        },

        
        {
            identity: 'GeoLocation',
            fn: function() {
                
                return (typeof navigator != 'undefined' && 'geolocation' in navigator) || (typeof google != 'undefined' && typeof google.gears != 'undefined');
            }
        },
        
        {
            identity: 'MouseEnterLeave',
            fn: function(doc, div){
                return ('onmouseenter' in div && 'onmouseleave' in div);
            }
        },
        
        {
            identity: 'MouseWheel',
            fn: function(doc, div) {
                return ('onmousewheel' in div);
            }
        },
        
        {
            identity: 'Opacity',
            fn: function(doc, div){
                
                if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
                    return false;
                }
                div.firstChild.style.cssText = 'opacity:0.73';
                return div.firstChild.style.opacity == '0.73';
            }
        },
        
        {
            identity: 'Placeholder',
            fn: function(doc) {
                return 'placeholder' in doc.createElement('input');
            }
        },

        
        {
            identity: 'Direct2DBug',
            fn: function() {
                return Ext.isString(document.body.style.msTransformOrigin) && Ext.isIE10m;
            }
        },
        
        {
            identity: 'BoundingClientRect',
            fn: function(doc, div) {
                return Ext.isFunction(div.getBoundingClientRect);
            }
        },
        
        {
            identity: 'RotatedBoundingClientRect',
            fn: function() {
                var body = document.body,
                    supports = false,
                    el = document.createElement('div'),
                    style = el.style;

                if (el.getBoundingClientRect) {
                    style.WebkitTransform = style.MozTransform =
                        style.OTransform = style.transform = 'rotate(90deg)';
                    style.width = '100px';
                    style.height = '30px';
                    body.appendChild(el)

                    supports = el.getBoundingClientRect().height !== 100;
                    body.removeChild(el);
                }
               
                return supports;
            }
        },
        {
            identity: 'IncludePaddingInWidthCalculation',
            fn: function(doc, div){
                return div.childNodes[1].firstChild.offsetWidth == 210;
            }
        },
        {
            identity: 'IncludePaddingInHeightCalculation',
            fn: function(doc, div){
                return div.childNodes[1].firstChild.offsetHeight == 210;
            }
        },

        
        {
            identity: 'ArraySort',
            fn: function() {
                var a = [1,2,3,4,5].sort(function(){ return 0; });
                return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
            }
        },
        
        {
            identity: 'Range',
            fn: function() {
                return !!document.createRange;
            }
        },
        
        {
            identity: 'CreateContextualFragment',
            fn: function() {
                var range = Ext.supports.Range ? document.createRange() : false;

                return range && !!range.createContextualFragment;
            }
        },

        
        {
            identity: 'WindowOnError',
            fn: function () {
                
                return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; 
            }
        },

        
        {
            identity: 'TextAreaMaxLength',
            fn: function(){
                var el = document.createElement('textarea');
                return ('maxlength' in el);
            }
        },
        
        
        {
            identity: 'GetPositionPercentage',
            fn: function(doc, div){
               return getStyle(div.childNodes[2], 'left') == '10%';
            }
        },
        
        {
            identity: 'PercentageHeightOverflowBug',
            fn: function(doc) {
                var hasBug = false,
                    style, el;

                if (Ext.getScrollbarSize().height) {
                    
                    el = doc.createElement('div');
                    style = el.style;
                    style.height = '50px';
                    style.width = '50px';
                    style.overflow = 'auto';
                    style.position = 'absolute';
                    
                    el.innerHTML = [
                        '<div style="display:table;height:100%;">',
                            
                            
                            
                            '<div style="width:51px;"></div>',
                        '</div>'
                    ].join('');
                    doc.body.appendChild(el);
                    if (el.firstChild.offsetHeight === 50) {
                        hasBug = true;
                    }
                    doc.body.removeChild(el);
                }
                
                return hasBug;
            }
        },
        
        {
            identity: 'xOriginBug',
            fn: function(doc, div) {
               div.innerHTML = '<div id="b1" style="height:100px;width:100px;direction:rtl;position:relative;overflow:scroll">' +
                    '<div id="b2" style="position:relative;width:100%;height:20px;"></div>' +
                    '<div id="b3" style="position:absolute;width:20px;height:20px;top:0px;right:0px"></div>' +
                '</div>';

                var outerBox = document.getElementById('b1').getBoundingClientRect(),
                    b2 = document.getElementById('b2').getBoundingClientRect(),
                    b3 = document.getElementById('b3').getBoundingClientRect();

                return (b2.left !== outerBox.left && b3.right !== outerBox.right);
            }
        },

        
        {
            identity: 'ScrollWidthInlinePaddingBug',
            fn: function(doc) {
                var hasBug = false,
                    style, el;

                el = doc.createElement('div');
                style = el.style;
                style.height = '50px';
                style.width = '50px';
                style.padding = '10px';
                style.overflow = 'hidden';
                style.position = 'absolute';
                
                el.innerHTML =
                    '<span style="display:inline-block;zoom:1;height:60px;width:60px;"></span>';
                doc.body.appendChild(el);
                if (el.scrollWidth === 70) {
                    hasBug = true;
                }
                doc.body.removeChild(el);
                
                return hasBug;
            }
        }
    ]
};
}());

Ext.supports.init(); 






Ext.util.DelayedTask = function(fn, scope, args, cancelOnDelay) {
    var me = this,
        delay,
        call = function() {
            clearInterval(me.id);
            me.id = null;
            fn.apply(scope, args || []);
            Ext.EventManager.idleEvent.fire();
        };

    cancelOnDelay = typeof cancelOnDelay === 'boolean' ? cancelOnDelay : true;

    
    me.id = null;

    
    me.delay = function(newDelay, newFn, newScope, newArgs) {
        if (cancelOnDelay) {
            me.cancel();
        }
        delay = newDelay || delay,
        fn    = newFn    || fn;
        scope = newScope || scope;
        args  = newArgs  || args;
        if (!me.id) {
            me.id = setInterval(call, delay);
        }
    };

    
    me.cancel = function() {
        if (me.id) {
            clearInterval(me.id);
            me.id = null;
        }
    };
};



Ext.define('Ext.util.Event', function() {
  var arraySlice = Array.prototype.slice,
      arrayInsert = Ext.Array.insert,
      toArray = Ext.Array.toArray,
      DelayedTask = Ext.util.DelayedTask;

  return {
                                     

    
    isEvent: true,
    
    
    suspended: 0,

    noOptions: {},

    constructor: function(observable, name) {
        this.name = name;
        this.observable = observable;
        this.listeners = [];
    },

    addListener: function(fn, scope, options) {
        var me = this,
            listeners, listener, priority, isNegativePriority, highestNegativePriorityIndex,
            hasNegativePriorityIndex, length, index, i, listenerPriority;

        scope = scope || me.observable;


        if (!me.isListening(fn, scope)) {
            listener = me.createListener(fn, scope, options);
            if (me.firing) {
                
                me.listeners = me.listeners.slice(0);
            }
            listeners = me.listeners;
            index = length = listeners.length;
            priority = options && options.priority;
            highestNegativePriorityIndex = me._highestNegativePriorityIndex;
            hasNegativePriorityIndex = (highestNegativePriorityIndex !== undefined);
            if (priority) {
                
                
                isNegativePriority = (priority < 0);
                if (!isNegativePriority || hasNegativePriorityIndex) {
                    
                    
                    
                    
                    
                    
                    for(i = (isNegativePriority ? highestNegativePriorityIndex : 0); i < length; i++) {
                        
                        listenerPriority = listeners[i].o ? listeners[i].o.priority||0 : 0;
                        if (listenerPriority < priority) {
                            index = i;
                            break;
                        }
                    }
                } else {
                    
                    
                    
                    me._highestNegativePriorityIndex = index;
                }
            } else if (hasNegativePriorityIndex) {
                
                
                
                
                index = highestNegativePriorityIndex;
            }

            if (!isNegativePriority && index <= highestNegativePriorityIndex) {
                me._highestNegativePriorityIndex ++;
            }
            if (index === length) {
                me.listeners[length] = listener;
            } else {
                arrayInsert(me.listeners, index, [listener]);
            }
        }
    },

    createListener: function(fn, scope, o) {
        scope = scope || this.observable;

        var me = this,
            listener = {
                fn: fn,
                scope: scope,
                ev: me
            },
            handler = fn;

        
        
        if (o) {
            listener.o = o;
            if (o.single) {
                handler = me.createSingle(handler, listener, o, scope);
            }
            if (o.target) {
                handler = me.createTargeted(handler, listener, o, scope);
            }
            if (o.delay) {
                handler = me.createDelayed(handler, listener, o, scope);
            }
            if (o.buffer) {
                handler = me.createBuffered(handler, listener, o, scope);
            }
        }

        listener.fireFn = handler;
        return listener;
    },

    findListener: function(fn, scope) {
        var listeners = this.listeners,
        i = listeners.length,
        listener,
        s;

        while (i--) {
            listener = listeners[i];
            if (listener) {
                s = listener.scope;

                
                
                
                if (listener.fn == fn && (s == (scope || this.observable))) {
                    return i;
                }
            }
        }

        return - 1;
    },

    isListening: function(fn, scope) {
        return this.findListener(fn, scope) !== -1;
    },

    removeListener: function(fn, scope) {
        var me = this,
            index,
            listener,
            highestNegativePriorityIndex,
            k;
        index = me.findListener(fn, scope);
        if (index != -1) {
            listener = me.listeners[index];
            highestNegativePriorityIndex = me._highestNegativePriorityIndex;

            if (me.firing) {
                me.listeners = me.listeners.slice(0);
            }

            
            if (listener.task) {
                listener.task.cancel();
                delete listener.task;
            }

            
            k = listener.tasks && listener.tasks.length;
            if (k) {
                while (k--) {
                    listener.tasks[k].cancel();
                }
                delete listener.tasks;
            }

            
            
            
            me.listeners.splice(index, 1);

            
            
            if (highestNegativePriorityIndex) {
                if (index < highestNegativePriorityIndex) {
                    me._highestNegativePriorityIndex --;
                } else if (index === highestNegativePriorityIndex && index === me.listeners.length) {
                    delete me._highestNegativePriorityIndex;
                }
            }
            return true;
        }

        return false;
    },

    
    clearListeners: function() {
        var listeners = this.listeners,
            i = listeners.length;

        while (i--) {
            this.removeListener(listeners[i].fn, listeners[i].scope);
        }
    },

    suspend: function() {
        this.suspended += 1;
    },

    resume: function() {
        if (this.suspended) {
            this.suspended--;
        }
    },

    fire: function() {
        var me = this,
            listeners = me.listeners,
            count = listeners.length,
            i,
            args,
            listener,
            len;

        if (!me.suspended && count > 0) {
            me.firing = true;
            args = arguments.length ? arraySlice.call(arguments, 0) : []
            len = args.length;
            for (i = 0; i < count; i++) {
                listener = listeners[i];
                if (listener.o) {
                    args[len] = listener.o;
                }
                if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
                    return (me.firing = false);
                }
            }
        }
        me.firing = false;
        return true;
    },

    createTargeted: function (handler, listener, o, scope) {
        return function(){
            if (o.target === arguments[0]){
                handler.apply(scope, arguments);
            }
        };
    },

    createBuffered: function (handler, listener, o, scope) {
        listener.task = new DelayedTask();
        return function() {
            listener.task.delay(o.buffer, handler, scope, toArray(arguments));
        };
    },

    createDelayed: function (handler, listener, o, scope) {
        return function() {
            var task = new DelayedTask();
            if (!listener.tasks) {
                listener.tasks = [];
            }
            listener.tasks.push(task);
            task.delay(o.delay || 10, handler, scope, toArray(arguments));
        };
    },

    createSingle: function (handler, listener, o, scope) {
        return function() {
            var event = listener.ev;

            if (event.removeListener(listener.fn, scope) && event.observable) {
                
                
                event.observable.hasListeners[event.name]--;
            }

            return handler.apply(scope, arguments);
        };
    }
  };
});






Ext.EventManager = new function() {
    var EventManager = this,
        doc = document,
        win = window,
        escapeRx = /\\/g,
        prefix = Ext.baseCSSPrefix,
        
        supportsAddEventListener = !Ext.isIE9 && 'addEventListener' in doc,
        readyEvent,
        initExtCss = function() {
            
            var bd = doc.body || doc.getElementsByTagName('body')[0],
                cls = [prefix + 'body'],
                htmlCls = [],
                supportsLG = Ext.supports.CSS3LinearGradient,
                supportsBR = Ext.supports.CSS3BorderRadius,
                html;

            if (!bd) {
                return false;
            }

            html = bd.parentNode;

            function add (c) {
                cls.push(prefix + c);
            }

            
            if (Ext.isIE && Ext.isIE9m) {
                add('ie');

                
                
                
                
                
                
                
                
                
                
                
                if (Ext.isIE6) {
                    add('ie6');
                } else { 
                    add('ie7p');

                    if (Ext.isIE7) {
                        add('ie7');
                    } else {
                        add('ie8p');

                        if (Ext.isIE8) {
                            add('ie8');
                        } else {
                            add('ie9p');

                            if (Ext.isIE9) {
                                add('ie9');
                            }
                        }
                    }
                }

                if (Ext.isIE7m) {
                    add('ie7m');
                }
                if (Ext.isIE8m) {
                    add('ie8m');
                }
                if (Ext.isIE9m) {
                    add('ie9m');
                }
                if (Ext.isIE7 || Ext.isIE8) {
                    add('ie78');
                }
            }
            
            if (Ext.isIE10) {
                add('ie10');
            }
            
            if (Ext.isGecko) {
                add('gecko');
                if (Ext.isGecko3) {
                    add('gecko3');
                }
                if (Ext.isGecko4) {
                    add('gecko4');
                }
                if (Ext.isGecko5) {
                    add('gecko5');
                }
            }
            if (Ext.isOpera) {
                add('opera');
            }
            if (Ext.isWebKit) {
                add('webkit');
            }
            if (Ext.isSafari) {
                add('safari');
                if (Ext.isSafari2) {
                    add('safari2');
                }
                if (Ext.isSafari3) {
                    add('safari3');
                }
                if (Ext.isSafari4) {
                    add('safari4');
                }
                if (Ext.isSafari5) {
                    add('safari5');
                }
                if (Ext.isSafari5_0) {
                    add('safari5_0')
                }
            }
            if (Ext.isChrome) {
                add('chrome');
            }
            if (Ext.isMac) {
                add('mac');
            }
            if (Ext.isLinux) {
                add('linux');
            }
            if (!supportsBR) {
                add('nbr');
            }
            if (!supportsLG) {
                add('nlg');
            }

            
            if (html) {
                if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
                    Ext.isBorderBox = false;
                }
                else {
                    Ext.isBorderBox = true;
                }

                if(!Ext.isBorderBox) {
                    htmlCls.push(prefix + 'content-box');
                }
                if (Ext.isStrict) {
                    htmlCls.push(prefix + 'strict');
                } else {
                    htmlCls.push(prefix + 'quirks');
                }
                Ext.fly(html, '_internal').addCls(htmlCls);
            }

            Ext.fly(bd, '_internal').addCls(cls);
            return true;
        };

    Ext.apply(EventManager, {
        
        hasBoundOnReady: false,

        
        hasFiredReady: false,

        
        deferReadyEvent : 1,

        
        onReadyChain : [],

        
        readyEvent:
            (function () {
                readyEvent = new Ext.util.Event();
                readyEvent.fire = function () {
                    Ext._beforeReadyTime = Ext._beforeReadyTime || new Date().getTime();
                    readyEvent.self.prototype.fire.apply(readyEvent, arguments);
                    Ext._afterReadytime = new Date().getTime();
                };
                return readyEvent;
            }()),

        
        idleEvent: new Ext.util.Event(),

        
        isReadyPaused: function(){
            return (/[?&]ext-pauseReadyFire\b/i.test(location.search) && !Ext._continueFireReady);
        },

        
        bindReadyEvent: function() {
            if (EventManager.hasBoundOnReady) {
                return;
            }

            
            if ( doc.readyState == 'complete'  ) {  
                EventManager.onReadyEvent({
                    type: doc.readyState || 'body'
                });
            } else {
                doc.addEventListener('DOMContentLoaded', EventManager.onReadyEvent, false);
                win.addEventListener('load', EventManager.onReadyEvent, false);
                EventManager.hasBoundOnReady = true;
            }
        },

        onReadyEvent : function(e) {
            if (e && e.type) {
                EventManager.onReadyChain.push(e.type);
            }

            if (EventManager.hasBoundOnReady) {
                doc.removeEventListener('DOMContentLoaded', EventManager.onReadyEvent, false);
                win.removeEventListener('load', EventManager.onReadyEvent, false);
            }

            if (!Ext.isReady) {
                EventManager.fireDocReady();
            }
        },

        
        fireDocReady: function() {
            if (!Ext.isReady) {
                Ext._readyTime = new Date().getTime();
                Ext.isReady = true;

                Ext.supports.init();
                EventManager.onWindowUnload();
                readyEvent.onReadyChain = EventManager.onReadyChain;    

                if (Ext.isNumber(EventManager.deferReadyEvent)) {
                    Ext.Function.defer(EventManager.fireReadyEvent, EventManager.deferReadyEvent);
                    EventManager.hasDocReadyTimer = true;
                } else {
                    EventManager.fireReadyEvent();
                }
            }
        },

        
        fireReadyEvent: function() {

            
            
            EventManager.hasDocReadyTimer = false;
            EventManager.isFiring = true;

            
            
            
            while (readyEvent.listeners.length && !EventManager.isReadyPaused()) {
                readyEvent.fire();
            }
            EventManager.isFiring = false;
            EventManager.hasFiredReady = true;
            Ext.EventManager.idleEvent.fire();
        },

        
        onDocumentReady: function(fn, scope, options) {
            options = options || {};
            
            options.single = true;
            readyEvent.addListener(fn, scope, options);

            
            
            if (!(EventManager.isFiring || EventManager.hasDocReadyTimer)) {
                if (Ext.isReady) {
                    EventManager.fireReadyEvent();
                } else {
                    EventManager.bindReadyEvent();
                }
            }
        },

        

        
        stoppedMouseDownEvent: new Ext.util.Event(),

        
        propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,

        
        getId : function(element) {
            var id;

            element = Ext.getDom(element);

            if (element === doc || element === win) {
                id = element === doc ? Ext.documentId : Ext.windowId;
            }
            else {
                id = Ext.id(element);
            }

            if (!Ext.cache[id]) {
                Ext.addCacheEntry(id, null, element);
            }

            return id;
        },

        
        prepareListenerConfig: function(element, config, isRemove) {
            var propRe = EventManager.propRe,
                key, value, args;

            
            for (key in config) {
                if (config.hasOwnProperty(key)) {
                    
                    if (!propRe.test(key)) {
                        value = config[key];
                        
                        
                        if (typeof value == 'function') {
                            
                            args = [element, key, value, config.scope, config];
                        } else {
                            
                            args = [element, key, value.fn, value.scope, value];
                        }

                        if (isRemove) {
                            EventManager.removeListener.apply(EventManager, args);
                        } else {
                            EventManager.addListener.apply(EventManager, args);
                        }
                    }
                }
            }
        },

        mouseEnterLeaveRe: /mouseenter|mouseleave/,

        
        normalizeEvent: function(eventName, fn) {
            if (EventManager.mouseEnterLeaveRe.test(eventName) && !Ext.supports.MouseEnterLeave) {
                if (fn) {
                    fn = Ext.Function.createInterceptor(fn, EventManager.contains);
                }
                eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
            } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera) {
                eventName = 'DOMMouseScroll';
            }
            return {
                eventName: eventName,
                fn: fn
            };
        },

        
        contains: function(event) {
            event = event.browserEvent || event;
            var parent = event.currentTarget,
                child = EventManager.getRelatedTarget(event);

            if (parent && parent.firstChild) {
                while (child) {
                    if (child === parent) {
                        return false;
                    }
                    child = child.parentNode;
                    if (child && (child.nodeType != 1)) {
                        child = null;
                    }
                }
            }
            return true;
        },

        
        addListener: function(element, eventName, fn, scope, options) {
            
            if (typeof eventName !== 'string') {
                EventManager.prepareListenerConfig(element, eventName);
                return;
            }

            var dom = element.dom || Ext.getDom(element),
                hasAddEventListener, bind, wrap, cache, id, cacheItem, capture;
            
            if (typeof fn === 'string') {
                fn = Ext.resolveMethod(fn, scope || element);
            }


            
            options = options || {};

            bind = EventManager.normalizeEvent(eventName, fn);
            wrap = EventManager.createListenerWrap(dom, eventName, bind.fn, scope, options);
            
            
            cache = EventManager.getEventListenerCache(element.dom ? element : dom, eventName);
            eventName = bind.eventName;

            
            hasAddEventListener = supportsAddEventListener || (Ext.isIE9 && !dom.attachEvent);
            
            if (!hasAddEventListener) {
                id = EventManager.normalizeId(dom);
                
                
                if (id) {
                    cacheItem = Ext.cache[id][eventName];
                    if (cacheItem && cacheItem.firing) {
                        
                        
                        
                        
                        
                        
                        cache = EventManager.cloneEventListenerCache(dom, eventName);
                    }
                }
            }

            capture = !!options.capture;
            cache.push({
                fn: fn,
                wrap: wrap,
                scope: scope,
                capture: capture 
            });

            if (!hasAddEventListener) {
                
                
                if (cache.length === 1) {
                    id = EventManager.normalizeId(dom, true);
                    fn = Ext.Function.bind(EventManager.handleSingleEvent, EventManager, [id, eventName], true);
                    Ext.cache[id][eventName] = {
                        firing: false,
                        fn: fn
                    };
                    dom.attachEvent('on' + eventName, fn);
                }
            } else {
                dom.addEventListener(eventName, wrap, capture);
            }

            if (dom == doc && eventName == 'mousedown') {
                EventManager.stoppedMouseDownEvent.addListener(wrap);
            }
        },
        
        
        
        normalizeId: function(dom, force) {
            var id;
            if (dom === document) {
                id = Ext.documentId;
            } else if (dom === window) {
                id = Ext.windowId;
            } else {
                id = dom.id;
            }
            if (!id && force) {
                id = EventManager.getId(dom);
            }
            return id;
        },
        
        handleSingleEvent: function(e, id, eventName) {
            
            
            
            var listenerCache = EventManager.getEventListenerCache(id, eventName),
                attachItem = Ext.cache[id][eventName],
                len, i;
                
            
            
            
            if (attachItem.firing) {
                return;
            }
                
            attachItem.firing = true;
            for (i = 0, len = listenerCache.length; i < len; ++i) {
                listenerCache[i].wrap(e);
            }
            attachItem.firing = false;
            
        },

        
        removeListener : function(element, eventName, fn, scope) {
            
            if (typeof eventName !== 'string') {
                EventManager.prepareListenerConfig(element, eventName, true);
                return;
            }

            var dom = Ext.getDom(element),
                id, el = element.dom ? element : Ext.get(dom),
                cache = EventManager.getEventListenerCache(el, eventName),
                bindName = EventManager.normalizeEvent(eventName).eventName,
                i = cache.length, j, cacheItem, hasRemoveEventListener,
                listener, wrap;
                
            if (!dom) {
                return;
            }

            
            hasRemoveEventListener = supportsAddEventListener || (Ext.isIE9 && !dom.detachEvent);
            
            if (typeof fn === 'string') {
                fn = Ext.resolveMethod(fn, scope || element);
            }

            while (i--) {
                listener = cache[i];

                if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
                    wrap = listener.wrap;

                    
                    if (wrap.task) {
                        clearTimeout(wrap.task);
                        delete wrap.task;
                    }

                    
                    j = wrap.tasks && wrap.tasks.length;
                    if (j) {
                        while (j--) {
                            clearTimeout(wrap.tasks[j]);
                        }
                        delete wrap.tasks;
                    }

                    if (!hasRemoveEventListener) {
                        
                        
                        id = EventManager.normalizeId(dom, true);
                        cacheItem = Ext.cache[id][bindName];
                        if (cacheItem && cacheItem.firing) {
                            
                            cache = EventManager.cloneEventListenerCache(dom, bindName);
                        }
                        
                        if (cache.length === 1) {
                            fn = cacheItem.fn;
                            delete Ext.cache[id][bindName];
                            dom.detachEvent('on' + bindName, fn);
                        }
                    } else {
                        dom.removeEventListener(bindName, wrap, listener.capture);
                    }

                    if (wrap && dom == doc && eventName == 'mousedown') {
                        EventManager.stoppedMouseDownEvent.removeListener(wrap);
                    }

                    
                    Ext.Array.erase(cache, i, 1);
                }
            }
        },

        
        removeAll : function(element) {
            var id = (typeof element === 'string') ? element : element.id,
                cache, events, eventName;

            
            if (id && (cache = Ext.cache[id])) {
                events = cache.events;
    
                for (eventName in events) {
                    if (events.hasOwnProperty(eventName)) {
                        EventManager.removeListener(element, eventName);
                    }
                }
                cache.events = {};
             }
        },

        
        purgeElement : function(element, eventName) {
            var dom = Ext.getDom(element),
                i = 0, len, childNodes;

            if (eventName) {
                EventManager.removeListener(element, eventName);
            } else {
                EventManager.removeAll(element);
            }

            if (dom && dom.childNodes) {
                childNodes = dom.childNodes;
                for (len = childNodes.length; i < len; i++) {
                    EventManager.purgeElement(childNodes[i], eventName);
                }
            }
        },

        
        createListenerWrap : function(dom, ename, fn, scope, options) {
            options = options || {};

            var f, gen, wrap = function(e, args) {
                
                if (!gen) {
                    f = ['if(!' + Ext.name + ') {return;}'];

                    if (options.buffer || options.delay || options.freezeEvent) {
                        if (options.freezeEvent) {
                            
                            
                            f.push('e = X.EventObject.setEvent(e);');
                        }
                        f.push('e = new X.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
                    } else {
                        f.push('e = X.EventObject.setEvent(e);');
                    }

                    if (options.delegate) {
                        
                        
                        f.push('var result, t = e.getTarget("' + (options.delegate + '').replace(escapeRx, '\\\\') + '", this);');
                        f.push('if(!t) {return;}');
                    } else {
                        f.push('var t = e.target, result;');
                    }

                    if (options.target) {
                        f.push('if(e.target !== options.target) {return;}');
                    }

                    if (options.stopEvent) {
                        f.push('e.stopEvent();');
                    } else {
                        if(options.preventDefault) {
                            f.push('e.preventDefault();');
                        }
                        if(options.stopPropagation) {
                            f.push('e.stopPropagation();');
                        }
                    }

                    if (options.normalized === false) {
                        f.push('e = e.browserEvent;');
                    }

                    if (options.buffer) {
                        f.push('(wrap.task && clearTimeout(wrap.task));');
                        f.push('wrap.task = setTimeout(function() {');
                    }

                    if (options.delay) {
                        f.push('wrap.tasks = wrap.tasks || [];');
                        f.push('wrap.tasks.push(setTimeout(function() {');
                    }

                    
                    f.push('result = fn.call(scope || dom, e, t, options);');

                    if (options.single) {
                        f.push('evtMgr.removeListener(dom, ename, fn, scope);');
                    }

                    
                    
                    if (ename !== 'mousemove' && ename !== 'unload') {
                        f.push('if (evtMgr.idleEvent.listeners.length) {');
                        f.push('evtMgr.idleEvent.fire();');
                        f.push('}');
                    }

                    if (options.delay) {
                        f.push('}, ' + options.delay + '));');
                    }

                    if (options.buffer) {
                        f.push('}, ' + options.buffer + ');');
                    }
                    f.push('return result;');

                    gen = Ext.cacheableFunctionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', 'X', 'evtMgr', f.join('\n'));
                }

                return gen.call(dom, e, options, fn, scope, ename, dom, wrap, args, Ext, EventManager);
            };
            return wrap;
        },
        
        
        getEventCache: function(element) {
            var elementCache, eventCache, id;
            
            if (!element) {
                return [];
            }

            if (element.$cache) {
                elementCache = element.$cache;
            } else {
                
                if (typeof element === 'string') {
                    id = element;
                } else {
                    id = EventManager.getId(element);
                }
                elementCache = Ext.cache[id];
            }
            eventCache = elementCache.events || (elementCache.events = {});
            return eventCache;
        },

        
        getEventListenerCache : function(element, eventName) {
            var eventCache = EventManager.getEventCache(element);
            return eventCache[eventName] || (eventCache[eventName] = []);
        },
        
        
        cloneEventListenerCache: function(element, eventName){
            var eventCache = EventManager.getEventCache(element),
                out;
                
            if (eventCache[eventName]) {
                out = eventCache[eventName].slice(0);
            } else {
                out = [];
            }
            eventCache[eventName] = out;
            return out;
        },

        
        mouseLeaveRe: /(mouseout|mouseleave)/,
        mouseEnterRe: /(mouseover|mouseenter)/,

        
        stopEvent: function(event) {
            EventManager.stopPropagation(event);
            EventManager.preventDefault(event);
        },

        
        stopPropagation: function(event) {
            event = event.browserEvent || event;
            if (event.stopPropagation) {
                event.stopPropagation();
            } else {
                event.cancelBubble = true;
            }
        },

        
        preventDefault: function(event) {
            event = event.browserEvent || event;
            if (event.preventDefault) {
                event.preventDefault();
            } else {
                event.returnValue = false;
                
                try {
                  
                  if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
                      event.keyCode = -1;
                  }
                } catch (e) {
                    
                }
            }
        },

        
        getRelatedTarget: function(event) {
            event = event.browserEvent || event;
            var target = event.relatedTarget;
            if (!target) {
                if (EventManager.mouseLeaveRe.test(event.type)) {
                    target = event.toElement;
                } else if (EventManager.mouseEnterRe.test(event.type)) {
                    target = event.fromElement;
                }
            }
            return EventManager.resolveTextNode(target);
        },

        
        getPageX: function(event) {
            return EventManager.getPageXY(event)[0];
        },

        
        getPageY: function(event) {
            return EventManager.getPageXY(event)[1];
        },

        
        getPageXY: function(event) {
            event = event.browserEvent || event;
            var x = event.pageX,
                y = event.pageY,
                docEl = doc.documentElement,
                body = doc.body;

            
            if (!x && x !== 0) {
                x = event.clientX + (docEl && docEl.scrollLeft || body && body.scrollLeft || 0) - (docEl && docEl.clientLeft || body && body.clientLeft || 0);
                y = event.clientY + (docEl && docEl.scrollTop  || body && body.scrollTop  || 0) - (docEl && docEl.clientTop  || body && body.clientTop  || 0);
            }
            return [x, y];
        },

        
        getTarget: function(event) {
            event = event.browserEvent || event;
            return EventManager.resolveTextNode(event.target || event.srcElement);
        },

        
        
        
        
        resolveTextNode: Ext.isGecko ?
            function(node) {
                if (node) {
                    
                    var s = HTMLElement.prototype.toString.call(node);
                    if (s !== '[xpconnect wrapped native prototype]' && s !== '[object XULElement]') {
                        return node.nodeType == 3 ? node.parentNode: node;
                    }
                }
            }
            :
            function(node) {
                return node && node.nodeType == 3 ? node.parentNode: node;
            },

        

        
        curWidth: 0,
        curHeight: 0,

        
        onWindowResize: function(fn, scope, options) {
            var resize = EventManager.resizeEvent;

            if (!resize) {
                EventManager.resizeEvent = resize = new Ext.util.Event();
                EventManager.on(win, 'resize', EventManager.fireResize, null, {buffer: 100});
            }
            resize.addListener(fn, scope, options);
        },

        
        fireResize: function() {
            var w = Ext.Element.getViewWidth(),
                h = Ext.Element.getViewHeight();

             
             if (EventManager.curHeight != h || EventManager.curWidth != w) {
                 EventManager.curHeight = h;
                 EventManager.curWidth = w;
                 EventManager.resizeEvent.fire(w, h);
             }
        },

        
        removeResizeListener: function(fn, scope) {
            var resize = EventManager.resizeEvent;
            if (resize) {
                resize.removeListener(fn, scope);
            }
        },

        
        onWindowUnload: function(fn, scope, options) {
            var unload = EventManager.unloadEvent;

            if (!unload) {
                EventManager.unloadEvent = unload = new Ext.util.Event();
                EventManager.addListener(win, 'unload', EventManager.fireUnload);
            }
            if (fn) {
                unload.addListener(fn, scope, options);
            }
        },

        
        fireUnload: function() {
            
            try {
                
                doc = win = undefined;

                var gridviews, i, ln,
                    el, cache;

                EventManager.unloadEvent.fire();
                
                if (Ext.isGecko3) {
                    gridviews = Ext.ComponentQuery.query('gridview');
                    i = 0;
                    ln = gridviews.length;
                    for (; i < ln; i++) {
                        gridviews[i].scrollToTop();
                    }
                }
                
                cache = Ext.cache;

                for (el in cache) {
                    if (cache.hasOwnProperty(el)) {
                        EventManager.removeAll(el);
                    }
                }
            } catch(e) {
            }
        },

        
        removeUnloadListener: function(fn, scope) {
            var unload = EventManager.unloadEvent;
            if (unload) {
                unload.removeListener(fn, scope);
            }
        },

        
        useKeyDown: Ext.isWebKit ?
                       parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
                       !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),

        
        getKeyEvent: function() {
            return EventManager.useKeyDown ? 'keydown' : 'keypress';
        }
    });

    
    if(!supportsAddEventListener && document.attachEvent) {
        Ext.apply( EventManager, {
            

            
            pollScroll : function() {
                var scrollable = true;

                try {
                    document.documentElement.doScroll('left');
                } catch(e) {
                    scrollable = false;
                }

                
                if (scrollable && document.body) {
                    EventManager.onReadyEvent({
                        type:'doScroll'
                    });
                } else {
                    
                    EventManager.scrollTimeout = setTimeout(EventManager.pollScroll, 20);
                }

                return scrollable;
            },

            
            scrollTimeout: null,

            
            readyStatesRe  : /complete/i,

            
            checkReadyState: function() {
                var state = document.readyState;

                if (EventManager.readyStatesRe.test(state)) {
                    EventManager.onReadyEvent({
                        type: state
                    });
                }
            },

            bindReadyEvent: function() {
                var topContext = true;

                if (EventManager.hasBoundOnReady) {
                    return;
                }

                
                try {
                    topContext = window.frameElement === undefined;
                } catch(e) {
                    
                    
                    topContext = false;
                }

                if (!topContext || !doc.documentElement.doScroll) {
                    EventManager.pollScroll = Ext.emptyFn;   
                }

                
                if (EventManager.pollScroll() === true) {
                    return;
                }

                
                if (doc.readyState == 'complete' )  {
                    EventManager.onReadyEvent({type: 'already ' + (doc.readyState || 'body') });
                } else {
                    doc.attachEvent('onreadystatechange', EventManager.checkReadyState);
                    window.attachEvent('onload', EventManager.onReadyEvent);
                    EventManager.hasBoundOnReady = true;
                }
            },

            onReadyEvent : function(e) {
                if (e && e.type) {
                    EventManager.onReadyChain.push(e.type);
                }

                if (EventManager.hasBoundOnReady) {
                    document.detachEvent('onreadystatechange', EventManager.checkReadyState);
                    window.detachEvent('onload', EventManager.onReadyEvent);
                }

                if (Ext.isNumber(EventManager.scrollTimeout)) {
                    clearTimeout(EventManager.scrollTimeout);
                    delete EventManager.scrollTimeout;
                }

                if (!Ext.isReady) {
                    EventManager.fireDocReady();
                }
            },

            
            onReadyChain : []
        });
    }


    
    Ext.onReady = function(fn, scope, options) {
        Ext.Loader.onReady(fn, scope, true, options);
    };

    
    Ext.onDocumentReady = EventManager.onDocumentReady;

    
    EventManager.on = EventManager.addListener;

    
    EventManager.un = EventManager.removeListener;

    Ext.onReady(initExtCss);
};



Ext.define('Ext.util.Observable', function(Observable) {

    
    var emptyArray = [],
        arrayProto = Array.prototype,
        arraySlice = arrayProto.slice,
        ExtEvent = Ext.util.Event,
        ListenerRemover = function(observable) {

            
            if (observable instanceof ListenerRemover) {
                return observable;
            }

            this.observable = observable;

            
            
            if (arguments[1].isObservable) {
                this.managedListeners = true;
            }
            this.args = arraySlice.call(arguments, 1);
        };

    ListenerRemover.prototype.destroy = function() {
        this.observable[this.managedListeners ? 'mun' : 'un'].apply(this.observable, this.args);
    };

    return {

        

                                                         

        statics: {
            
            releaseCapture: function(o) {
                o.fireEventArgs = this.prototype.fireEventArgs;
            },

            
            capture: function(o, fn, scope) {
                
                
                
                
                var newFn = function(eventName, args) {
                    return fn.apply(scope, [eventName].concat(args));
                }
                
                this.captureArgs(o, newFn, scope);
            },
            
            
            captureArgs: function(o, fn, scope) {
                o.fireEventArgs = Ext.Function.createInterceptor(o.fireEventArgs, fn, scope);
            },

            
            observe: function(cls, listeners) {
                if (cls) {
                    if (!cls.isObservable) {
                        Ext.applyIf(cls, new this());
                        this.captureArgs(cls.prototype, cls.fireEventArgs, cls);
                    }
                    if (Ext.isObject(listeners)) {
                        cls.on(listeners);
                    }
                }
                return cls;
            },

            
            prepareClass: function (T, mixin) {
                
                

                
                

                if (!T.HasListeners) {
                    
                    
                    
                    var HasListeners = function () {},
                        SuperHL = T.superclass.HasListeners || (mixin && mixin.HasListeners) ||
                                Observable.HasListeners;

                    
                    T.prototype.HasListeners = T.HasListeners = HasListeners;

                    
                    
                    HasListeners.prototype = T.hasListeners = new SuperHL();
                }
            }
        },

        

        

        
        isObservable: true,

        
        eventsSuspended: 0,

        

        constructor: function(config) {
            var me = this;

            Ext.apply(me, config);

            
            if (!me.hasListeners) {
                me.hasListeners = new me.HasListeners();
            }

            me.events = me.events || {};
            if (me.listeners) {
                me.on(me.listeners);
                me.listeners = null; 
            }

            if (me.bubbleEvents) {
                me.enableBubble(me.bubbleEvents);
            }
        },

        onClassExtended: function (T) {
            if (!T.HasListeners) {
                
                
                Observable.prepareClass(T);
            }
        },

        
        
        eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|destroyable|vertical|horizontal|freezeEvent|priority)$/,

        
        addManagedListener: function(item, ename, fn, scope, options,  noDestroy) {
            var me = this,
                managedListeners = me.managedListeners = me.managedListeners || [],
                config, passedOptions;

            if (typeof ename !== 'string') {
                
                
                
                
                passedOptions = arguments.length > 4 ? options : ename;

                options = ename;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!me.eventOptionsRe.test(ename)) {
                            
                            
                            me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope || scope, config.fn ? config : passedOptions, true);
                        }
                    }
                }
                if (options && options.destroyable) {
                    return new ListenerRemover(me, item, options);
                }
            }
            else {
                if (typeof fn === 'string') {
                    scope = scope || me;
                    fn = Ext.resolveMethod(fn, scope);
                }
                
                managedListeners.push({
                    item: item,
                    ename: ename,
                    fn: fn,
                    scope: scope,
                    options: options
                });

                item.on(ename, fn, scope, options);

                
                if (!noDestroy && options && options.destroyable) {
                    return new ListenerRemover(me, item, ename, fn, scope);
                }
            }
        },

        
        removeManagedListener: function(item, ename, fn, scope) {
            var me = this,
                options,
                config,
                managedListeners,
                length,
                i, func;

            if (typeof ename !== 'string') {
                options = ename;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!me.eventOptionsRe.test(ename)) {
                            me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope || scope);
                        }
                    }
                }
            } else {
                managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
                
                if (typeof fn === 'string') {
                    scope = scope || me;
                    fn = Ext.resolveMethod(fn, scope);
                }

                for (i = 0, length = managedListeners.length; i < length; i++) {
                    me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
                }
            }
        },

        
        fireEvent: function(eventName) {
            return this.fireEventArgs(eventName, arraySlice.call(arguments, 1));
        },

        
        fireEventArgs: function(eventName, args) {
            eventName = eventName.toLowerCase();
            var me = this,
                events = me.events,
                event = events && events[eventName],
                ret = true;

            
            
            if (event && me.hasListeners[eventName]) {
                ret = me.continueFireEvent(eventName, args || emptyArray, event.bubble);
            }
            return ret;
        },

        
        continueFireEvent: function(eventName, args, bubbles) {
            var target = this,
                queue, event,
                ret = true;

            do {
                if (target.eventsSuspended) {
                    if ((queue = target.eventQueue)) {
                        queue.push([eventName, args, bubbles]);
                    }
                    return ret;
                } else {
                    event = target.events[eventName];
                    
                    
                    if (event && event !== true) {
                        if ((ret = event.fire.apply(event, args)) === false) {
                            break;
                        }
                    }
                }
            } while (bubbles && (target = target.getBubbleParent()));
            return ret;
        },

        
        getBubbleParent: function() {
            var me = this, parent = me.getBubbleTarget && me.getBubbleTarget();
            if (parent && parent.isObservable) {
                return parent;
            }
            return null;
        },

        
        addListener: function(ename, fn, scope, options) {
            var me = this,
                config, event,
                prevListenerCount = 0;

            
            if (typeof ename !== 'string') {
                options = ename;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!me.eventOptionsRe.test(ename)) {
                            
                            me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
                        }
                    }
                }
                if (options && options.destroyable) {
                    return new ListenerRemover(me, options);
                }
            }
            
            else {
                ename = ename.toLowerCase();
                event = me.events[ename];
                if (event && event.isEvent) {
                    prevListenerCount = event.listeners.length;
                } else {
                    me.events[ename] = event = new ExtEvent(me, ename);
                }

                
                if (typeof fn === 'string') {
                    scope = scope || me;
                    fn = Ext.resolveMethod(fn, scope);
                }
                event.addListener(fn, scope, options);

                
                
                if (event.listeners.length !== prevListenerCount) {
                    me.hasListeners._incr_(ename);
                }
                if (options && options.destroyable) {
                    return new ListenerRemover(me, ename, fn, scope, options);
                }
            }
        },

        
        removeListener: function(ename, fn, scope) {
            var me = this,
                config,
                event,
                options;

            if (typeof ename !== 'string') {
                options = ename;
                for (ename in options) {
                    if (options.hasOwnProperty(ename)) {
                        config = options[ename];
                        if (!me.eventOptionsRe.test(ename)) {
                            me.removeListener(ename, config.fn || config, config.scope || options.scope);
                        }
                    }
                }
            } else {
                ename = ename.toLowerCase();
                event = me.events[ename];
                if (event && event.isEvent) {
                    if (typeof fn === 'string') {
                        scope = scope || me;
                        fn = Ext.resolveMethod(fn, scope);
                    }
                    
                    if (event.removeListener(fn, scope)) {
                        me.hasListeners._decr_(ename);
                    }
                }
            }
        },

        
        clearListeners: function() {
            var events = this.events,
                hasListeners = this.hasListeners,
                event,
                key;

            for (key in events) {
                if (events.hasOwnProperty(key)) {
                    event = events[key];
                    if (event.isEvent) {
                        delete hasListeners[key];
                        event.clearListeners();
                    }
                }
            }

            this.clearManagedListeners();
        },


        
        clearManagedListeners : function() {
            var managedListeners = this.managedListeners || [],
                i = 0,
                len = managedListeners.length;

            for (; i < len; i++) {
                this.removeManagedListenerItem(true, managedListeners[i]);
            }

            this.managedListeners = [];
        },

        
        removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
            if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
                managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
                if (!isClear) {
                    Ext.Array.remove(this.managedListeners, managedListener);
                }
            }
        },


        
        addEvents: function(o) {
            var me = this,
                events = me.events || (me.events = {}),
                arg, args, i;

            if (typeof o == 'string') {
                for (args = arguments, i = args.length; i--; ) {
                    arg = args[i];
                    if (!events[arg]) {
                        events[arg] = true;
                    }
                }
            } else {
                Ext.applyIf(me.events, o);
            }
        },

        
        hasListener: function(ename) {
            return !!this.hasListeners[ename.toLowerCase()];
        },

        
        suspendEvents: function(queueSuspended) {
            this.eventsSuspended += 1;
            if (queueSuspended && !this.eventQueue) {
                this.eventQueue = [];
            }
        },

        
        suspendEvent: function(eventName) {
            var len = arguments.length,
                i, event;

            for (i = 0; i < len; i++) {
                event = this.events[arguments[i]];

                
                if (event && event.suspend) {
                    event.suspend();
                }
            }
        },

        
        resumeEvent: function() {
            var len = arguments.length,
                i, event;

            for (i = 0; i < len; i++) {

                
                event = this.events[arguments[i]];
                if (event && event.resume) {
                    event.resume();
                }
            }
        },

        
        resumeEvents: function() {
            var me = this,
                queued = me.eventQueue,
                qLen, q;

            if (me.eventsSuspended && ! --me.eventsSuspended) {
                delete me.eventQueue;

                if (queued) {
                    qLen = queued.length;
                    for (q = 0; q < qLen; q++) {
                        me.continueFireEvent.apply(me, queued[q]);
                    }
                }
            }
        },

        
        relayEvents : function(origin, events, prefix) {
            var me = this,
                len = events.length,
                i = 0,
                oldName,
                relayers = {};

            for (; i < len; i++) {
                oldName = events[i];

                
                relayers[oldName] = me.createRelayer(prefix ? prefix + oldName : oldName);
            }
            
            
            
            me.mon(origin, relayers, null, null, undefined);

            
            return new ListenerRemover(me, origin, relayers);
        },

        
        createRelayer: function(newName, beginEnd) {
            var me = this;
            return function() {
                return me.fireEventArgs.call(me, newName, beginEnd ? arraySlice.apply(arguments, beginEnd) : arguments);
            };
        },

        
        enableBubble: function(eventNames) {
            if (eventNames) {
                var me = this,
                    names = (typeof eventNames == 'string') ? arguments : eventNames,
                    length = names.length,
                    events = me.events,
                    ename, event, i;

                for (i = 0; i < length; ++i) {
                    ename = names[i].toLowerCase();
                    event = events[ename];

                    if (!event || typeof event == 'boolean') {
                        events[ename] = event = new ExtEvent(me, ename);
                    }

                    
                    
                    me.hasListeners._incr_(ename);

                    event.bubble = true;
                }
            }
        }
    };
}, function() {
    var Observable = this,
        proto = Observable.prototype,
        HasListeners = function () {},
        prepareMixin = function (T) {
            if (!T.HasListeners) {
                var proto = T.prototype;

                
                Observable.prepareClass(T, this);

                
                
                T.onExtended(function (U) {
                    
                    Observable.prepareClass(U);
                });

                
                
                if (proto.onClassMixedIn) {
                    
                    Ext.override(T, {
                        onClassMixedIn: function (U) {
                            prepareMixin.call(this, U);
                            this.callParent(arguments);
                        }
                    });
                } else {
                    
                    proto.onClassMixedIn = function (U) {
                        prepareMixin.call(this, U);
                    };
                }
            }
        },
        globalEvents;

    HasListeners.prototype = {
        
        _decr_: function (ev) {
            if (! --this[ev]) {
                
                
                
                delete this[ev];
            }
        },
        _incr_: function (ev) {
            if (this.hasOwnProperty(ev)) {
                
                ++this[ev];
            } else {
                
                
                this[ev] = 1;
            }
        }
    };

    proto.HasListeners = Observable.HasListeners = HasListeners;

    Observable.createAlias({
        
        on: 'addListener',
        
        un: 'removeListener',
        
        mon: 'addManagedListener',
        
        mun: 'removeManagedListener'
    });

    
    Observable.observeClass = Observable.observe;

    
    Ext.globalEvents = globalEvents = new Observable({
        events: {
            idle: Ext.EventManager.idleEvent,
            ready: Ext.EventManager.readyEvent
        }
    });

    
    Ext.on = function() {
        return globalEvents.addListener.apply(globalEvents, arguments);
    };

    
    Ext.un = function() {
        return globalEvents.removeListener.apply(globalEvents, arguments);
    };

    
    
    
    function getMethodEvent(method){
        var e = (this.methodEvents = this.methodEvents || {})[method],
            returnValue,
            v,
            cancel,
            obj = this,
            makeCall;

        if (!e) {
            this.methodEvents[method] = e = {};
            e.originalFn = this[method];
            e.methodName = method;
            e.before = [];
            e.after = [];

            makeCall = function(fn, scope, args){
                if((v = fn.apply(scope || obj, args)) !== undefined){
                    if (typeof v == 'object') {
                        if(v.returnValue !== undefined){
                            returnValue = v.returnValue;
                        }else{
                            returnValue = v;
                        }
                        cancel = !!v.cancel;
                    }
                    else
                        if (v === false) {
                            cancel = true;
                        }
                        else {
                            returnValue = v;
                        }
                }
            };

            this[method] = function(){
                var args = Array.prototype.slice.call(arguments, 0),
                    b, i, len;
                returnValue = v = undefined;
                cancel = false;

                for(i = 0, len = e.before.length; i < len; i++){
                    b = e.before[i];
                    makeCall(b.fn, b.scope, args);
                    if (cancel) {
                        return returnValue;
                    }
                }

                if((v = e.originalFn.apply(obj, args)) !== undefined){
                    returnValue = v;
                }

                for(i = 0, len = e.after.length; i < len; i++){
                    b = e.after[i];
                    makeCall(b.fn, b.scope, args);
                    if (cancel) {
                        return returnValue;
                    }
                }
                return returnValue;
            };
        }
        return e;
    }

    Ext.apply(proto, {
        onClassMixedIn: prepareMixin,

        
        
        
        beforeMethod : function(method, fn, scope){
            getMethodEvent.call(this, method).before.push({
                fn: fn,
                scope: scope
            });
        },

        
        afterMethod : function(method, fn, scope){
            getMethodEvent.call(this, method).after.push({
                fn: fn,
                scope: scope
            });
        },

        removeMethodListener: function(method, fn, scope){
            var e = this.getMethodEvent(method),
                i, len;
            for(i = 0, len = e.before.length; i < len; i++){
                if(e.before[i].fn == fn && e.before[i].scope == scope){
                    Ext.Array.erase(e.before, i, 1);
                    return;
                }
            }
            for(i = 0, len = e.after.length; i < len; i++){
                if(e.after[i].fn == fn && e.after[i].scope == scope){
                    Ext.Array.erase(e.after, i, 1);
                    return;
                }
            }
        },

        toggleEventLogging: function(toggle) {
            Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
                if (Ext.isDefined(Ext.global.console)) {
                    Ext.global.console.log(en, arguments);
                }
            });
        }
    });
});






Ext.define('Ext.EventObjectImpl', {
                             

    
    BACKSPACE: 8,
    
    TAB: 9,
    
    NUM_CENTER: 12,
    
    ENTER: 13,
    
    RETURN: 13,
    
    SHIFT: 16,
    
    CTRL: 17,
    
    ALT: 18,
    
    PAUSE: 19,
    
    CAPS_LOCK: 20,
    
    ESC: 27,
    
    SPACE: 32,
    
    PAGE_UP: 33,
    
    PAGE_DOWN: 34,
    
    END: 35,
    
    HOME: 36,
    
    LEFT: 37,
    
    UP: 38,
    
    RIGHT: 39,
    
    DOWN: 40,
    
    PRINT_SCREEN: 44,
    
    INSERT: 45,
    
    DELETE: 46,
    
    ZERO: 48,
    
    ONE: 49,
    
    TWO: 50,
    
    THREE: 51,
    
    FOUR: 52,
    
    FIVE: 53,
    
    SIX: 54,
    
    SEVEN: 55,
    
    EIGHT: 56,
    
    NINE: 57,
    
    A: 65,
    
    B: 66,
    
    C: 67,
    
    D: 68,
    
    E: 69,
    
    F: 70,
    
    G: 71,
    
    H: 72,
    
    I: 73,
    
    J: 74,
    
    K: 75,
    
    L: 76,
    
    M: 77,
    
    N: 78,
    
    O: 79,
    
    P: 80,
    
    Q: 81,
    
    R: 82,
    
    S: 83,
    
    T: 84,
    
    U: 85,
    
    V: 86,
    
    W: 87,
    
    X: 88,
    
    Y: 89,
    
    Z: 90,
    
    CONTEXT_MENU: 93,
    
    NUM_ZERO: 96,
    
    NUM_ONE: 97,
    
    NUM_TWO: 98,
    
    NUM_THREE: 99,
    
    NUM_FOUR: 100,
    
    NUM_FIVE: 101,
    
    NUM_SIX: 102,
    
    NUM_SEVEN: 103,
    
    NUM_EIGHT: 104,
    
    NUM_NINE: 105,
    
    NUM_MULTIPLY: 106,
    
    NUM_PLUS: 107,
    
    NUM_MINUS: 109,
    
    NUM_PERIOD: 110,
    
    NUM_DIVISION: 111,
    
    F1: 112,
    
    F2: 113,
    
    F3: 114,
    
    F4: 115,
    
    F5: 116,
    
    F6: 117,
    
    F7: 118,
    
    F8: 119,
    
    F9: 120,
    
    F10: 121,
    
    F11: 122,
    
    F12: 123,
    
    WHEEL_SCALE: (function () {
        var scale;

        if (Ext.isGecko) {
            
            scale = 3;
        } else if (Ext.isMac) {
            
            
            

            if (Ext.isSafari && Ext.webKitVersion >= 532.0) {
                
                
                
                
                
                
                scale = 120;
            } else {
                
                
                scale = 12;
            }

            
            
            
            
            scale *= 3;
        } else {
            
            scale = 120;
        }

        return scale;
    }()),

    
    clickRe: /(dbl)?click/,
    
    safariKeys: {
        3: 13, 
        63234: 37, 
        63235: 39, 
        63232: 38, 
        63233: 40, 
        63276: 33, 
        63277: 34, 
        63272: 46, 
        63273: 36, 
        63275: 35 
    },
    
    btnMap: Ext.isIE ? {
        1: 0,
        4: 1,
        2: 2
    } : {
        0: 0,
        1: 1,
        2: 2
    },
    
    
    
    

    constructor: function(event, freezeEvent){
        if (event) {
            this.setEvent(event.browserEvent || event, freezeEvent);
        }
    },

    setEvent: function(event, freezeEvent){
        var me = this, button, options;

        if (event === me || (event && event.browserEvent)) { 
            return event;
        }
        me.browserEvent = event;
        if (event) {
            
            button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
            if (me.clickRe.test(event.type) && button == -1) {
                button = 0;
            }
            options = {
                type: event.type,
                button: button,
                shiftKey: event.shiftKey,
                
                ctrlKey: event.ctrlKey || event.metaKey || false,
                altKey: event.altKey,
                
                keyCode: event.keyCode,
                charCode: event.charCode,
                
                target: Ext.EventManager.getTarget(event),
                relatedTarget: Ext.EventManager.getRelatedTarget(event),
                currentTarget: event.currentTarget,
                xy: (freezeEvent ? me.getXY() : null)
            };
        } else {
            options = {
                button: -1,
                shiftKey: false,
                ctrlKey: false,
                altKey: false,
                keyCode: 0,
                charCode: 0,
                target: null,
                xy: [0, 0]
            };
        }
        Ext.apply(me, options);
        return me;
    },

    
    stopEvent: function(){
        this.stopPropagation();
        this.preventDefault();
    },

    
    preventDefault: function(){
        if (this.browserEvent) {
            Ext.EventManager.preventDefault(this.browserEvent);
        }
    },

    
    stopPropagation: function(){
        var browserEvent = this.browserEvent;

        if (browserEvent) {
            if (browserEvent.type == 'mousedown') {
                Ext.EventManager.stoppedMouseDownEvent.fire(this);
            }
            Ext.EventManager.stopPropagation(browserEvent);
        }
    },

    
    getCharCode: function(){
        return this.charCode || this.keyCode;
    },

    
    getKey: function(){
        return this.normalizeKey(this.keyCode || this.charCode);
    },

    
    normalizeKey: function(key){
        
        return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
    },

    
    getPageX: function(){
        return this.getX();
    },

    
    getPageY: function(){
        return this.getY();
    },

    
    getX: function() {
        return this.getXY()[0];
    },

    
    getY: function() {
        return this.getXY()[1];
    },

    
    getXY: function() {
        if (!this.xy) {
            
            this.xy = Ext.EventManager.getPageXY(this.browserEvent);
        }
        return this.xy;
    },

    
    getTarget : function(selector, maxDepth, returnEl){
        if (selector) {
            return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
        }
        return returnEl ? Ext.get(this.target) : this.target;
    },

    
    getRelatedTarget : function(selector, maxDepth, returnEl){
        if (selector && this.relatedTarget) {
            return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
        }
        return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
    },

    
    correctWheelDelta : function (delta) {
        var scale = this.WHEEL_SCALE,
            ret = Math.round(delta / scale);

        if (!ret && delta) {
            ret = (delta < 0) ? -1 : 1; 
        }

        return ret;
    },

    
    getWheelDeltas : function () {
        var me = this,
            event = me.browserEvent,
            dx = 0, dy = 0; 

        if (Ext.isDefined(event.wheelDeltaX)) { 
            dx = event.wheelDeltaX;
            dy = event.wheelDeltaY;
        } else if (event.wheelDelta) { 
            dy = event.wheelDelta;
        } else if (event.detail) { 
            dy = -event.detail; 

            
            
            if (dy > 100) {
                dy = 3;
            } else if (dy < -100) {
                dy = -3;
            }

            
            
            if (Ext.isDefined(event.axis) && event.axis === event.HORIZONTAL_AXIS) {
                dx = dy;
                dy = 0;
            }
        }

        return {
            x: me.correctWheelDelta(dx),
            y: me.correctWheelDelta(dy)
        };
    },

    
    getWheelDelta : function(){
        var deltas = this.getWheelDeltas();

        return deltas.y;
    },

    
    within : function(el, related, allowEl){
        if(el){
            var t = related ? this.getRelatedTarget() : this.getTarget(),
                result;

            if (t) {
                result = Ext.fly(el, '_internal').contains(t);
                if (!result && allowEl) {
                    result = t == Ext.getDom(el);
                }
                return result;
            }
        }
        return false;
    },

    
    isNavKeyPress : function(){
        var me = this,
            k = this.normalizeKey(me.keyCode);

       return (k >= 33 && k <= 40) ||  
       k == me.RETURN ||
       k == me.TAB ||
       k == me.ESC;
    },

    
    isSpecialKey : function(){
        var k = this.normalizeKey(this.keyCode);
        return (this.type == 'keypress' && this.ctrlKey) ||
        this.isNavKeyPress() ||
        (k == this.BACKSPACE) || 
        (k >= 16 && k <= 20) || 
        (k >= 44 && k <= 46);   
    },

    
    getPoint : function(){
        var xy = this.getXY();
        return new Ext.util.Point(xy[0], xy[1]);
    },

   
    hasModifier : function(){
        return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
    },

    
    injectEvent: (function () {
        var API,
            dispatchers = {}, 
            crazyIEButtons;

        

        
        

        if (!Ext.isIE9m && document.createEvent) { 
            API = {
                createHtmlEvent: function (doc, type, bubbles, cancelable) {
                    var event = doc.createEvent('HTMLEvents');

                    event.initEvent(type, bubbles, cancelable);
                    return event;
                },

                createMouseEvent: function (doc, type, bubbles, cancelable, detail,
                                            clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
                                            button, relatedTarget) {
                    var event = doc.createEvent('MouseEvents'),
                        view = doc.defaultView || window;

                    if (event.initMouseEvent) {
                        event.initMouseEvent(type, bubbles, cancelable, view, detail,
                                    clientX, clientY, clientX, clientY, ctrlKey, altKey,
                                    shiftKey, metaKey, button, relatedTarget);
                    } else { 
                        event = doc.createEvent('UIEvents');
                        event.initEvent(type, bubbles, cancelable);
                        event.view = view;
                        event.detail = detail;
                        event.screenX = clientX;
                        event.screenY = clientY;
                        event.clientX = clientX;
                        event.clientY = clientY;
                        event.ctrlKey = ctrlKey;
                        event.altKey = altKey;
                        event.metaKey = metaKey;
                        event.shiftKey = shiftKey;
                        event.button = button;
                        event.relatedTarget = relatedTarget;
                    }

                    return event;
                },

                createUIEvent: function (doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEvent('UIEvents'),
                        view = doc.defaultView || window;

                    event.initUIEvent(type, bubbles, cancelable, view, detail);
                    return event;
                },

                fireEvent: function (target, type, event) {
                    target.dispatchEvent(event);
                },

                fixTarget: function (target) {
                    
                    if (target == window && !target.dispatchEvent) {
                        return document;
                    }

                    return target;
                }
            };
        } else if (document.createEventObject) { 
            crazyIEButtons = { 0: 1, 1: 4, 2: 2 };

            API = {
                createHtmlEvent: function (doc, type, bubbles, cancelable) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },

                createMouseEvent: function (doc, type, bubbles, cancelable, detail,
                                            clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
                                            button, relatedTarget) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    event.detail = detail;
                    event.screenX = clientX;
                    event.screenY = clientY;
                    event.clientX = clientX;
                    event.clientY = clientY;
                    event.ctrlKey = ctrlKey;
                    event.altKey = altKey;
                    event.shiftKey = shiftKey;
                    event.metaKey = metaKey;
                    event.button = crazyIEButtons[button] || button;
                    event.relatedTarget = relatedTarget; 
                    return event;
                },

                createUIEvent: function (doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },

                fireEvent: function (target, type, event) {
                    target.fireEvent('on' + type, event);
                },

                fixTarget: function (target) {
                    if (target == document) {
                        
                        
                        return document.documentElement;
                    }

                    return target;
                }
            };
        }

        
        

        Ext.Object.each({
                load:   [false, false],
                unload: [false, false],
                select: [true, false],
                change: [true, false],
                submit: [true, true],
                reset:  [true, false],
                resize: [true, false],
                scroll: [true, false]
            },
            function (name, value) {
                var bubbles = value[0], cancelable = value[1];
                dispatchers[name] = function (targetEl, srcEvent) {
                    var e = API.createHtmlEvent(name, bubbles, cancelable);
                    API.fireEvent(targetEl, name, e);
                };
            });

        
        

        function createMouseEventDispatcher (type, detail) {
            var cancelable = (type != 'mousemove');
            return function (targetEl, srcEvent) {
                var xy = srcEvent.getXY(),
                    e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
                                detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
                                srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
                                srcEvent.relatedTarget);
                API.fireEvent(targetEl, type, e);
            };
        }

        Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
            function (eventName) {
                dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
            });

        
        

        Ext.Object.each({
                focusin:  [true, false],
                focusout: [true, false],
                activate: [true, true],
                focus:    [false, false],
                blur:     [false, false]
            },
            function (name, value) {
                var bubbles = value[0], cancelable = value[1];
                dispatchers[name] = function (targetEl, srcEvent) {
                    var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
                    API.fireEvent(targetEl, name, e);
                };
            });

        
        if (!API) {
            

            dispatchers = {}; 

            API = {
                fixTarget: Ext.identityFn
            };
        }

        function cannotInject (target, srcEvent) {
        }

        return function (target) {
            var me = this,
                dispatcher = dispatchers[me.type] || cannotInject,
                t = target ? (target.dom || target) : me.getTarget();

            t = API.fixTarget(t);
            dispatcher(t, me);
        };
    }()) 

}, function() {

Ext.EventObject = new Ext.EventObjectImpl();

});






Ext.define('Ext.dom.AbstractQuery', {
    
    select: function(q, root) {
        var results = [],
            nodes,
            i,
            j,
            qlen,
            nlen;

        root = root || document;

        if (typeof root == 'string') {
            root = document.getElementById(root);
        }

        q = q.split(",");

        for (i = 0,qlen = q.length; i < qlen; i++) {
            if (typeof q[i] == 'string') {
                
                
                if (typeof q[i][0] == '@') {
                    nodes = root.getAttributeNode(q[i].substring(1));
                    results.push(nodes);
                } else {
                    nodes = root.querySelectorAll(q[i]);

                    for (j = 0,nlen = nodes.length; j < nlen; j++) {
                        results.push(nodes[j]);
                    }
                }
            }
        }

        return results;
    },

    
    selectNode: function(q, root) {
        return this.select(q, root)[0];
    },

    
    is: function(el, q) {
        if (typeof el == "string") {
            el = document.getElementById(el);
        }
        return this.select(q).indexOf(el) !== -1;
    }

});





Ext.define('Ext.dom.AbstractHelper', {
    emptyTags : /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
    confRe : /^(?:tag|children|cn|html|tpl|tplData)$/i,
    endRe : /end/i,
    styleSepRe: /\s*(?::|;)\s*/,

    
    attributeTransform: { cls : 'class', htmlFor : 'for' },

    closeTags: {},

    decamelizeName : (function () {
        var camelCaseRe = /([a-z])([A-Z])/g,
            cache = {};

        function decamel (match, p1, p2) {
            return p1 + '-' + p2.toLowerCase();
        }

        return function (s) {
            return cache[s] || (cache[s] = s.replace(camelCaseRe, decamel));
        };
    }()),

    generateMarkup: function(spec, buffer) {
        var me = this,
            specType = typeof spec,
            attr, val, tag, i, closeTags;

        if (specType == "string" || specType == "number") {
            buffer.push(spec);
        } else if (Ext.isArray(spec)) {
            for (i = 0; i < spec.length; i++) {
                if (spec[i]) {
                    me.generateMarkup(spec[i], buffer);
                }
            }
        } else {
            tag = spec.tag || 'div';
            buffer.push('<', tag);

            for (attr in spec) {
                if (spec.hasOwnProperty(attr)) {
                    val = spec[attr];
                    if (!me.confRe.test(attr)) {
                        if (typeof val == "object") {
                            buffer.push(' ', attr, '="');
                            me.generateStyles(val, buffer).push('"');
                        } else {
                            buffer.push(' ', me.attributeTransform[attr] || attr, '="', val, '"');
                        }
                    }
                }
            }

            
            if (me.emptyTags.test(tag)) {
                buffer.push('/>');
            } else {
                buffer.push('>');

                
                if ((val = spec.tpl)) {
                    val.applyOut(spec.tplData, buffer);
                }
                if ((val = spec.html)) {
                    buffer.push(val);
                }
                if ((val = spec.cn || spec.children)) {
                    me.generateMarkup(val, buffer);
                }

                
                closeTags = me.closeTags;
                buffer.push(closeTags[tag] || (closeTags[tag] = '</' + tag + '>'));
            }
        }

        return buffer;
    },

    
    generateStyles: function (styles, buffer) {
        var a = buffer || [],
            name;

        for (name in styles) {
            if (styles.hasOwnProperty(name)) {
                a.push(this.decamelizeName(name), ':', styles[name], ';');
            }
        }

        return buffer || a.join('');
    },

    
    markup: function(spec) {
        if (typeof spec == "string") {
            return spec;
        }

        var buf = this.generateMarkup(spec, []);
        return buf.join('');
    },

    
    applyStyles: function(el, styles) {
        if (styles) {
            var i = 0,
                len;

            el = Ext.fly(el, '_applyStyles');
            if (typeof styles == 'function') {
                styles = styles.call();
            }
            if (typeof styles == 'string') {
                styles = Ext.util.Format.trim(styles).split(this.styleSepRe);
                for (len = styles.length; i < len;) {
                    el.setStyle(styles[i++], styles[i++]);
                }
            } else if (Ext.isObject(styles)) {
                el.setStyle(styles);
            }
        }
    },

    
    insertHtml: function(where, el, html) {
        var hash = {},
            setStart,
            range,
            frag,
            rangeEl;

        where = where.toLowerCase();

        
        hash['beforebegin'] = ['BeforeBegin', 'previousSibling'];
        hash['afterend'] = ['AfterEnd', 'nextSibling'];

        range = el.ownerDocument.createRange();
        setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before');
        if (hash[where]) {
            range[setStart](el);
            frag = range.createContextualFragment(html);
            el.parentNode.insertBefore(frag, where == 'beforebegin' ? el : el.nextSibling);
            return el[(where == 'beforebegin' ? 'previous' : 'next') + 'Sibling'];
        }
        else {
            rangeEl = (where == 'afterbegin' ? 'first' : 'last') + 'Child';
            if (el.firstChild) {
                range[setStart](el[rangeEl]);
                frag = range.createContextualFragment(html);
                if (where == 'afterbegin') {
                    el.insertBefore(frag, el.firstChild);
                }
                else {
                    el.appendChild(frag);
                }
            }
            else {
                el.innerHTML = html;
            }
            return el[rangeEl];
        }

        throw 'Illegal insertion point -> "' + where + '"';
    },

    
    insertBefore: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'beforebegin');
    },

    
    insertAfter: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'afterend', 'nextSibling');
    },

    
    insertFirst: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'afterbegin', 'firstChild');
    },

    
    append: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'beforeend', '', true);
    },

    
    overwrite: function(el, o, returnElement) {
        el = Ext.getDom(el);
        el.innerHTML = this.markup(o);
        return returnElement ? Ext.get(el.firstChild) : el.firstChild;
    },

    doInsert: function(el, o, returnElement, pos, sibling, append) {
        var newNode = this.insertHtml(pos, Ext.getDom(el), this.markup(o));
        return returnElement ? Ext.get(newNode, true) : newNode;
    }

});



Ext.define('Ext.dom.AbstractElement_static', {
    override: 'Ext.dom.AbstractElement',

    inheritableStatics: {
        unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
        camelRe: /(-[a-z])/gi,
        msRe: /^-ms-/,
        cssRe: /([a-z0-9\-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*)?;?/gi,
        opacityRe: /alpha\(opacity=(.*)\)/i,
        propertyCache: {},
        defaultUnit : "px",
        borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
        paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
        margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
        
        addUnits: function(size, units) {
            
            if (typeof size == 'number') {
                return size + (units || this.defaultUnit || 'px');
            }

            
            if (size === "" || size == "auto" || size === undefined || size === null) {
                return size || '';
            }

            
            if (!this.unitRe.test(size)) {
                return size || '';
            }

            return size;
        },

        
        isAncestor: function(p, c) {
            var ret = false;

            p = Ext.getDom(p);
            c = Ext.getDom(c);
            if (p && c) {
                if (p.contains) {
                    return p.contains(c);
                } else if (p.compareDocumentPosition) {
                    return !!(p.compareDocumentPosition(c) & 16);
                } else {
                    while ((c = c.parentNode)) {
                        ret = c == p || ret;
                    }
                }
            }
            return ret;
        },

        
        parseBox: function(box) {
            box = box || 0;
            
            var type = typeof box,
                parts,
                ln;

            if (type === 'number') {
                return {
                    top   : box,
                    right : box,
                    bottom: box,
                    left  : box
                };
             } else if (type !== 'string') {
                 
                 return box;
             }

            parts  = box.split(' ');
            ln = parts.length;

            if (ln == 1) {
                parts[1] = parts[2] = parts[3] = parts[0];
            } else if (ln == 2) {
                parts[2] = parts[0];
                parts[3] = parts[1];
            } else if (ln == 3) {
                parts[3] = parts[1];
            }

            return {
                top   :parseFloat(parts[0]) || 0,
                right :parseFloat(parts[1]) || 0,
                bottom:parseFloat(parts[2]) || 0,
                left  :parseFloat(parts[3]) || 0
            };
        },

        
        unitizeBox: function(box, units) {
            var a = this.addUnits,
                b = this.parseBox(box);

            return a(b.top, units) + ' ' +
                   a(b.right, units) + ' ' +
                   a(b.bottom, units) + ' ' +
                   a(b.left, units);

        },

        
        camelReplaceFn: function(m, a) {
            return a.charAt(1).toUpperCase();
        },

        
        normalize: function(prop) {
            
            if (prop == 'float') {
                prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
            }
            
            return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.msRe, 'ms-').replace(this.camelRe, this.camelReplaceFn));
        },

        
        getDocumentHeight: function() {
            return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
        },

        
        getDocumentWidth: function() {
            return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
        },

        
        getViewportHeight: function(){
            return window.innerHeight;
        },

        
        getViewportWidth: function() {
            return window.innerWidth;
        },

        
        getViewSize: function() {
            return {
                width: window.innerWidth,
                height: window.innerHeight
            };
        },

        
        getOrientation: function() {
            if (Ext.supports.OrientationChange) {
                return (window.orientation == 0) ? 'portrait' : 'landscape';
            }

            return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
        },

        
        fromPoint: function(x, y) {
            return Ext.get(document.elementFromPoint(x, y));
        },

        
        parseStyles: function(styles){
            var out = {},
                cssRe = this.cssRe,
                matches;

            if (styles) {
                
                
                
                
                cssRe.lastIndex = 0;
                while ((matches = cssRe.exec(styles))) {
                    out[matches[1]] = matches[2]||'';
                }
            }
            return out;
        }
    }
},
function () {
    var doc = document,
        activeElement = null,
        isCSS1 = doc.compatMode == "CSS1Compat";

    
    
    
    if (!('activeElement' in doc) && doc.addEventListener) {
        doc.addEventListener('focus',
            function (ev) {
                if (ev && ev.target) {
                    activeElement = (ev.target == doc) ? null : ev.target;
                }
            }, true);
    }

    
    function makeSelectionRestoreFn (activeEl, start, end) {
        return function () {
            activeEl.selectionStart = start;
            activeEl.selectionEnd = end;
        };
    }

    this.addInheritableStatics({
        
        getActiveElement: function () {
            var active;
            
            
            
            try {
                active = doc.activeElement;
            } catch(e) {}
            
            
            
            active = active || activeElement;
            if (!active) {
                active = activeElement = document.body;
            }
            return active;
        },

        
        getRightMarginFixCleaner: function (target) {
            var supports = Ext.supports,
                hasInputBug = supports.DisplayChangeInputSelectionBug,
                hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug,
                activeEl,
                tag,
                start,
                end;

            if (hasInputBug || hasTextAreaBug) {
                activeEl = doc.activeElement || activeElement; 
                tag = activeEl && activeEl.tagName;

                if ((hasTextAreaBug && tag == 'TEXTAREA') ||
                    (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
                    if (Ext.dom.Element.isAncestor(target, activeEl)) {
                        start = activeEl.selectionStart;
                        end = activeEl.selectionEnd;

                        if (Ext.isNumber(start) && Ext.isNumber(end)) { 
                            
                            
                            
                            
                            return makeSelectionRestoreFn(activeEl, start, end);
                        }
                    }
                }
            }

            return Ext.emptyFn; 
        },

        getViewWidth: function(full) {
            return full ? Ext.dom.Element.getDocumentWidth() : Ext.dom.Element.getViewportWidth();
        },

        getViewHeight: function(full) {
            return full ? Ext.dom.Element.getDocumentHeight() : Ext.dom.Element.getViewportHeight();
        },

        getDocumentHeight: function() {
            return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, Ext.dom.Element.getViewportHeight());
        },

        getDocumentWidth: function() {
            return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, Ext.dom.Element.getViewportWidth());
        },

        getViewportHeight: function(){
            return Ext.isIE9m ?
                   (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
                   self.innerHeight;
        },

        getViewportWidth: function() {
            return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
                   Ext.isIE9m ? doc.documentElement.clientWidth : self.innerWidth;
        },

        
        serializeForm: function(form) {
            var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
                hasSubmit = false,
                encoder   = encodeURIComponent,
                data      = '',
                eLen      = fElements.length,
                element, name, type, options, hasValue, e,
                o, oLen, opt;

            for (e = 0; e < eLen; e++) {
                element = fElements[e];
                name    = element.name;
                type    = element.type;
                options = element.options;

                if (!element.disabled && name) {
                    if (/select-(one|multiple)/i.test(type)) {
                        oLen = options.length;
                        for (o = 0; o < oLen; o++) {
                            opt = options[o];
                            if (opt.selected) {
                                hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
                                data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
                            }
                        }
                    } else if (!(/file|undefined|reset|button/i.test(type))) {
                        if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
                            data += encoder(name) + '=' + encoder(element.value) + '&';
                            hasSubmit = /submit/i.test(type);
                        }
                    }
                }
            }
            return data.substr(0, data.length - 1);
        }
    });
});



Ext.define('Ext.dom.AbstractElement_insertion', {
    override: 'Ext.dom.AbstractElement',

    
    appendChild: function(el, returnDom) {
        var me = this,
            insertEl,
            eLen, e, oldUseDom;

        if (el.nodeType || el.dom || typeof el == 'string') { 
            el = Ext.getDom(el);
            me.dom.appendChild(el);
            return !returnDom ? Ext.get(el) : el;
        } else if (el.length) {
            
            insertEl = Ext.fly(document.createDocumentFragment(), '_internal');
            eLen = el.length;

            
            Ext.DomHelper.useDom = true;
            for (e = 0; e < eLen; e++) {
                insertEl.appendChild(el[e], returnDom);
            }
            Ext.DomHelper.useDom = oldUseDom;
            me.dom.appendChild(insertEl.dom);
            return returnDom ? insertEl.dom : insertEl;
        }
        else { 
            return me.createChild(el, null, returnDom);
        }
    },

    
    appendTo: function(el) {
        Ext.getDom(el).appendChild(this.dom);
        return this;
    },

    
    insertBefore: function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el);
        return this;
    },

    
    insertAfter: function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el.nextSibling);
        return this;
    },

    
    insertFirst: function(el, returnDom) {
        el = el || {};
        if (el.nodeType || el.dom || typeof el == 'string') { 
            el = Ext.getDom(el);
            this.dom.insertBefore(el, this.dom.firstChild);
            return !returnDom ? Ext.get(el) : el;
        }
        else { 
            return this.createChild(el, this.dom.firstChild, returnDom);
        }
    },

    
    insertSibling: function(el, where, returnDom) {
        var me        = this,
            DomHelper = Ext.core.DomHelper,
            oldUseDom = DomHelper.useDom,
            isAfter   = (where || 'before').toLowerCase() == 'after',
            rt, insertEl, eLen, e;

        if (Ext.isArray(el)) {
            
            insertEl = Ext.fly(document.createDocumentFragment(), '_internal');
            eLen = el.length;

            
            DomHelper.useDom = true;
            for (e = 0; e < eLen; e++) {
                rt = insertEl.appendChild(el[e], returnDom);
            }
            DomHelper.useDom = oldUseDom;

            
            me.dom.parentNode.insertBefore(insertEl.dom, isAfter ? me.dom.nextSibling : me.dom);
            return rt;
        }

        el = el || {};

        if (el.nodeType || el.dom) {
            rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
            if (!returnDom) {
                rt = Ext.get(rt);
            }
        } else {
            if (isAfter && !me.dom.nextSibling) {
                rt = DomHelper.append(me.dom.parentNode, el, !returnDom);
            } else {
                rt = DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
            }
        }
        return rt;
    },

    
    replace: function(el) {
        el = Ext.get(el);
        this.insertBefore(el);
        el.remove();
        return this;
    },

    
    replaceWith: function(el){
        var me = this;

        if (el.nodeType || el.dom || typeof el == 'string') {
            el = Ext.get(el);
            me.dom.parentNode.insertBefore(el.dom, me.dom);
        } else {
            el = Ext.core.DomHelper.insertBefore(me.dom, el);
        }

        delete Ext.cache[me.id];
        Ext.removeNode(me.dom);
        me.id = Ext.id(me.dom = el);
        Ext.dom.AbstractElement.addToCache(me.isFlyweight ? new Ext.dom.AbstractElement(me.dom) : me);
        return me;
    },

    
    createChild: function(config, insertBefore, returnDom) {
        config = config || {tag:'div'};
        if (insertBefore) {
            return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
        }
        else {
            return Ext.core.DomHelper.append(this.dom, config,  returnDom !== true);
        }
    },

    
    wrap: function(config, returnDom, selector) {
        var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, true),
            target = newEl;
        
        if (selector) {
            target = Ext.DomQuery.selectNode(selector, newEl.dom);
        }

        target.appendChild(this.dom);
        return returnDom ? newEl.dom : newEl;
    },

    
    insertHtml: function(where, html, returnEl) {
        var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
        return returnEl ? Ext.get(el) : el;
    }
});



Ext.define('Ext.dom.AbstractElement_style', {

    override: 'Ext.dom.AbstractElement'

}, function() {
    
    var Element = this,
        wordsRe = /\w/g,
        spacesRe = /\s+/,
        transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
        
        
        
        
        hasClassList = Ext.supports.ClassList,
        PADDING = 'padding',
        MARGIN = 'margin',
        BORDER = 'border',
        LEFT_SUFFIX = '-left',
        RIGHT_SUFFIX = '-right',
        TOP_SUFFIX = '-top',
        BOTTOM_SUFFIX = '-bottom',
        WIDTH = '-width',
        
        borders = {l: BORDER + LEFT_SUFFIX + WIDTH, r: BORDER + RIGHT_SUFFIX + WIDTH, t: BORDER + TOP_SUFFIX + WIDTH, b: BORDER + BOTTOM_SUFFIX + WIDTH},
        paddings = {l: PADDING + LEFT_SUFFIX, r: PADDING + RIGHT_SUFFIX, t: PADDING + TOP_SUFFIX, b: PADDING + BOTTOM_SUFFIX},
        margins = {l: MARGIN + LEFT_SUFFIX, r: MARGIN + RIGHT_SUFFIX, t: MARGIN + TOP_SUFFIX, b: MARGIN + BOTTOM_SUFFIX},
        internalFly = new Element.Fly();


    Ext.override(Element, {

        
        styleHooks: {},

        
        addStyles : function(sides, styles){
            var totalSize = 0,
                sidesArr = (sides || '').match(wordsRe),
                i,
                len = sidesArr.length,
                side,
                styleSides = [];

            if (len == 1) {
                totalSize = Math.abs(parseFloat(this.getStyle(styles[sidesArr[0]])) || 0);
            } else if (len) {
                for (i = 0; i < len; i++) {
                    side = sidesArr[i];
                    styleSides.push(styles[side]);
                }
                
                styleSides = this.getStyle(styleSides);

                for (i=0; i < len; i++) {
                    side = sidesArr[i];
                    totalSize += Math.abs(parseFloat(styleSides[styles[side]]) || 0);
                }
            }

            return totalSize;
        },

        
        addCls: (function(){
            var addWithClassList = function(className) {
                var me = this,
                    dom = me.dom,
                    trimRe = me.trimRe,
                    origClassName = className,
                    classList,
                    newCls,
                    i,
                    len,
                    cls;

                if (typeof(className) == 'string') {
                    
                    className = className.replace(trimRe, '').split(spacesRe);
                }

                
                
                if (dom && className && !!(len = className.length)) {
                    if (!dom.className) {
                        dom.className = className.join(' ');
                    } else {
                        classList = dom.classList;
                        if (classList) {
                            for (i = 0; i < len; ++i) {
                                cls = className[i];
                                if (cls) {
                                    if (!classList.contains(cls)) {
                                        if (newCls) {
                                            newCls.push(cls);
                                        } else {
                                            newCls = dom.className.replace(trimRe, '');
                                            newCls = newCls ? [newCls, cls] : [cls];
                                        }
                                    }
                                }
                            }

                            if (newCls) {
                                dom.className = newCls.join(' '); 
                            }
                        } else {
                            addWithoutClassList(origClassName);
                        }
                    }
                }
                return me;
            }, addWithoutClassList = function(className) {
                var me = this,
                    dom = me.dom,
                    elClasses;

                if (dom && className && className.length) {
                    elClasses = Ext.Element.mergeClsList(dom.className, className);
                    if (elClasses.changed) {
                        dom.className = elClasses.join(' '); 
                    }
                }
                return me;
            };
            
            return hasClassList ? addWithClassList : addWithoutClassList;
        })(),


        
        removeCls: function(className) {
            var me = this,
                dom = me.dom,
                classList,
                len,
                elClasses;

            if (typeof(className) == 'string') {
                
                className = className.replace(me.trimRe, '').split(spacesRe);
            }

            if (dom && dom.className && className && !!(len = className.length)) {
                classList = dom.classList;
                if (len === 1 && classList) {
                    if (className[0]) {
                        classList.remove(className[0]); 
                    }
                } else {
                    elClasses = Ext.Element.removeCls(dom.className, className);
                    if (elClasses.changed) {
                        dom.className = elClasses.join(' ');
                    }
                }
            }
            return me;
        },

        
        radioCls: function(className) {
            var cn = this.dom.parentNode.childNodes,
                v,
                i, len;
            className = Ext.isArray(className) ? className: [className];
            for (i = 0, len = cn.length; i < len; i++) {
                v = cn[i];
                if (v && v.nodeType == 1) {
                    internalFly.attach(v).removeCls(className);
                }
            }
            return this.addCls(className);
        },

        
        toggleCls: (function(){
            var toggleWithClassList = function(className){
                var me = this,
                    dom = me.dom,
                    classList;

                if (dom) {
                    className = className.replace(me.trimRe, '');
                    if (className) {
                        classList = dom.classList;
                        if (classList) {
                            classList.toggle(className);
                        } else {
                            toggleWithoutClassList(className);
                        }
                    }
                }

                return me;
            }, toggleWithoutClassList = function(className){
                return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
            };
            
            return hasClassList ? toggleWithClassList :  toggleWithoutClassList;
        })(),

        
        hasCls: (function(){
            var hasClsWithClassList = function(className) {
                var dom = this.dom,
                    out = false,
                    classList;
                    
                if (dom && className) {
                    classList = dom.classList;
                    if (classList) {
                        out = classList.contains(className);
                    } else {
                        out = hasClsWithoutClassList(className);
                    }
                }
                return out;
            }, hasClsWithoutClassList = function(className){
                var dom = this.dom;
                return dom ? className && (' '+dom.className+' ').indexOf(' '+className+' ') !== -1 : false;
            };
            
            return hasClassList ? hasClsWithClassList : hasClsWithoutClassList;
        })(),

        
        replaceCls: function(oldClassName, newClassName){
            return this.removeCls(oldClassName).addCls(newClassName);
        },

        
        isStyle: function(style, val) {
            return this.getStyle(style) == val;
        },

        
        getStyle: function (property, inline) {
            var me = this,
                dom = me.dom,
                multiple = typeof property != 'string',
                hooks = me.styleHooks,
                prop = property,
                props = prop,
                len = 1,
                domStyle, camel, values, hook, out, style, i;

            if (multiple) {
                values = {};
                prop = props[0];
                i = 0;
                if (!(len = props.length)) {
                    return values;
                }
            }

            if (!dom || dom.documentElement) {
                return values || '';
            }

            domStyle = dom.style;

            if (inline) {
                style = domStyle;
            } else {
                
                
                
                
                style = dom.ownerDocument.defaultView.getComputedStyle(dom, null);

                
                if (!style) {
                    inline = true;
                    style = domStyle;
                }
            }

            do {
                hook = hooks[prop];

                if (!hook) {
                    hooks[prop] = hook = { name: Element.normalize(prop) };
                }

                if (hook.get) {
                    out = hook.get(dom, me, inline, style);
                } else {
                    camel = hook.name;
                    out = style[camel];
                }

                if (!multiple) {
                   return out;
                }

                values[prop] = out;
                prop = props[++i];
            } while (i < len);

            return values;
        },

        getStyles: function () {
            var props = Ext.Array.slice(arguments),
                len = props.length,
                inline;

            if (len && typeof props[len-1] == 'boolean') {
                inline = props.pop();
            }

            return this.getStyle(props, inline);
        },

        
        isTransparent: function (prop) {
            var value = this.getStyle(prop);
            return value ? transparentRe.test(value) : false;
        },

        
        setStyle: function(prop, value) {
            var me = this,
                dom = me.dom,
                hooks = me.styleHooks,
                style = dom.style,
                name = prop,
                hook;

            
            if (typeof name == 'string') {
                hook = hooks[name];
                if (!hook) {
                    hooks[name] = hook = { name: Element.normalize(name) };
                }
                value = (value == null) ? '' : value;
                if (hook.set) {
                    hook.set(dom, value, me);
                } else {
                    style[hook.name] = value;
                }
                if (hook.afterSet) {
                    hook.afterSet(dom, value, me);
                }
            } else {
                for (name in prop) {
                    if (prop.hasOwnProperty(name)) {
                        hook = hooks[name];
                        if (!hook) {
                            hooks[name] = hook = { name: Element.normalize(name) };
                        }
                        value = prop[name];
                        value = (value == null) ? '' : value;
                        if (hook.set) {
                            hook.set(dom, value, me);
                        } else {
                            style[hook.name] = value;
                        }
                        if (hook.afterSet) {
                            hook.afterSet(dom, value, me);
                        }
                    }
                }
            }

            return me;
        },

        
        getHeight: function(contentHeight) {
            var dom = this.dom,
                height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight;
            return height > 0 ? height: 0;
        },

        
        getWidth: function(contentWidth) {
            var dom = this.dom,
                width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth;
            return width > 0 ? width: 0;
        },

        
        setWidth: function(width) {
            var me = this;
                me.dom.style.width = Element.addUnits(width);
            return me;
        },

        
        setHeight: function(height) {
            var me = this;
                me.dom.style.height = Element.addUnits(height);
            return me;
        },

        
        getBorderWidth: function(side){
            return this.addStyles(side, borders);
        },

        
        getPadding: function(side){
            return this.addStyles(side, paddings);
        },

        margins : margins,

        
        applyStyles: function(styles) {
            if (styles) {
                var i,
                    len,
                    dom = this.dom;

                if (typeof styles == 'function') {
                    styles = styles.call();
                }
                if (typeof styles == 'string') {
                    styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/);
                    for (i = 0, len = styles.length; i < len;) {
                        dom.style[Element.normalize(styles[i++])] = styles[i++];
                    }
                }
                else if (typeof styles == 'object') {
                    this.setStyle(styles);
                }
            }
        },

        
        setSize: function(width, height) {
            var me = this,
                style = me.dom.style;

            if (Ext.isObject(width)) {
                
                height = width.height;
                width = width.width;
            }

            style.width = Element.addUnits(width);
            style.height = Element.addUnits(height);
            return me;
        },

        
        getViewSize: function() {
            var doc = document,
                dom = this.dom;

            if (dom == doc || dom == doc.body) {
                return {
                    width: Element.getViewportWidth(),
                    height: Element.getViewportHeight()
                };
            }
            else {
                return {
                    width: dom.clientWidth,
                    height: dom.clientHeight
                };
            }
        },

        
        getSize: function(contentSize) {
            var dom = this.dom;
            return {
                width: Math.max(0, contentSize ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth),
                height: Math.max(0, contentSize ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight)
            };
        },

        
        repaint: function() {
            var dom = this.dom;
            this.addCls(Ext.baseCSSPrefix + 'repaint');
            setTimeout(function(){
                internalFly.attach(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
            }, 1);
            return this;
        },

        
        getMargin: function(side){
            var me = this,
                hash = {t:"top", l:"left", r:"right", b: "bottom"},
                key,
                o,
                margins;

            if (!side) {
                margins = [];
                for (key in me.margins) {
                    if(me.margins.hasOwnProperty(key)) {
                        margins.push(me.margins[key]);
                    }
                }
                o = me.getStyle(margins);
                if(o && typeof o == 'object') {
                    
                    for (key in me.margins) {
                        if(me.margins.hasOwnProperty(key)) {
                            o[hash[key]] = parseFloat(o[me.margins[key]]) || 0;
                        }
                    }
                }

                return o;
            } else {
                return me.addStyles(side, me.margins);
            }
        },

        
        mask: function(msg, msgCls, transparent) {
            var me = this,
                dom = me.dom,
                data = (me.$cache || me.getCache()).data,
                el = data.mask,
                mask,
                size,
                cls = '',
                prefix = Ext.baseCSSPrefix;

            me.addCls(prefix + 'masked');
            if (me.getStyle("position") == "static") {
                me.addCls(prefix + 'masked-relative');
            }
            if (el) {
                el.remove();
            }
            if (msgCls && typeof msgCls == 'string' ) {
                cls = ' ' + msgCls;
            }
            else {
                cls = ' ' + prefix + 'mask-gray';
            }

            mask = me.createChild({
                cls: prefix + 'mask' + ((transparent !== false) ? '' : (' ' + prefix + 'mask-gray')),
                html: msg ? ('<div class="' + (msgCls || (prefix + 'mask-message')) + '">' + msg + '</div>') : ''
            });

            size = me.getSize();

            data.mask = mask;

            if (dom === document.body) {
                size.height = window.innerHeight;
                if (me.orientationHandler) {
                    Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                }

                me.orientationHandler = function() {
                    size = me.getSize();
                    size.height = window.innerHeight;
                    mask.setSize(size);
                };

                Ext.EventManager.onOrientationChange(me.orientationHandler, me);
            }
            mask.setSize(size);
            if (Ext.is.iPad) {
                Ext.repaint();
            }
        },

        
        unmask: function() {
            var me = this,
                data = (me.$cache || me.getCache()).data,
                mask = data.mask,
                prefix = Ext.baseCSSPrefix;

            if (mask) {
                mask.remove();
                delete data.mask;
            }
            me.removeCls([prefix + 'masked', prefix + 'masked-relative']);

            if (me.dom === document.body) {
                Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                delete me.orientationHandler;
            }
        }
    });


    Ext.onReady(function () {
        var supports = Ext.supports,
            styleHooks,
            colorStyles, i, name, camel;

        function fixTransparent (dom, el, inline, style) {
            var value = style[this.name] || '';
            return transparentRe.test(value) ? 'transparent' : value;
        }

        function fixRightMargin (dom, el, inline, style) {
            var result = style.marginRight,
                domStyle, display;

            
            
            if (result != '0px') {
                domStyle = dom.style;
                display = domStyle.display;
                domStyle.display = 'inline-block';
                result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, null)).marginRight;
                domStyle.display = display;
            }

            return result;
        }

        function fixRightMarginAndInputFocus (dom, el, inline, style) {
            var result = style.marginRight,
                domStyle, cleaner, display;

            if (result != '0px') {
                domStyle = dom.style;
                cleaner = Element.getRightMarginFixCleaner(dom);
                display = domStyle.display;
                domStyle.display = 'inline-block';
                result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, '')).marginRight;
                domStyle.display = display;
                cleaner();
            }

            return result;
        }

        styleHooks = Element.prototype.styleHooks;

        
        
        if (supports.init) {
            supports.init();
        }

        
        if (!supports.RightMargin) {
            styleHooks.marginRight = styleHooks['margin-right'] = {
                name: 'marginRight',
                
                
                get: (supports.DisplayChangeInputSelectionBug || supports.DisplayChangeTextAreaSelectionBug) ?
                    fixRightMarginAndInputFocus : fixRightMargin
            };
        }

        if (!supports.TransparentColor) {
            colorStyles = ['background-color', 'border-color', 'color', 'outline-color'];
            for (i = colorStyles.length; i--; ) {
                name = colorStyles[i];
                camel = Element.normalize(name);

                styleHooks[name] = styleHooks[camel] = {
                    name: camel,
                    get: fixTransparent
                };
            }
        }
    });

});



Ext.define('Ext.dom.AbstractElement_traversal', {
    override: 'Ext.dom.AbstractElement',

    
    findParent: function(simpleSelector, limit, returnEl) {
        var target = this.dom,
            topmost = document.documentElement,
            depth = 0,
            stopEl;

        limit = limit || 50;
        if (isNaN(limit)) {
            stopEl = Ext.getDom(limit);
            limit = Number.MAX_VALUE;
        }
        while (target && target.nodeType == 1 && depth < limit && target != topmost && target != stopEl) {
            if (Ext.DomQuery.is(target, simpleSelector)) {
                return returnEl ? Ext.get(target) : target;
            }
            depth++;
            target = target.parentNode;
        }
        return null;
    },

    
    findParentNode: function(simpleSelector, limit, returnEl) {
        var p = Ext.fly(this.dom.parentNode, '_internal');
        return p ? p.findParent(simpleSelector, limit, returnEl) : null;
    },

    
    up: function(simpleSelector, limit, returnDom) {
        return this.findParentNode(simpleSelector, limit, !returnDom);
    },

    
    select: function(selector, composite) {
        return Ext.dom.Element.select(selector, this.dom, composite);
    },

    
    query: function(selector) {
        return Ext.DomQuery.select(selector, this.dom);
    },

    
    down: function(selector, returnDom) {
        var n = Ext.DomQuery.selectNode(selector, this.dom);
        return returnDom ? n : Ext.get(n);
    },

    
    child: function(selector, returnDom) {
        var node,
            me = this,
            id;

        
        
        id = Ext.id(me.dom);
        
        id = Ext.escapeId(id);
        node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
        return returnDom ? node : Ext.get(node);
    },

     
    parent: function(selector, returnDom) {
        return this.matchNode('parentNode', 'parentNode', selector, returnDom);
    },

     
    next: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
    },

    
    prev: function(selector, returnDom) {
        return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
    },


    
    first: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
    },

    
    last: function(selector, returnDom) {
        return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
    },

    matchNode: function(dir, start, selector, returnDom) {
        if (!this.dom) {
            return null;
        }

        var n = this.dom[start];
        while (n) {
            if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
                return !returnDom ? Ext.get(n) : n;
            }
            n = n[dir];
        }
        return null;
    },

    isAncestor: function(element) {
        return this.self.isAncestor.call(this.self, this.dom, element);
    }
});





Ext.define('Ext.dom.AbstractElement', {
               
                           
                                         
                                            
                                        
                                           
      

    trimRe: /^\s+|\s+$/g,
    whitespaceRe: /\s/,
    
    inheritableStatics: {
        trimRe: /^\s+|\s+$/g,
        whitespaceRe: /\s/,

        
        get: function(el) {
            var me = this,
                document = window.document,
                El = Ext.dom.Element,
                cacheItem,
                docEl,
                extEl,
                dom,
                id;

            if (!el) {
                return null;
            }

            
            if (el.isFly) {
                el = el.dom;
            }

            if (typeof el == "string") { 
                if (el == Ext.windowId) {
                    return El.get(window);
                } else if (el == Ext.documentId) {
                    return El.get(document);
                }
                
                cacheItem = Ext.cache[el];
                
                
                
                
                if (cacheItem && cacheItem.skipGarbageCollection) {
                    extEl = cacheItem.el;
                    return extEl;
                }
                
                if (!(dom = document.getElementById(el))) {
                    return null;
                }

                if (cacheItem && cacheItem.el) {
                    extEl = Ext.updateCacheEntry(cacheItem, dom).el;
                } else {
                    
                    extEl = new El(dom, !!cacheItem);
                }
                return extEl;
            } else if (el.tagName) { 
                if (!(id = el.id)) {
                    id = Ext.id(el);
                }
                cacheItem = Ext.cache[id];
                if (cacheItem && cacheItem.el) {
                    extEl = Ext.updateCacheEntry(cacheItem, el).el;
                } else {
                    
                    extEl = new El(el, !!cacheItem);
                }
                return extEl;
            } else if (el instanceof me) {
                if (el != me.docEl && el != me.winEl) {
                    id = el.id;
                    
                    
                    cacheItem = Ext.cache[id];
                    if (cacheItem) {
                        Ext.updateCacheEntry(cacheItem, document.getElementById(id) || el.dom);
                    }
                }
                return el;
            } else if (el.isComposite) {
                return el;
            } else if (Ext.isArray(el)) {
                return me.select(el);
            } else if (el === document) {
                
                if (!me.docEl) {
                    docEl = me.docEl = Ext.Object.chain(El.prototype);
                    docEl.dom = document;
                    
                    
                    
                    docEl.el = docEl;
                    docEl.id = Ext.id(document);
                    me.addToCache(docEl);
                }
                return me.docEl;
            } else if (el === window) {
                if (!me.winEl) {
                    me.winEl = Ext.Object.chain(El.prototype);
                    me.winEl.dom = window;
                    me.winEl.id = Ext.id(window);
                    me.addToCache(me.winEl);
                }
                return me.winEl;
            }
            return null;
        },

        addToCache: function(el, id) {
            if (el) {
                Ext.addCacheEntry(id, el);
            }
            return el;
        },

        addMethods: function() {
            this.override.apply(this, arguments);
        },

        
        mergeClsList: function() {
            var clsList, clsHash = {},
                i, length, j, listLength, clsName, result = [],
                changed = false,
                trimRe = this.trimRe,
                whitespaceRe = this.whitespaceRe;

            for (i = 0, length = arguments.length; i < length; i++) {
                clsList = arguments[i];
                if (Ext.isString(clsList)) {
                    clsList = clsList.replace(trimRe, '').split(whitespaceRe);
                }
                if (clsList) {
                    for (j = 0, listLength = clsList.length; j < listLength; j++) {
                        clsName = clsList[j];
                        if (!clsHash[clsName]) {
                            if (i) {
                                changed = true;
                            }
                            clsHash[clsName] = true;
                        }
                    }
                }
            }

            for (clsName in clsHash) {
                result.push(clsName);
            }
            result.changed = changed;
            return result;
        },

        
        removeCls: function(existingClsList, removeClsList) {
            var clsHash = {},
                i, length, clsName, result = [],
                changed = false,
                whitespaceRe = this.whitespaceRe;

            if (existingClsList) {
                if (Ext.isString(existingClsList)) {
                    existingClsList = existingClsList.replace(this.trimRe, '').split(whitespaceRe);
                }
                for (i = 0, length = existingClsList.length; i < length; i++) {
                    clsHash[existingClsList[i]] = true;
                }
            }
            if (removeClsList) {
                if (Ext.isString(removeClsList)) {
                    removeClsList = removeClsList.split(whitespaceRe);
                }
                for (i = 0, length = removeClsList.length; i < length; i++) {
                    clsName = removeClsList[i];
                    if (clsHash[clsName]) {
                        changed = true;
                        delete clsHash[clsName];
                    }
                }
            }
            for (clsName in clsHash) {
                result.push(clsName);
            }
            result.changed = changed;
            return result;
        },

        
        VISIBILITY: 1,

        
        DISPLAY: 2,

        
        OFFSETS: 3,

        
        ASCLASS: 4
    },

    constructor: function(element, forceNew) {
        var me = this,
            dom = typeof element == 'string'
                ? document.getElementById(element)
                : element,
            id;

        
        
        
        me.el = me;

        if (!dom) {
            return null;
        }

        id = dom.id;
        if (!forceNew && id && Ext.cache[id]) {
            
            return Ext.cache[id].el;
        }

        
        me.dom = dom;

        
        me.id = id || Ext.id(dom);

        me.self.addToCache(me);
    },

    
    set: function(o, useSet) {
         var el = this.dom,
             attr,
             value;

         for (attr in o) {
             if (o.hasOwnProperty(attr)) {
                 value = o[attr];
                 if (attr == 'style') {
                     this.applyStyles(value);
                 }
                 else if (attr == 'cls') {
                     el.className = value;
                 }
                 else if (useSet !== false) {
                     if (value === undefined) {
                         el.removeAttribute(attr);
                     } else {
                        el.setAttribute(attr, value);
                     }
                 }
                 else {
                     el[attr] = value;
                 }
             }
         }
         return this;
     },

    
    defaultUnit: "px",

    
    is: function(simpleSelector) {
        return Ext.DomQuery.is(this.dom, simpleSelector);
    },

    
    getValue: function(asNumber) {
        var val = this.dom.value;
        return asNumber ? parseInt(val, 10) : val;
    },

    
    remove: function() {
        var me = this,
            dom = me.dom;
            
        if (me.isAnimate) {
            me.stopAnimation();
        }

        if (dom) {
            Ext.removeNode(dom);
            delete me.dom;
        }
    },

    
    contains: function(el) {
        if (!el) {
            return false;
        }

        var me = this,
            dom = el.dom || el;

        
        return (dom === me.dom) || Ext.dom.AbstractElement.isAncestor(me.dom, dom);
    },

    
    getAttribute: function(name, ns) {
        var dom = this.dom;
        return dom.getAttributeNS(ns, name) || dom.getAttribute(ns + ":" + name) || dom.getAttribute(name) || dom[name];
    },

    
    update: function(html) {
        if (this.dom) {
            this.dom.innerHTML = html;
        }
        return this;
    },


    
    setHTML: function(html) {
        if(this.dom) {
            this.dom.innerHTML = html;
        }
        return this;
    },

    
    getHTML: function() {
        return this.dom ? this.dom.innerHTML : '';
    },

    
    hide: function() {
        this.setVisible(false);
        return this;
    },

    
    show: function() {
        this.setVisible(true);
        return this;
    },

    
    setVisible: function(visible, animate) {
        var me = this,
            statics = me.self,
            mode = me.getVisibilityMode(),
            prefix = Ext.baseCSSPrefix;

        switch (mode) {
            case statics.VISIBILITY:
                me.removeCls([prefix + 'hidden-display', prefix + 'hidden-offsets']);
                me[visible ? 'removeCls' : 'addCls'](prefix + 'hidden-visibility');
            break;

            case statics.DISPLAY:
                me.removeCls([prefix + 'hidden-visibility', prefix + 'hidden-offsets']);
                me[visible ? 'removeCls' : 'addCls'](prefix + 'hidden-display');
            break;

            case statics.OFFSETS:
                me.removeCls([prefix + 'hidden-visibility', prefix + 'hidden-display']);
                me[visible ? 'removeCls' : 'addCls'](prefix + 'hidden-offsets');
            break;
        }

        return me;
    },

    getVisibilityMode: function() {
        
        
        
        var data = (this.$cache || this.getCache()).data,
            visMode = data.visibilityMode;

        if (visMode === undefined) {
            data.visibilityMode = visMode = this.self.DISPLAY;
        }
        
        return visMode;
    },

    
    setVisibilityMode: function(mode) {
        (this.$cache || this.getCache()).data.visibilityMode = mode;
        return this;
    },
    
    getCache: function() {
        var me = this,
            id = me.dom.id || Ext.id(me.dom);

        
        
        
        me.$cache = Ext.cache[id] || Ext.addCacheEntry(id, null, me.dom);
            
        return me.$cache;
    }
},
function() {
    var AbstractElement = this;

    
    Ext.getDetachedBody = function () {
        var detachedEl = AbstractElement.detachedBodyEl;

        if (!detachedEl) {
            detachedEl = document.createElement('div');
            AbstractElement.detachedBodyEl = detachedEl = new AbstractElement.Fly(detachedEl);
            detachedEl.isDetachedBody = true;
        }

        return detachedEl;
    };

    
    Ext.getElementById = function (id) {
        var el = document.getElementById(id),
            detachedBodyEl;

        if (!el && (detachedBodyEl = AbstractElement.detachedBodyEl)) {
            el = detachedBodyEl.dom.querySelector('#' + Ext.escapeId(id));
        }

        return el;
    };

    
    Ext.get = function(el) {
        return Ext.dom.Element.get(el);
    };

    this.addStatics({
        
        Fly: new Ext.Class({
            
            
            
            
            extend: AbstractElement,

            
            isFly: true,

            constructor: function(dom) {
                this.dom = dom;
                
                
                
                this.el = this;
            },

            
            attach: function (dom) {

                
                this.dom = dom;
                
                
                this.$cache = dom.id ? Ext.cache[dom.id] : null;
                return this;
            }
        }),

        _flyweights: {},

        
        fly: function(dom, named) {
            var fly = null,
                _flyweights = AbstractElement._flyweights;

            named = named || '_global';

            dom = Ext.getDom(dom);

            if (dom) {
                fly = _flyweights[named] || (_flyweights[named] = new AbstractElement.Fly());

                
                
                fly.dom = dom;
                
                
                fly.$cache = dom.id ? Ext.cache[dom.id] : null;
            }
            return fly;
        }
    });

    
    Ext.fly = function() {
        return AbstractElement.fly.apply(AbstractElement, arguments);
    };

    (function (proto) {
        
        proto.destroy = proto.remove;

        
        if (document.querySelector) {
            proto.getById = function (id, asDom) {
                
                
                var dom = document.getElementById(id) ||
                    this.dom.querySelector('#'+Ext.escapeId(id));
                return asDom ? dom : (dom ? Ext.get(dom) : null);
            };
        } else {
            proto.getById = function (id, asDom) {
                var dom = document.getElementById(id);
                return asDom ? dom : (dom ? Ext.get(dom) : null);
            };
        }
    }(this.prototype));
});







Ext.define('Ext.dom.Helper', (function() {


var afterbegin = 'afterbegin',
    afterend = 'afterend',
    beforebegin = 'beforebegin',
    beforeend = 'beforeend',
    ts = '<table>',
    te = '</table>',
    tbs = ts+'<tbody>',
    tbe = '</tbody>'+te,
    trs = tbs + '<tr>',
    tre = '</tr>'+tbe,
    detachedDiv = document.createElement('div'),
    bbValues = ['BeforeBegin', 'previousSibling'],
    aeValues = ['AfterEnd', 'nextSibling'],
    bb_ae_PositionHash = {
        beforebegin: bbValues,
        afterend: aeValues
    },
    fullPositionHash = {
        beforebegin: bbValues,
        afterend: aeValues,
        afterbegin: ['AfterBegin', 'firstChild'],
        beforeend: ['BeforeEnd', 'lastChild']
    };


return {
    extend:  Ext.dom.AbstractHelper ,
                                         

    tableRe: /^(?:table|thead|tbody|tr|td)$/i,

    tableElRe: /td|tr|tbody|thead/i,

    
    useDom : false,

    
    createDom: function(o, parentNode){
        var el,
            doc = document,
            useSet,
            attr,
            val,
            cn,
            i, l;

        if (Ext.isArray(o)) {                       
            el = doc.createDocumentFragment(); 
            for (i = 0, l = o.length; i < l; i++) {
                this.createDom(o[i], el);
            }
        } else if (typeof o == 'string') {         
            el = doc.createTextNode(o);
        } else {
            el = doc.createElement(o.tag || 'div');
            useSet = !!el.setAttribute; 
            for (attr in o) {
                if (!this.confRe.test(attr)) {
                    val = o[attr];
                    if (attr == 'cls') {
                        el.className = val;
                    } else {
                        if (useSet) {
                            el.setAttribute(attr, val);
                        } else {
                            el[attr] = val;
                        }
                    }
                }
            }
            Ext.DomHelper.applyStyles(el, o.style);

            if ((cn = o.children || o.cn)) {
                this.createDom(cn, el);
            } else if (o.html) {
                el.innerHTML = o.html;
            }
        }
        if (parentNode) {
            parentNode.appendChild(el);
        }
        return el;
    },

    ieTable: function(depth, openingTags, htmlContent, closingTags){
        detachedDiv.innerHTML = [openingTags, htmlContent, closingTags].join('');

        var i = -1,
            el = detachedDiv,
            ns;

        while (++i < depth) {
            el = el.firstChild;
        }
        
        ns = el.nextSibling;

        if (ns) {
            ns = el;
            el = document.createDocumentFragment();
            
            while (ns) {
                 nx = ns.nextSibling;
                 el.appendChild(ns);
                 ns = nx;
            }
        }
        return el;
    },

    
    insertIntoTable: function(tag, where, destinationEl, html) {
        var node,
            before,
            bb = where == beforebegin,
            ab = where == afterbegin,
            be = where == beforeend,
            ae = where == afterend;

        if (tag == 'td' && (ab || be) || !this.tableElRe.test(tag) && (bb || ae)) {
            return null;
        }
        before = bb ? destinationEl :
                 ae ? destinationEl.nextSibling :
                 ab ? destinationEl.firstChild : null;

        if (bb || ae) {
            destinationEl = destinationEl.parentNode;
        }

        if (tag == 'td' || (tag == 'tr' && (be || ab))) {
            node = this.ieTable(4, trs, html, tre);
        } else if (((tag == 'tbody' || tag == 'thead') && (be || ab)) ||
                (tag == 'tr' && (bb || ae))) {
            node = this.ieTable(3, tbs, html, tbe);
        } else {
            node = this.ieTable(2, ts, html, te);
        }
        destinationEl.insertBefore(node, before);
        return node;
    },

    
    createContextualFragment: function(html) {
        var fragment = document.createDocumentFragment(),
            length, childNodes;

        detachedDiv.innerHTML = html;
        childNodes = detachedDiv.childNodes;
        length = childNodes.length;

        
        while (length--) {
            fragment.appendChild(childNodes[0]);
        }
        return fragment;
    },

    applyStyles: function(el, styles) {
        if (styles) {
            if (typeof styles == "function") {
                styles = styles.call();
            }
            if (typeof styles == "string") {
                styles = Ext.dom.Element.parseStyles(styles);
            }
            if (typeof styles == "object") {
                Ext.fly(el, '_applyStyles').setStyle(styles);
            }
        }
    },

    
    createHtml: function(spec) {
        return this.markup(spec);
    },

    doInsert: function(el, o, returnElement, pos, sibling, append) {
        
        el = el.dom || Ext.getDom(el);

        var newNode;

        if (this.useDom) {
            newNode = this.createDom(o, null);

            if (append) {
                el.appendChild(newNode);
            }
            else {
                (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
            }

        } else {
            newNode = this.insertHtml(pos, el, this.markup(o));
        }
        return returnElement ? Ext.get(newNode, true) : newNode;
    },

    
    overwrite: function(el, html, returnElement) {
        var newNode;

        el = Ext.getDom(el);
        html = this.markup(html);

        
        if (Ext.isIE && this.tableRe.test(el.tagName)) {
            
            while (el.firstChild) {
                el.removeChild(el.firstChild);
            }
            if (html) {
                newNode = this.insertHtml('afterbegin', el, html);
                return returnElement ? Ext.get(newNode) : newNode;
            }
            return null;
        }
        el.innerHTML = html;
        return returnElement ? Ext.get(el.firstChild) : el.firstChild;
    },

    insertHtml: function(where, el, html) {
        var hashVal,
            range,
            rangeEl,
            setStart,
            frag;

        where = where.toLowerCase();

        
        if (el.insertAdjacentHTML) {

            
            if (Ext.isIE && this.tableRe.test(el.tagName) && (frag = this.insertIntoTable(el.tagName.toLowerCase(), where, el, html))) {
                return frag;
            }

            if ((hashVal = fullPositionHash[where])) {

                if (Ext.global.MSApp && Ext.global.MSApp.execUnsafeLocalFunction) {
                    
                    MSApp.execUnsafeLocalFunction(function () {
                        el.insertAdjacentHTML(hashVal[0], html);
                    });
                } else {
                    el.insertAdjacentHTML(hashVal[0], html);
                }

                return el[hashVal[1]];
            }
            
        } else {
            
            if (el.nodeType === 3) {
                where = where === 'afterbegin' ? 'beforebegin' : where;
                where = where === 'beforeend' ? 'afterend' : where;
            }
            range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
            setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before');
            if (bb_ae_PositionHash[where]) {
                if (range) {
                    range[setStart](el);
                    frag = range.createContextualFragment(html);
                } else {
                    frag = this.createContextualFragment(html);
                }
                el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
                return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
            } else {
                rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
                if (el.firstChild) {
                    if (range) {
                        range[setStart](el[rangeEl]);
                        frag = range.createContextualFragment(html);
                    } else {
                        frag = this.createContextualFragment(html);
                    }

                    if (where == afterbegin) {
                        el.insertBefore(frag, el.firstChild);
                    } else {
                        el.appendChild(frag);
                    }
                } else {
                    el.innerHTML = html;
                }
                return el[rangeEl];
            }
        }
    },

    
    createTemplate: function(o) {
        var html = this.markup(o);
        return new Ext.Template(html);
    }

};
})(), function() {
    Ext.ns('Ext.core');
    Ext.DomHelper = Ext.core.DomHelper = new this;
});



Ext.define('Ext.Template', {

    

                                                    

    inheritableStatics: {
        
        from: function(el, config) {
            el = Ext.getDom(el);
            return new this(el.value || el.innerHTML, config || '');
        }
    },

    

    
    constructor: function(html) {
        var me = this,
            args = arguments,
            buffer = [],
            i = 0,
            length = args.length,
            value;

        me.initialConfig = {};
        
        
        
        
        if (length === 1 && Ext.isArray(html)) {
            args = html;
            length = args.length;
        }

        if (length > 1) {
            for (; i < length; i++) {
                value = args[i];
                if (typeof value == 'object') {
                    Ext.apply(me.initialConfig, value);
                    Ext.apply(me, value);
                } else {
                    buffer.push(value);
                }
            }
        } else {
            buffer.push(html);
        }

        
        me.html = buffer.join('');

        if (me.compiled) {
            me.compile();
        }
    },

    
    isTemplate: true,

    

    
    disableFormats: false,

    re: /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,

    
    apply: function(values) {
        var me = this,
            useFormat = me.disableFormats !== true,
            fm = Ext.util.Format,
            tpl = me,
            ret;

        if (me.compiled) {
            return me.compiled(values).join('');
        }

        function fn(m, name, format, args) {
            if (format && useFormat) {
                if (args) {
                    args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')());
                } else {
                    args = [values[name]];
                }
                if (format.substr(0, 5) == "this.") {
                    return tpl[format.substr(5)].apply(tpl, args);
                }
                else {
                    return fm[format].apply(fm, args);
                }
            }
            else {
                return values[name] !== undefined ? values[name] : "";
            }
        }

        ret = me.html.replace(me.re, fn);
        return ret;
    },

    
    applyOut: function(values, out) {
        var me = this;

        if (me.compiled) {
            out.push.apply(out, me.compiled(values));
        } else {
            out.push(me.apply(values));
        }

        return out;
    },

    
    applyTemplate: function () {
        return this.apply.apply(this, arguments);
    },

    
    set: function(html, compile) {
        var me = this;
        me.html = html;
        me.compiled = null;
        return compile ? me.compile() : me;
    },

    compileARe: /\\/g,
    compileBRe: /(\r\n|\n)/g,
    compileCRe: /'/g,

    /**
     * Compiles the template into an internal function, eliminating the RegEx overhead.
     * @return {Ext.Template} this
     */
    compile: function() {
        var me = this,
            fm = Ext.util.Format,
            useFormat = me.disableFormats !== true,
            body, bodyReturn;

        function fn(m, name, format, args) {
            if (format && useFormat) {
                args = args ? ',' + args: "";
                if (format.substr(0, 5) != "this.") {
                    format = "fm." + format + '(';
                }
                else {
                    format = 'this.' + format.substr(5) + '(';
                }
            }
            else {
                args = '';
                format = "(values['" + name + "'] == undefined ? '' : ";
            }
            return "'," + format + "values['" + name + "']" + args + ") ,'";
        }

        bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn);
        body = "this.compiled = function(values){ return ['" + bodyReturn + "'];};";
        eval(body);
        return me;
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    insertFirst: function(el, values, returnElement) {
        return this.doInsert('afterBegin', el, values, returnElement);
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) before el.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    insertBefore: function(el, values, returnElement) {
        return this.doInsert('beforeBegin', el, values, returnElement);
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) after el.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    insertAfter: function(el, values, returnElement) {
        return this.doInsert('afterEnd', el, values, returnElement);
    },

    /**
     * Applies the supplied `values` to the template and appends the new node(s) to the specified `el`.
     *
     * For example usage see {@link Ext.Template Ext.Template class docs}.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return an Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    append: function(el, values, returnElement) {
        return this.doInsert('beforeEnd', el, values, returnElement);
    },

    doInsert: function(where, el, values, returnElement) {
        var newNode = Ext.DomHelper.insertHtml(where, Ext.getDom(el), this.apply(values));
        return returnElement ? Ext.get(newNode) : newNode;
    },

    /**
     * Applies the supplied values to the template and overwrites the content of el with the new node(s).
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    overwrite: function(el, values, returnElement) {
        var newNode = Ext.DomHelper.overwrite(Ext.getDom(el), this.apply(values));
        return returnElement ? Ext.get(newNode) : newNode;
    }
});

// @tag core
/**
 * This class parses the XTemplate syntax and calls abstract methods to process the parts.
 * @private
 */
Ext.define('Ext.XTemplateParser', {
    constructor: function (config) {
        Ext.apply(this, config);
    },

    /**
     * @property {Number} level The 'for' or 'foreach' loop context level. This is adjusted
     * up by one prior to calling {@link #doFor} or {@link #doForEach} and down by one after
     * calling the corresponding {@link #doEnd} that closes the loop. This will be 1 on the
     * first {@link #doFor} or {@link #doForEach} call.
     */

    /**
     * This method is called to process a piece of raw text from the tpl.
     * @param {String} text
     * @method doText
     */
    // doText: function (text)

    /**
     * This method is called to process expressions (like `{[expr]}`).
     * @param {String} expr The body of the expression (inside "{[" and "]}").
     * @method doExpr
     */
    // doExpr: function (expr)

    /**
     * This method is called to process simple tags (like `{tag}`).
     * @method doTag
     */
    // doTag: function (tag)

    /**
     * This method is called to process `<tpl else>`.
     * @method doElse
     */
    // doElse: function ()

    /**
     * This method is called to process `{% text %}`.
     * @param {String} text
     * @method doEval
     */
    // doEval: function (text)

    /**
     * This method is called to process `<tpl if="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doIf
     */
    // doIf: function (action, actions)

    /**
     * This method is called to process `<tpl elseif="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doElseIf
     */
    // doElseIf: function (action, actions)

    /**
     * This method is called to process `<tpl switch="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doSwitch
     */
    // doSwitch: function (action, actions)

    /**
     * This method is called to process `<tpl case="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doCase
     */
    // doCase: function (action, actions)

    /**
     * This method is called to process `<tpl default>`.
     * @method doDefault
     */
    // doDefault: function ()

    /**
     * This method is called to process `</tpl>`. It is given the action type that started
     * the tpl and the set of additional actions.
     * @param {String} type The type of action that is being ended.
     * @param {Object} actions The other actions keyed by the attribute name (such as 'exec').
     * @method doEnd
     */
    // doEnd: function (type, actions) 

    /**
     * This method is called to process `<tpl for="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doFor
     */
    // doFor: function (action, actions)

    /**
     * This method is called to process `<tpl foreach="action">`. If there are other
     * attributes, these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name (such as 'exec').
     * @method doForEach
     */
    // doForEach: function (action, actions)

    /**
     * This method is called to process `<tpl exec="action">`. If there are other attributes,
     * these are passed in the actions object.
     * @param {String} action
     * @param {Object} actions Other actions keyed by the attribute name.
     * @method doExec
     */
    // doExec: function (action, actions)

    /**
     * This method is called to process an empty `<tpl>`. This is unlikely to need to be
     * implemented, so a default (do nothing) version is provided.
     * @method
     */
    doTpl: Ext.emptyFn,

    parse: function (str) {
        var me = this,
            len = str.length,
            aliases = { elseif: 'elif' },
            topRe = me.topRe,
            actionsRe = me.actionsRe,
            index, stack, s, m, t, prev, frame, subMatch, begin, end, actions,
            prop;

        me.level = 0;
        me.stack = stack = [];

        for (index = 0; index < len; index = end) {
            topRe.lastIndex = index;
            m = topRe.exec(str);

            if (!m) {
                me.doText(str.substring(index, len));
                break;
            }

            begin = m.index;
            end = topRe.lastIndex;

            if (index < begin) {
                me.doText(str.substring(index, begin));
            }

            if (m[1]) {
                end = str.indexOf('%}', begin+2);
                me.doEval(str.substring(begin+2, end));
                end += 2;
            } else if (m[2]) {
                end = str.indexOf(']}', begin+2);
                me.doExpr(str.substring(begin+2, end));
                end += 2;
            } else if (m[3]) { // if ('{' token)
                me.doTag(m[3]);
            } else if (m[4]) { // content of a <tpl xxxxxx xxx> tag
                actions = null;
                while ((subMatch = actionsRe.exec(m[4])) !== null) {
                    s = subMatch[2] || subMatch[3];
                    if (s) {
                        s = Ext.String.htmlDecode(s); // decode attr value
                        t = subMatch[1];
                        t = aliases[t] || t;
                        actions = actions || {};
                        prev = actions[t];

                        if (typeof prev == 'string') {
                            actions[t] = [prev, s];
                        } else if (prev) {
                            actions[t].push(s);
                        } else {
                            actions[t] = s;
                        }
                    }
                }

                if (!actions) {
                    if (me.elseRe.test(m[4])) {
                        me.doElse();
                    } else if (me.defaultRe.test(m[4])) {
                        me.doDefault();
                    } else {
                        me.doTpl();
                        stack.push({ type: 'tpl' });
                    }
                }
                else if (actions['if']) {
                    me.doIf(actions['if'], actions);
                    stack.push({ type: 'if' });
                }
                else if (actions['switch']) {
                    me.doSwitch(actions['switch'], actions);
                    stack.push({ type: 'switch' });
                }
                else if (actions['case']) {
                    me.doCase(actions['case'], actions);
                }
                else if (actions['elif']) {
                    me.doElseIf(actions['elif'], actions);
                }
                else if (actions['for']) {
                    ++me.level;

                    // Extract property name to use from indexed item
                    if (prop = me.propRe.exec(m[4])) {
                        actions.propName = prop[1] || prop[2];
                    }
                    me.doFor(actions['for'], actions);
                    stack.push({ type: 'for', actions: actions });
                }
                else if (actions['foreach']) {
                    ++me.level;

                    // Extract property name to use from indexed item
                    if (prop = me.propRe.exec(m[4])) {
                        actions.propName = prop[1] || prop[2];
                    }
                    me.doForEach(actions['foreach'], actions);
                    stack.push({ type: 'foreach', actions: actions });
                }
                else if (actions.exec) {
                    me.doExec(actions.exec, actions);
                    stack.push({ type: 'exec', actions: actions });
                }
                /*
                else {
                    // todo - error
                }
                */
            } else if (m[0].length === 5) {
                // if the length of m[0] is 5, assume that we're dealing with an opening tpl tag with no attributes (e.g. <tpl>...</tpl>)
                // in this case no action is needed other than pushing it on to the stack
                stack.push({ type: 'tpl' });
            } else {
                frame = stack.pop();
                me.doEnd(frame.type, frame.actions);
                if (frame.type == 'for' || frame.type == 'foreach') {
                    --me.level;
                }
            }
        }
    },

    // Internal regexes
    
    topRe:     /(?:(\{\%)|(\{\[)|\{([^{}]+)\})|(?:<tpl([^>]*)\>)|(?:<\/tpl>)/g,
    actionsRe: /\s*(elif|elseif|if|for|foreach|exec|switch|case|eval|between)\s*\=\s*(?:(?:"([^"]*)")|(?:'([^']*)'))\s*/g,
    propRe:    /prop=(?:(?:"([^"]*)")|(?:'([^']*)'))/,
    defaultRe: /^\s*default\s*$/,
    elseRe:    /^\s*else\s*$/
});



Ext.define('Ext.XTemplateCompiler', {
    extend:  Ext.XTemplateParser ,

    
    
    
    useEval: Ext.isGecko,

    
    
    
    useIndex: Ext.isIE8m,

    useFormat: true,
    
    propNameRe: /^[\w\d\$]*$/,

    compile: function (tpl) {
        var me = this,
            code = me.generate(tpl);

        
        
        
        
        return me.useEval ? me.evalTpl(code) : (new Function('Ext', code))(Ext);
    },

    generate: function (tpl) {
        var me = this,
            
            definitions = 'var fm=Ext.util.Format,ts=Object.prototype.toString;',
            code;

        
        me.maxLevel = 0;

        me.body = [
            'var c0=values, a0=' + me.createArrayTest(0) + ', p0=parent, n0=xcount, i0=xindex, k0, v;\n'
        ];
        if (me.definitions) {
            if (typeof me.definitions === 'string') {
                me.definitions = [me.definitions, definitions ];
            } else {
                me.definitions.push(definitions);
            }
        } else {
            me.definitions = [ definitions ];
        }
        me.switches = [];

        me.parse(tpl);

        me.definitions.push(
            (me.useEval ? '$=' : 'return') + ' function (' + me.fnArgs + ') {',
                me.body.join(''),
            '}'
        );

        code = me.definitions.join('\n');

        
        me.definitions.length = me.body.length = me.switches.length = 0;
        delete me.definitions;
        delete me.body;
        delete me.switches;

        return code;
    },

    
    

    doText: function (text) {
        var me = this,
            out = me.body;

        text = text.replace(me.aposRe, "\\'").replace(me.newLineRe, '\\n');
        if (me.useIndex) {
            out.push('out[out.length]=\'', text, '\'\n');
        } else {
            out.push('out.push(\'', text, '\')\n');
        }
    },

    doExpr: function (expr) {
        var out = this.body;
        out.push('if ((v=' + expr + ') != null) out');

        
        
        if (this.useIndex) {
             out.push('[out.length]=v+\'\'\n');
        } else {
             out.push('.push(v+\'\')\n');
        }
    },

    doTag: function (tag) {
        var expr = this.parseTag(tag);
        if (expr) {
            this.doExpr(expr);
        } else {
            
            this.doText('{' + tag + '}');
        }
    },

    doElse: function () {
        this.body.push('} else {\n');
    },

    doEval: function (text) {
        this.body.push(text, '\n');
    },

    doIf: function (action, actions) {
        var me = this;

        
        if (action === '.') {
            me.body.push('if (values) {\n');
        } else if (me.propNameRe.test(action)) {
            me.body.push('if (', me.parseTag(action), ') {\n');
        }
        
        else {
            me.body.push('if (', me.addFn(action), me.callFn, ') {\n');
        }
        if (actions.exec) {
            me.doExec(actions.exec);
        }
    },

    doElseIf: function (action, actions) {
        var me = this;

        
        if (action === '.') {
            me.body.push('else if (values) {\n');
        } else if (me.propNameRe.test(action)) {
            me.body.push('} else if (', me.parseTag(action), ') {\n');
        }
        
        else {
            me.body.push('} else if (', me.addFn(action), me.callFn, ') {\n');
        }
        if (actions.exec) {
            me.doExec(actions.exec);
        }
    },

    doSwitch: function (action) {
        var me = this;

        
        if (action === '.') {
            me.body.push('switch (values) {\n');
        } else if (me.propNameRe.test(action)) {
            me.body.push('switch (', me.parseTag(action), ') {\n');
        }
        
        else {
            me.body.push('switch (', me.addFn(action), me.callFn, ') {\n');
        }
        me.switches.push(0);
    },

    doCase: function (action) {
        var me = this,
            cases = Ext.isArray(action) ? action : [action],
            n = me.switches.length - 1,
            match, i;

        if (me.switches[n]) {
            me.body.push('break;\n');
        } else {
            me.switches[n]++;
        }

        for (i = 0, n = cases.length; i < n; ++i) {
            match = me.intRe.exec(cases[i]);
            cases[i] = match ? match[1] : ("'" + cases[i].replace(me.aposRe,"\\'") + "'");
        }

        me.body.push('case ', cases.join(': case '), ':\n');
    },

    doDefault: function () {
        var me = this,
            n = me.switches.length - 1;

        if (me.switches[n]) {
            me.body.push('break;\n');
        } else {
            me.switches[n]++;
        }

        me.body.push('default:\n');
    },

    doEnd: function (type, actions) {
        var me = this,
            L = me.level-1;

        if (type == 'for' || type == 'foreach') {
            
            if (actions.exec) {
                me.doExec(actions.exec);
            }

            me.body.push('}\n');
            me.body.push('parent=p',L,';values=r',L+1,';xcount=n'+L+';xindex=i',L,'+1;xkey=k',L,';\n');
        } else if (type == 'if' || type == 'switch') {
            me.body.push('}\n');
        }
    },

    doFor: function (action, actions) {
        var me = this,
            s,
            L = me.level,
            up = L-1,
            parentAssignment;

        
        if (action === '.') {
            s = 'values';
        } else if (me.propNameRe.test(action)) {
            s = me.parseTag(action);
        }
        
        else {
            s = me.addFn(action) + me.callFn;
        }

        

        
        if (me.maxLevel < L) {
            me.maxLevel = L;
            me.body.push('var ');
        }
        
        if (action == '.') {
            parentAssignment = 'c' + L;
        } else {
            parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up;
        }
        
        me.body.push('i',L,'=0,n', L, '=0,c',L,'=',s,',a',L,'=', me.createArrayTest(L),',r',L,'=values,p',L,',k',L,';\n',
            'p',L,'=parent=',parentAssignment,'\n',
            'if (c',L,'){if(a',L,'){n', L,'=c', L, '.length;}else if (c', L, '.isMixedCollection){c',L,'=c',L,'.items;n',L,'=c',L,'.length;}else if(c',L,'.isStore){c',L,'=c',L,'.data.items;n',L,'=c',L,'.length;}else{c',L,'=[c',L,'];n',L,'=1;}}\n',
            'for (xcount=n',L,';i',L,'<n'+L+';++i',L,'){\n',
            'values=c',L,'[i',L,']');
        if (actions.propName) {
            me.body.push('.', actions.propName);
        }
        me.body.push('\n',
            'xindex=i',L,'+1\n');
        
        if (actions.between) {
            me.body.push('if(xindex>1){ out.push("',actions.between,'"); } \n');
        }
    },

    doForEach: function (action, actions) {
        var me = this,
            s,
            L = me.level,
            up = L-1,
            parentAssignment;

        
        if (action === '.') {
            s = 'values';
        } else if (me.propNameRe.test(action)) {
            s = me.parseTag(action);
        }
        
        else {
            s = me.addFn(action) + me.callFn;
        }

        

        
        if (me.maxLevel < L) {
            me.maxLevel = L;
            me.body.push('var ');
        }
        
        if (action == '.') {
            parentAssignment = 'c' + L;
        } else {
            parentAssignment = 'a' + up + '?c' + up + '[i' + up + ']:c' + up;
        }
        
        me.body.push('i',L,'=-1,n',L,'=0,c',L,'=',s,',a',L,'=',me.createArrayTest(L),',r',L,'=values,p',L,',k',L,';\n',
            'p',L,'=parent=',parentAssignment,'\n',
            'for(k',L,' in c',L,'){\n',
                'xindex=++i',L,'+1;\n',
                'xkey=k',L,';\n',
                'values=c',L,'[k',L,'];');
        if (actions.propName) {
            me.body.push('.', actions.propName);
        }
        
        if (actions.between) {
            me.body.push('if(xindex>1){ out.push("',actions.between,'"); } \n');
        }
    },

    createArrayTest: ('isArray' in Array) ? function(L) {
        return 'Array.isArray(c' + L + ')';
    } : function(L) {
        return 'ts.call(c' + L + ')==="[object Array]"';
    },

    doExec: function (action, actions) {
        var me = this,
            name = 'f' + me.definitions.length;

        me.definitions.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' try { with(values) {',
                            '  ' + action,
                            ' }} catch(e) {',
                            '}',
                      '}');

        me.body.push(name + me.callFn + '\n');
    },

    
    

    addFn: function (body) {
        var me = this,
            name = 'f' + me.definitions.length;

        if (body === '.') {
            me.definitions.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' return values',
                       '}');
        } else if (body === '..') {
            me.definitions.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' return parent',
                       '}');
        } else {
            me.definitions.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' try { with(values) {',
                            '  return(' + body + ')',
                            ' }} catch(e) {',
                            '}',
                       '}');
        }

        return name;
    },

    parseTag: function (tag) {
        var me = this,
            m = me.tagRe.exec(tag),
            name, format, args, math, v;

        if (!m) {
            return null;
        }

        name = m[1];
        format = m[2];
        args = m[3];
        math = m[4];

        
        if (name == '.') {
            
            if (!me.validTypes) {
                me.definitions.push('var validTypes={string:1,number:1,boolean:1};');
                me.validTypes = true;
            }
            v = 'validTypes[typeof values] || ts.call(values) === "[object Date]" ? values : ""';
        }
        
        else if (name == '#') {
            v = 'xindex';
        }
        
        else if (name == '$') {
            v = 'xkey';
        }
        else if (name.substr(0, 7) == "parent.") {
            v = name;
        }
        
        else if (isNaN(name) && name.indexOf('-') == -1 && name.indexOf('.') != -1) {
            v = "values." + name;
        }
        
        
        else {    
            v = "values['" + name + "']";
        }

        if (math) {
            v = '(' + v + math + ')';
        }

        if (format && me.useFormat) {
            args = args ? ',' + args : "";
            if (format.substr(0, 5) != "this.") {
                format = "fm." + format + '(';
            } else {
                format += '(';
            }
        } else {
            return v;
        }

        return format + v + args + ')';
    },

    
    evalTpl: function ($) {

        
        
        
        
        eval($);
        return $;
    },

    newLineRe: /\r\n|\r|\n/g,
    aposRe: /[']/g,
    intRe:  /^\s*(\d+)\s*$/,
    tagRe:  /^([\w-\.\#\$]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?$/

}, function () {
    var proto = this.prototype;

    proto.fnArgs = 'out,values,parent,xindex,xcount,xkey';
    proto.callFn = '.call(this,' + proto.fnArgs + ')';
});

// @tag core
/**
 * A template class that supports advanced functionality like:
 *
 * - Autofilling arrays using templates and sub-templates
 * - Conditional processing with basic comparison operators
 * - Basic math function support
 * - Execute arbitrary inline code with special built-in template variables
 * - Custom member functions
 * - Many special tags and built-in operators that aren't defined as part of the API, but are supported in the templates that can be created
 *
 * XTemplate provides the templating mechanism built into {@link Ext.view.View}.
 *
 * The {@link Ext.Template} describes the acceptable parameters to pass to the constructor. The following examples
 * demonstrate all of the supported features.
 *
 * # Sample Data
 *
 * This is the data object used for reference in each code example:
 *
 *     var data = {
 *         name: 'Don Griffin',
 *         title: 'Senior Technomage',
 *         company: 'Sencha Inc.',
 *         drinks: ['Coffee', 'Water', 'More Coffee'],
 *         kids: [
 *             { name: 'Aubrey',  age: 17 },
 *             { name: 'Joshua',  age: 13 },
 *             { name: 'Cale',    age: 10 },
 *             { name: 'Nikol',   age: 5 },
 *             { name: 'Solomon', age: 0 }
 *         ]
 *     };
 *
 * # Auto filling of arrays
 *
 * The **tpl** tag and the **for** operator are used to process the provided data object:
 *
 * - If the value specified in for is an array, it will auto-fill, repeating the template block inside the tpl
 *   tag for each item in the array.
 * - If for="." is specified, the data object provided is examined.
 * - If between="..." is specified, the provided value will be inserted between the items.
 *   This is also supported in the "foreach" looping template.
 * - While processing an array, the special variable {#} will provide the current array index + 1 (starts at 1, not 0).
 *
 * Examples:
 *
 *     <tpl for=".">...</tpl>       
 *     <tpl for="foo">...</tpl>     
 *     <tpl for="foo.bar">...</tpl> 
 *     <tpl for="." between=",">...</tpl> 
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Kids: ',
 *         '<tpl for=".">',       
 *             '<p>{#}. {name}</p>',  
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data.kids); 
 *
 * An example illustrating how the **for** property can be leveraged to access specified members of the provided data
 * object to populate the template:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Title: {title}</p>',
 *         '<p>Company: {company}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',     
 *             '<p>{name}</p>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);  
 *
 * Flat arrays that contain values (and not objects) can be auto-rendered using the special **`{.}`** variable inside a
 * loop. This variable will represent the value of the array at the current index:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>{name}\'s favorite beverages:</p>',
 *         '<tpl for="drinks">',
 *             '<div> - {.}</div>',
 *         '</tpl>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * When processing a sub-template, for example while looping through a child array, you can access the parent object's
 * members via the **parent** object:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',
 *                 '<p>{name}</p>',
 *                 '<p>Dad: {parent.name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *     
 * The **foreach** operator is used to loop over an object's properties.  The following
 * example demonstrates looping over the main data object's properties:
 * 
 *     var tpl = new Ext.XTemplate(
 *         '<dl>',
 *             '<tpl foreach=".">',
 *                 '<dt>{$}</dt>', // the special **`{$}`** variable contains the property name
 *                 '<dd>{.}</dd>', // within the loop, the **`{.}`** variable is set to the property value
 *             '</tpl>',
 *         '</dl>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * # Conditional processing with basic comparison operators
 *
 * The **tpl** tag and the **if** operator are used to provide conditional checks for deciding whether or not to render
 * specific parts of the template.
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',
 *                 '<p>{name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * More advanced conditionals are also supported:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<p>{name} is a ',
 *             '<tpl if="age &gt;= 13">',
 *                 '<p>teenager</p>',
 *             '<tpl elseif="age &gt;= 2">',
 *                 '<p>kid</p>',
 *             '<tpl else>',
 *                 '<p>baby</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<p>{name} is a ',
 *             '<tpl switch="name">',
 *                 '<tpl case="Aubrey" case="Nikol">',
 *                     '<p>girl</p>',
 *                 '<tpl default>',
 *                     '<p>boy</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *
 * A `break` is implied between each case and default, however, multiple cases can be listed
 * in a single &lt;tpl&gt; tag.
 *
 * # Using double quotes
 *
 * Examples:
 *
 *     var tpl = new Ext.XTemplate(
 *         "<tpl if='age &gt; 1 && age &lt; 10'>Child</tpl>",
 *         "<tpl if='age &gt;= 10 && age &lt; 18'>Teenager</tpl>",
 *         "<tpl if='this.isGirl(name)'>...</tpl>",
 *         '<tpl if="id == \'download\'">...</tpl>',
 *         "<tpl if='needsIcon'><img src='{icon}' class='{iconCls}'/></tpl>",
 *         "<tpl if='name == \"Don\"'>Hello</tpl>"
 *     );
 *
 * # Basic math support
 *
 * The following basic math operators may be applied directly on numeric data values:
 *
 *     + - * /
 *
 * For example:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',  
 *                 '<p>{#}: {name}</p>',  
 *                 '<p>In 5 Years: {age+5}</p>',  
 *                 '<p>Dad: {parent.name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * # Execute arbitrary inline code with special built-in template variables
 *
 * Anything between `{[ ... ]}` is considered code to be executed in the scope of the template.
 * The expression is evaluated and the result is included in the generated result. There are
 * some special variables available in that code:
 *
 * - **out**: The output array into which the template is being appended (using `push` to later
 *   `join`).
 * - **values**: The values in the current scope. If you are using scope changing sub-templates,
 *   you can change what values is.
 * - **parent**: The scope (values) of the ancestor template.
 * - **xindex**: If you are in a "for" or "foreach" looping template, the index of the loop you are in (1-based).
 * - **xcount**: If you are in a "for" looping template, the total length of the array you are looping.
 * - **xkey**: If you are in a "foreach" looping template, the key of the current property
 * being examined.
 *
 * This example demonstrates basic row striping using an inline code block and the xindex variable:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
 *             '{name}',
 *             '</div>',
 *         '</tpl></p>'
 *      );
 *
 * Any code contained in "verbatim" blocks (using "{% ... %}") will be inserted directly in
 * the generated code for the template. These blocks are not included in the output. This
 * can be used for simple things like break/continue in a loop, or control structures or
 * method calls (when they don't produce output). The `this` references the template instance.
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '{% if (xindex % 2 === 0) continue; %}',
 *             '{name}',
 *             '{% if (xindex > 100) break; %}',
 *             '</div>',
 *         '</tpl></p>'
 *      );
 *
 * # Template member functions
 *
 * One or more member functions can be specified in a configuration object passed into the XTemplate constructor for
 * more complex processing:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="this.isGirl(name)">',
 *                 '<p>Girl: {name} - {age}</p>',
 *             '<tpl else>',
 *                 '<p>Boy: {name} - {age}</p>',
 *             '</tpl>',
 *             '<tpl if="this.isBaby(age)">',
 *                 '<p>{name} is a baby!</p>',
 *             '</tpl>',
 *         '</tpl></p>',
 *         {
 *             // XTemplate configuration:
 *             disableFormats: true,
 *             // member functions:
 *             isGirl: function(name){
 *                return name == 'Aubrey' || name == 'Nikol';
 *             },
 *             isBaby: function(age){
 *                return age < 1;
 *             }
 *         }
 *     );
 *     tpl.overwrite(panel.body, data);
 */
Ext.define('Ext.XTemplate', {
    extend:  Ext.Template ,

                                      

    /**
     * @private
     */
    emptyObj: {},

    /**
     * @cfg {Boolean} compiled
     * Only applies to {@link Ext.Template}, XTemplates are compiled automatically on the
     * first call to {@link #apply} or {@link #applyOut}.
     * @hide
     */

    /**
     * @cfg {String/Array} definitions
     * Optional. A statement, or array of statements which set up `var`s which may then
     * be accessed within the scope of the generated function.
     */

    apply: function(values, parent) {
        return this.applyOut(values, [], parent).join('');
    },

    applyOut: function(values, out, parent) {
        var me = this,
            compiler;

        if (!me.fn) {
            compiler = new Ext.XTemplateCompiler({
                useFormat: me.disableFormats !== true,
                definitions: me.definitions
            });

            me.fn = compiler.compile(me.html);
        }

        try {
            me.fn(out, values, parent || me.emptyObj, 1, 1);
        } catch (e) {
        }

        return out;
    },

    /**
     * Does nothing. XTemplates are compiled automatically, so this function simply returns this.
     * @return {Ext.XTemplate} this
     */
    compile: function() {
        return this;
    },

    statics: {
        /**
         * Gets an `XTemplate` from an object (an instance of an {@link Ext#define}'d class).
         * Many times, templates are configured high in the class hierarchy and are to be
         * shared by all classes that derive from that base. To further complicate matters,
         * these templates are seldom actual instances but are rather configurations. For
         * example:
         *
         *      Ext.define('MyApp.Class', {
         *          extraCls: 'extra-class',
         *
         *          someTpl: [
         *              '<div class="{%this.emitClass(out)%}"></div>',
         *          {
         *              
         *              emitClass: function(out) {
         *                  out.push(this.owner.extraCls);
         *              }
         *          }]
         *      });
         *
         * The goal being to share that template definition with all instances and even
         * instances of derived classes, until `someTpl` is overridden. This method will
         * "upgrade" these configurations to be real `XTemplate` instances *in place* (to
         * avoid creating one instance per object).
         *
         * The resulting XTemplate will have an `owner` reference injected which refers back
         * to the owning object whether that is an object which has an *own instance*, or a
         * class prototype. Through this link, XTemplate member functions will be able to access
         * prototype properties of its owning class.
         *
         * @param {Object} instance The object from which to get the `XTemplate` (must be
         * an instance of an {@link Ext#define}'d class).
         * @param {String} name The name of the property by which to get the `XTemplate`.
         * @return {Ext.XTemplate} The `XTemplate` instance or null if not found.
         * @protected
         * @static
         */
        getTpl: function (instance, name) {
            var tpl = instance[name], // go for it! 99% of the time we will get it!
                owner;

            if (tpl && !tpl.isTemplate) { // tpl is just a configuration (not an instance)
                // create the template instance from the configuration:
                tpl = Ext.ClassManager.dynInstantiate('Ext.XTemplate', tpl);

                // and replace the reference with the new instance:
                if (instance.hasOwnProperty(name)) { // the tpl is on the instance
                    owner = instance;
                } else { // must be somewhere in the prototype chain
                    for (owner = instance.self.prototype; owner && !owner.hasOwnProperty(name); owner = owner.superclass) {
                    }
                }
                owner[name] = tpl;
                tpl.owner = owner;
            }
            // else !tpl (no such tpl) or the tpl is an instance already... either way, tpl
            // is ready to return

            return tpl || null;
        }
    }
});

// @tag dom,core
// @require Helper.js
// @define Ext.dom.Query
// @define Ext.core.DomQuery
// @define Ext.DomQuery

/*
 * This is code is also distributed under MIT license for use
 * with jQuery and prototype JavaScript libraries.
 */
/**
 * @class Ext.dom.Query
 * @alternateClassName Ext.DomQuery
 * @alternateClassName Ext.core.DomQuery
 * @singleton
 *
 * Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes
 * and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
 *
 * DomQuery supports most of the [CSS3 selectors spec][1], along with some custom selectors and basic XPath.
 *
 * All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example
 * `div.foo:nth-child(odd)[@foo=bar].bar:first` would be a perfectly valid selector. Node filters are processed
 * in the order in which they appear, which allows you to optimize your queries for your document structure.
 *
 * ## Element Selectors:
 *
 *   - **`*`** any element
 *   - **`E`** an element with the tag E
 *   - **`E F`** All descendent elements of E that have the tag F
 *   - **`E > F`** or **E/F** all direct children elements of E that have the tag F
 *   - **`E + F`** all elements with the tag F that are immediately preceded by an element with the tag E
 *   - **`E ~ F`** all elements with the tag F that are preceded by a sibling element with the tag E
 *
 * ## Attribute Selectors:
 *
 * The use of `@` and quotes are optional. For example, `div[@foo='bar']` is also a valid attribute selector.
 *
 *   - **`E[foo]`** has an attribute "foo"
 *   - **`E[foo=bar]`** has an attribute "foo" that equals "bar"
 *   - **`E[foo^=bar]`** has an attribute "foo" that starts with "bar"
 *   - **`E[foo$=bar]`** has an attribute "foo" that ends with "bar"
 *   - **`E[foo*=bar]`** has an attribute "foo" that contains the substring "bar"
 *   - **`E[foo%=2]`** has an attribute "foo" that is evenly divisible by 2
 *   - **`E[foo!=bar]`** attribute "foo" does not equal "bar"
 *
 * ## Pseudo Classes:
 *
 *   - **`E:first-child`** E is the first child of its parent
 *   - **`E:last-child`** E is the last child of its parent
 *   - **`E:nth-child(_n_)`** E is the _n_th child of its parent (1 based as per the spec)
 *   - **`E:nth-child(odd)`** E is an odd child of its parent
 *   - **`E:nth-child(even)`** E is an even child of its parent
 *   - **`E:only-child`** E is the only child of its parent
 *   - **`E:checked`** E is an element that is has a checked attribute that is true (e.g. a radio or checkbox)
 *   - **`E:first`** the first E in the resultset
 *   - **`E:last`** the last E in the resultset
 *   - **`E:nth(_n_)`** the _n_th E in the resultset (1 based)
 *   - **`E:odd`** shortcut for :nth-child(odd)
 *   - **`E:even`** shortcut for :nth-child(even)
 *   - **`E:contains(foo)`** E's innerHTML contains the substring "foo"
 *   - **`E:nodeValue(foo)`** E contains a textNode with a nodeValue that equals "foo"
 *   - **`E:not(S)`** an E element that does not match simple selector S
 *   - **`E:has(S)`** an E element that has a descendent that matches simple selector S
 *   - **`E:next(S)`** an E element whose next sibling matches simple selector S
 *   - **`E:prev(S)`** an E element whose previous sibling matches simple selector S
 *   - **`E:any(S1|S2|S2)`** an E element which matches any of the simple selectors S1, S2 or S3
 *   - **`E:visible(true)`** an E element which is deeply visible according to {@link Ext.dom.Element#isVisible}
 *
 * ## CSS Value Selectors:
 *
 *   - **`E{display=none}`** css value "display" that equals "none"
 *   - **`E{display^=none}`** css value "display" that starts with "none"
 *   - **`E{display$=none}`** css value "display" that ends with "none"
 *   - **`E{display*=none}`** css value "display" that contains the substring "none"
 *   - **`E{display%=2}`** css value "display" that is evenly divisible by 2
 *   - **`E{display!=none}`** css value "display" that does not equal "none"
 * 
 * ## XML Namespaces:
 *   - **`ns|E`** an element with tag E and namespace prefix ns
 *
 * [1]: http:
 */
Ext.ns('Ext.core');

Ext.dom.Query = Ext.core.DomQuery = Ext.DomQuery = (function() {
    var DQ,
        doc = document,
        cache = {},
        simpleCache = {},
        valueCache = {},
        useClassList = !!doc.documentElement.classList,
        useElementPointer = !!doc.documentElement.firstElementChild,
        useChildrenCollection = (function() {
            var d = doc.createElement('div');
            d.innerHTML = '<!-- -->text<!-- -->';
            return d.children && (d.children.length === 0);
        })(),
        nonSpace = /\S/,
        trimRe = /^\s+|\s+$/g,
        tplRe = /\{(\d+)\}/g,
        modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
        tagTokenRe = /^(#)?([\w\-\*\|\\]+)/,
        nthRe = /(\d*)n\+?(\d*)/,
        nthRe2 = /\D/,
        startIdRe = /^\s*#/,
        
        
        
        isIE = window.ActiveXObject ? true : false,
        key = 30803,
        longHex = /\\([0-9a-fA-F]{6})/g,
        shortHex = /\\([0-9a-fA-F]{1,6})\s{0,1}/g,
        nonHex = /\\([^0-9a-fA-F]{1})/g,
        escapes = /\\/g,
        num, hasEscapes,
        
        
        supportsColonNsSeparator = (function () {
            var xmlDoc,
                xmlString = '<r><a:b xmlns:a="n"></a:b></r>';

            if (window.DOMParser) {
                xmlDoc = (new DOMParser()).parseFromString(xmlString, "application/xml");
            } else {
                xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
                xmlDoc.loadXML(xmlString);
            }

            return !!xmlDoc.getElementsByTagName('a:b').length;
        })(),

        
        
        longHexToChar = function($0, $1) {
            return String.fromCharCode(parseInt($1, 16));
        },

        
        shortToLongHex = function($0, $1) {
            while ($1.length < 6) {
                $1 = '0' + $1;
            }
            return '\\' + $1;
        },

        
        charToLongHex = function($0, $1) {
            num = $1.charCodeAt(0).toString(16);
            if (num.length === 1) {
                num = '0' + num;
            }
            return '\\0000' + num;
        },

        
        
        
        unescapeCssSelector = function(selector) {
            return (hasEscapes) ? selector.replace(longHex, longHexToChar) : selector;
        },

        
        setupEscapes = function(path) {
            hasEscapes = (path.indexOf('\\') > -1);
            if (hasEscapes) {
                path = path
                    .replace(shortHex, shortToLongHex)
                    .replace(nonHex, charToLongHex)
                    .replace(escapes, '\\\\');  
            }
            return path;
        };

    
    
    eval("var batch = 30803, child, next, prev, byClassName;");

    
    
    child = useChildrenCollection ?
        function child(parent, index) {
            return parent.children[index];
        } :
        function child(parent, index) {
            var i = 0,
                n = parent.firstChild;
            while (n) {
                if (n.nodeType == 1) {
                    if (++i == index) {
                        return n;
                    }
                }
                n = n.nextSibling;
            }
            return null;
        };

    
    next = useElementPointer ?
        function(n) {
            return n.nextElementSibling;
        } :
        function(n) {
            while ((n = n.nextSibling) && n.nodeType != 1);
            return n;
        };

    
    prev = useElementPointer ?
        function(n) {
            return n.previousElementSibling;
        } :
        function(n) {
            while ((n = n.previousSibling) && n.nodeType != 1);
            return n;
        };

    
    
    function children(parent) {
        var n = parent.firstChild,
            nodeIndex = -1,
            nextNode;

        while (n) {
            nextNode = n.nextSibling;
            
            if (n.nodeType == 3 && !nonSpace.test(n.nodeValue)) {
                parent.removeChild(n);
            } else {
                
                n.nodeIndex = ++nodeIndex;
            }
            n = nextNode;
        }
        return this;
    }

    
    
    byClassName = useClassList ? 
        function (nodeSet, cls) {
            cls = unescapeCssSelector(cls);
            if (!cls) {
                return nodeSet;
            }
            var result = [], ri = -1,
                i, ci, classList;

            for (i = 0; ci = nodeSet[i]; i++) {
                classList = ci.classList;
                if (classList) {
                    if (classList.contains(cls)) {
                        result[++ri] = ci;
                    }
                } else if ((' ' + ci.className + ' ').indexOf(cls) !== -1) {
                    
                    
                    result[++ri] = ci;
                }
            }
            return result;
        } :
        function (nodeSet, cls) {
            cls = unescapeCssSelector(cls);
            if (!cls) {
                return nodeSet;
            }
            var result = [], ri = -1,
                i, ci;

            for (i = 0; ci = nodeSet[i]; i++) {
                if ((' ' + ci.className + ' ').indexOf(cls) !== -1) {
                    result[++ri] = ci;
                }
            }
            return result;
        };

    function attrValue(n, attr) {
        
        if (!n.tagName && typeof n.length != "undefined") {
            n = n[0];
        }
        if (!n) {
            return null;
        }

        if (attr == "for") {
            return n.htmlFor;
        }
        if (attr == "class" || attr == "className") {
            return n.className;
        }
        return n.getAttribute(attr) || n[attr];

    }

    
    
    
    function getNodes(ns, mode, tagName) {
        var result = [], ri = -1, cs,
            i, ni, j, ci, cn, utag, n, cj;
        if (!ns) {
            return result;
        }
        tagName = tagName.replace('|', ':') || "*";
        
        if (typeof ns.getElementsByTagName != "undefined") {
            ns = [ns];
        }

        
        
        if (!mode) {
            tagName = unescapeCssSelector(tagName);
            if (!supportsColonNsSeparator && DQ.isXml(ns[0]) &&
                tagName.indexOf(':') !== -1) {
                
                
                
                
                
                
                for (i = 0; ni = ns[i]; i++) {
                    cs = ni.getElementsByTagName(tagName.split(':').pop());
                    for (j = 0; ci = cs[j]; j++) {
                        if (ci.tagName === tagName) {
                            result[++ri] = ci;
                        }
                    }
                }
            } else {
                for (i = 0; ni = ns[i]; i++) {
                    cs = ni.getElementsByTagName(tagName);
                    for (j = 0; ci = cs[j]; j++) {
                        result[++ri] = ci;
                    }
                }
            }
            
            
        } else if (mode == "/" || mode == ">") {
            utag = tagName.toUpperCase();
            for (i = 0; ni = ns[i]; i++) {
                cn = ni.childNodes;
                for (j = 0; cj = cn[j]; j++) {
                    if (cj.nodeName == utag || cj.nodeName == tagName || tagName == '*') {
                        result[++ri] = cj;
                    }
                }
            }
            
            
        } else if (mode == "+") {
            utag = tagName.toUpperCase();
            for (i = 0; n = ns[i]; i++) {
                while ((n = n.nextSibling) && n.nodeType != 1);
                if (n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')) {
                    result[++ri] = n;
                }
            }
            
            
        } else if (mode == "~") {
            utag = tagName.toUpperCase();
            for (i = 0; n = ns[i]; i++) {
                while ((n = n.nextSibling)) {
                    if (n.nodeName == utag || n.nodeName == tagName || tagName == '*') {
                        result[++ri] = n;
                    }
                }
            }
        }
        return result;
    }

    function concat(a, b) {
        a.push.apply(a, b);
        return a;
    }

    function byTag(cs, tagName) {
        if (cs.tagName || cs === doc) {
            cs = [cs];
        }
        if (!tagName) {
            return cs;
        }
        var result = [], ri = -1,
            i, ci;
        tagName = tagName.toLowerCase();
        for (i = 0; ci = cs[i]; i++) {
            if (ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName) {
                result[++ri] = ci;
            }
        }
        return result;
    }

    function byId(cs, id) {
        id = unescapeCssSelector(id);
        if (cs.tagName || cs === doc) {
            cs = [cs];
        }
        if (!id) {
            return cs;
        }
        var result = [], ri = -1,
            i, ci;
        for (i = 0; ci = cs[i]; i++) {
            if (ci && ci.id == id) {
                result[++ri] = ci;
                return result;
            }
        }
        return result;
    }

    
    
    function byAttribute(cs, attr, value, op, custom) {
        var result = [],
            ri = -1,
            useGetStyle = custom == "{",
            fn = DQ.operators[op],
            a,
            xml,
            hasXml,
            i, ci;

        value = unescapeCssSelector(value);

        for (i = 0; ci = cs[i]; i++) {
            
            if (ci.nodeType === 1) {
                
                if (!hasXml) {
                    xml = DQ.isXml(ci);
                    hasXml = true;
                }

                
                if (!xml) {
                    if (useGetStyle) {
                        a = DQ.getStyle(ci, attr);
                    } else if (attr == "class" || attr == "className") {
                        a = ci.className;
                    } else if (attr == "for") {
                        a = ci.htmlFor;
                    } else if (attr == "href") {
                        
                        
                        a = ci.getAttribute("href", 2);
                    } else {
                        a = ci.getAttribute(attr);
                    }
                } else {
                    a = ci.getAttribute(attr);
                }
                if ((fn && fn(a, value)) || (!fn && a)) {
                    result[++ri] = ci;
                }
            }
        }
        return result;
    }

    function byPseudo(cs, name, value) {
        value = unescapeCssSelector(value);
        return DQ.pseudos[name](cs, value);
    }

    function nodupIEXml(cs) {
        var d = ++key,
            r,
            i, len, c;
        cs[0].setAttribute("_nodup", d);
        r = [cs[0]];
        for (i = 1, len = cs.length; i < len; i++) {
            c = cs[i];
            if (!c.getAttribute("_nodup") != d) {
                c.setAttribute("_nodup", d);
                r[r.length] = c;
            }
        }
        for (i = 0, len = cs.length; i < len; i++) {
            cs[i].removeAttribute("_nodup");
        }
        return r;
    }

    function nodup(cs) {
        if (!cs) {
            return [];
        }
        var len = cs.length, c, i, r = cs, cj, ri = -1, d, j;
        if (!len || typeof cs.nodeType != "undefined" || len == 1) {
            return cs;
        }
        if (isIE && typeof cs[0].selectSingleNode != "undefined") {
            return nodupIEXml(cs);
        }
        d = ++key;
        cs[0]._nodup = d;
        for (i = 1; c = cs[i]; i++) {
            if (c._nodup != d) {
                c._nodup = d;
            } else {
                r = [];
                for (j = 0; j < i; j++) {
                    r[++ri] = cs[j];
                }
                for (j = i + 1; cj = cs[j]; j++) {
                    if (cj._nodup != d) {
                        cj._nodup = d;
                        r[++ri] = cj;
                    }
                }
                return r;
            }
        }
        return r;
    }

    function quickDiffIEXml(c1, c2) {
        var d = ++key,
            r = [],
            i, len;
        for (i = 0, len = c1.length; i < len; i++) {
            c1[i].setAttribute("_qdiff", d);
        }
        for (i = 0, len = c2.length; i < len; i++) {
            if (c2[i].getAttribute("_qdiff") != d) {
                r[r.length] = c2[i];
            }
        }
        for (i = 0, len = c1.length; i < len; i++) {
            c1[i].removeAttribute("_qdiff");
        }
        return r;
    }

    function quickDiff(c1, c2) {
        var len1 = c1.length,
            d = ++key,
            r = [],
            i, len;
        if (!len1) {
            return c2;
        }
        if (isIE && typeof c1[0].selectSingleNode != "undefined") {
            return quickDiffIEXml(c1, c2);
        }
        for (i = 0; i < len1; i++) {
            c1[i]._qdiff = d;
        }
        for (i = 0, len = c2.length; i < len; i++) {
            if (c2[i]._qdiff != d) {
                r[r.length] = c2[i];
            }
        }
        return r;
    }

    function quickId(ns, mode, root, id) {
        if (ns == root) {
            id = unescapeCssSelector(id);
            var d = root.ownerDocument || root;
            return d.getElementById(id);
        }
        ns = getNodes(ns, mode, "*");
        return byId(ns, id);
    }

    return DQ = {
        getStyle: function(el, name) {
            return Ext.fly(el, '_DomQuery').getStyle(name);
        },
        
        compile: function(path, type) {
            type = type || "select";

            
            var fn = ["var f = function(root) {\n var mode; ++batch; var n = root || document;\n"],
                lastPath,
                matchers = DQ.matchers,
                matchersLn = matchers.length,
                modeMatch,
                
                lmode = path.match(modeRe),
                tokenMatch, matched, j, t, m;

            path = setupEscapes(path);

            if (lmode && lmode[1]) {
                fn[fn.length] = 'mode="' + lmode[1].replace(trimRe, "") + '";';
                path = path.replace(lmode[1], "");
            }

            
            while (path.substr(0, 1) == "/") {
                path = path.substr(1);
            }

            while (path && lastPath != path) {
                lastPath = path;
                tokenMatch = path.match(tagTokenRe);
                if (type == "select") {
                    if (tokenMatch) {
                        
                        if (tokenMatch[1] == "#") {
                            fn[fn.length] = 'n = quickId(n, mode, root, "' + tokenMatch[2] + '");';
                        } else {
                            fn[fn.length] = 'n = getNodes(n, mode, "' + tokenMatch[2] + '");';
                        }
                        path = path.replace(tokenMatch[0], "");
                    } else if (path.substr(0, 1) != '@') {
                        fn[fn.length] = 'n = getNodes(n, mode, "*");';
                    }
                    
                } else {
                    if (tokenMatch) {
                        if (tokenMatch[1] == "#") {
                            fn[fn.length] = 'n = byId(n, "' + tokenMatch[2] + '");';
                        } else {
                            fn[fn.length] = 'n = byTag(n, "' + tokenMatch[2] + '");';
                        }
                        path = path.replace(tokenMatch[0], "");
                    }
                }
                while (!(modeMatch = path.match(modeRe))) {
                    matched = false;
                    for (j = 0; j < matchersLn; j++) {
                        t = matchers[j];
                        m = path.match(t.re);
                        if (m) {
                            fn[fn.length] = t.select.replace(tplRe, function(x, i) {
                                return m[i];
                            });
                            path = path.replace(m[0], "");
                            matched = true;
                            break;
                        }
                    }
                    
                    if (!matched) {
                        Ext.Error.raise({
                            sourceClass:'Ext.DomQuery',
                            sourceMethod:'compile',
                            msg:'Error parsing selector. Parsing failed at "' + path + '"'
                        });
                    }
                }
                if (modeMatch[1]) {
                    fn[fn.length] = 'mode="' + modeMatch[1].replace(trimRe, "") + '";';
                    path = path.replace(modeMatch[1], "");
                }
            }
            
            fn[fn.length] = "return nodup(n);\n}";

            
            eval(fn.join(""));
            return f;
        },

        
        jsSelect: function(path, root, type) {
            
            root = root || doc;

            if (typeof root == "string") {
                root = doc.getElementById(root);
            }
            var paths = path.split(","),
                results = [],
                i, len, subPath, result;

            
            for (i = 0, len = paths.length; i < len; i++) {
                subPath = paths[i].replace(trimRe, "");
                
                if (!cache[subPath]) {
                    
                    cache[subPath] = DQ.compile(subPath, type);
                    if (!cache[subPath]) {
                        Ext.Error.raise({
                            sourceClass:'Ext.DomQuery',
                            sourceMethod:'jsSelect',
                            msg:subPath + ' is not a valid selector'
                        });
                    }
                } else {
                    
                    
                    setupEscapes(subPath);
                }
                result = cache[subPath](root);
                if (result && result !== doc) {
                    results = results.concat(result);
                }
            }

            
            
            if (paths.length > 1) {
                return nodup(results);
            }
            return results;
        },

        isXml: function(el) {
            var docEl = (el ? el.ownerDocument || el : 0).documentElement;
            return docEl ? docEl.nodeName !== "HTML" : false;
        },

        
        select : doc.querySelectorAll ? function(path, root, type, single) {
            root = root || doc;
            if (!DQ.isXml(root)) {
                try {
                    
                    if (root.parentNode && (root.nodeType !== 9) && path.indexOf(',') === -1 && !startIdRe.test(path)) {
                        path = '#' + Ext.escapeId(Ext.id(root)) + ' ' + path;
                        root = root.parentNode;
                    }
                    return single ? [ root.querySelector(path) ]
                        : Ext.Array.toArray(root.querySelectorAll(path));
                }
                catch (e) {
                }
            }
            return DQ.jsSelect.call(this, path, root, type);
        } : function(path, root, type) {
            return DQ.jsSelect.call(this, path, root, type);
        },

        
        selectNode : function(path, root){
            return Ext.DomQuery.select(path, root, null, true)[0];
        },

        
        selectValue: function(path, root, defaultValue) {
            path = path.replace(trimRe, "");
            if (!valueCache[path]) {
                valueCache[path] = DQ.compile(path, "select");
            } else {
                setupEscapes(path);
            }

            var n = valueCache[path](root),
                v;

            n = n[0] ? n[0] : n;

            
            
            
            
            if (typeof n.normalize == 'function') {
                n.normalize();
            }

            v = (n && n.firstChild ? n.firstChild.nodeValue : null);
            return ((v === null || v === undefined || v === '') ? defaultValue : v);
        },

        
        selectNumber: function(path, root, defaultValue) {
            var v = DQ.selectValue(path, root, defaultValue || 0);
            return parseFloat(v);
        },

        
        is: function(el, ss) {
            if (typeof el == "string") {
                el = doc.getElementById(el);
            }
            var isArray = Ext.isArray(el),
                result = DQ.filter(isArray ? el : [el], ss);
            return isArray ? (result.length == el.length) : (result.length > 0);
        },

        
        filter: function(els, ss, nonMatches) {
            ss = ss.replace(trimRe, "");
            if (!simpleCache[ss]) {
                simpleCache[ss] = DQ.compile(ss, "simple");
            } else {
                setupEscapes(ss);
            }

            var result = simpleCache[ss](els);
            return nonMatches ? quickDiff(result, els) : result;
        },

        
        matchers: [{
            re: /^\.([\w\-\\]+)/,
            select: useClassList ? 'n = byClassName(n, "{1}");' : 'n = byClassName(n, " {1} ");'
        }, {
            re: /^\:([\w\-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
            select: 'n = byPseudo(n, "{1}", "{2}");'
        },  {
            re: /^(?:([\[\{])(?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
            select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
        }, {
            re: /^#([\w\-\\]+)/,
            select: 'n = byId(n, "{1}");'
        }, {
            re: /^@([\w\-\.]+)/,
            select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
        }],

        
        operators: {
            "=": function(a, v) {
                return a == v;
            },
            "!=": function(a, v) {
                return a != v;
            },
            "^=": function(a, v) {
                return a && a.substr(0, v.length) == v;
            },
            "$=": function(a, v) {
                return a && a.substr(a.length - v.length) == v;
            },
            "*=": function(a, v) {
                return a && a.indexOf(v) !== -1;
            },
            "%=": function(a, v) {
                return (a % v) === 0;
            },
            "|=": function(a, v) {
                return a && (a == v || a.substr(0, v.length + 1) == v + '-');
            },
            "~=": function(a, v) {
                return a && (' ' + a + ' ').indexOf(' ' + v + ' ') != -1;
            }
        },

        
        pseudos: {
            "first-child": function(c) {
                var r = [], ri = -1, n,
                    i, ci;
                for (i = 0; (ci = n = c[i]); i++) {
                    while ((n = n.previousSibling) && n.nodeType != 1);
                    if (!n) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "last-child": function(c) {
                var r = [], ri = -1, n,
                    i, ci;
                for (i = 0; (ci = n = c[i]); i++) {
                    while ((n = n.nextSibling) && n.nodeType != 1);
                    if (!n) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "nth-child": function(c, a) {
                var r = [], ri = -1,
                    m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
                    f = (m[1] || 1) - 0, l = m[2] - 0,
                    i, n, j, cn, pn;
                for (i = 0; n = c[i]; i++) {
                    pn = n.parentNode;
                    if (batch != pn._batch) {
                        j = 0;
                        for (cn = pn.firstChild; cn; cn = cn.nextSibling) {
                            if (cn.nodeType == 1) {
                                cn.nodeIndex = ++j;
                            }
                        }
                        pn._batch = batch;
                    }
                    if (f == 1) {
                        if (l === 0 || n.nodeIndex == l) {
                            r[++ri] = n;
                        }
                    } else if ((n.nodeIndex + l) % f === 0) {
                        r[++ri] = n;
                    }
                }

                return r;
            },

            "only-child": function(c) {
                var r = [], ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if (!prev(ci) && !next(ci)) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "empty": function(c) {
                var r = [], ri = -1,
                    i, ci, cns, j, cn, empty;
                for (i = 0; ci = c[i]; i++) {
                    cns = ci.childNodes;
                    j = 0;
                    empty = true;
                    while (cn = cns[j]) {
                        ++j;
                        if (cn.nodeType == 1 || cn.nodeType == 3) {
                            empty = false;
                            break;
                        }
                    }
                    if (empty) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "contains": function(c, v) {
                var r = [], ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if ((ci.textContent || ci.innerText || ci.text || '').indexOf(v) != -1) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "nodeValue": function(c, v) {
                var r = [], ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if (ci.firstChild && ci.firstChild.nodeValue == v) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "checked": function(c) {
                var r = [], ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if (ci.checked === true) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "not": function(c, ss) {
                return DQ.filter(c, ss, true);
            },

            "any": function(c, selectors) {
                var ss = selectors.split('|'),
                    r = [], ri = -1, s,
                    i, ci, j;
                for (i = 0; ci = c[i]; i++) {
                    for (j = 0; s = ss[j]; j++) {
                        if (DQ.is(ci, s)) {
                            r[++ri] = ci;
                            break;
                        }
                    }
                }
                return r;
            },

            "odd": function(c) {
                return this["nth-child"](c, "odd");
            },

            "even": function(c) {
                return this["nth-child"](c, "even");
            },

            "nth": function(c, a) {
                return c[a - 1] || [];
            },

            "first": function(c) {
                return c[0] || [];
            },

            "last": function(c) {
                return c[c.length - 1] || [];
            },

            "has": function(c, ss) {
                var s = DQ.select,
                    r = [], ri = -1,
                    i, ci;
                for (i = 0; ci = c[i]; i++) {
                    if (s(ss, ci).length > 0) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "next": function(c, ss) {
                var is = DQ.is,
                    r = [], ri = -1,
                    i, ci, n;
                for (i = 0; ci = c[i]; i++) {
                    n = next(ci);
                    if (n && is(n, ss)) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "prev": function(c, ss) {
                var is = DQ.is,
                    r = [], ri = -1,
                    i, ci, n;
                for (i = 0; ci = c[i]; i++) {
                    n = prev(ci);
                    if (n && is(n, ss)) {
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            focusable: function(candidates) {
                var len = candidates.length,
                    results = [],
                    i = 0,
                    c;

                for (; i < len; i++) {
                    c = candidates[i];
                    if (Ext.fly(c, '_DomQuery').isFocusable()) {
                        results.push(c);
                    }
                }

                return results;
            },
            
            visible: function(candidates, deep) {
                var len = candidates.length,
                    results = [],
                    i = 0,
                    c;

                for (; i < len; i++) {
                    c = candidates[i];
                    if (Ext.fly(c, '_DomQuery').isVisible(deep)) {
                        results.push(c);
                    }
                }

                return results;
            }
        }
    };
}());


Ext.query = Ext.DomQuery.select;





Ext.define('Ext.dom.Element_anim', {
    override: 'Ext.dom.Element',

    
    animate: function(config) {
        var me = this,
            listeners,
            anim,
            animId = me.dom.id || Ext.id(me.dom);

        if (!Ext.fx.Manager.hasFxBlock(animId)) {
            
            if (config.listeners) {
                listeners = config.listeners;
                delete config.listeners;
            }
            if (config.internalListeners) {
                config.listeners = config.internalListeners;
                delete config.internalListeners;
            }
            anim = new Ext.fx.Anim(me.anim(config));
            if (listeners) {
                anim.on(listeners);
            }
            Ext.fx.Manager.queueFx(anim);
        }
        return me;
    },

    
    anim: function(config) {
        if (!Ext.isObject(config)) {
            return (config) ? {} : false;
        }

        var me = this,
            duration = config.duration || Ext.fx.Anim.prototype.duration,
            easing = config.easing || 'ease',
            animConfig;

        if (config.stopAnimation) {
            me.stopAnimation();
        }

        Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));

        
        Ext.fx.Manager.setFxDefaults(me.id, {
            delay: 0
        });

        animConfig = {
            
            target: me.dom,
            remove: config.remove,
            alternate: config.alternate || false,
            duration: duration,
            easing: easing,
            callback: config.callback,
            listeners: config.listeners,
            iterations: config.iterations || 1,
            scope: config.scope,
            block: config.block,
            concurrent: config.concurrent,
            delay: config.delay || 0,
            paused: true,
            keyframes: config.keyframes,
            from: config.from || {},
            to: Ext.apply({}, config)
        };
        Ext.apply(animConfig.to, config.to);

        
        delete animConfig.to.to;
        delete animConfig.to.from;
        delete animConfig.to.remove;
        delete animConfig.to.alternate;
        delete animConfig.to.keyframes;
        delete animConfig.to.iterations;
        delete animConfig.to.listeners;
        delete animConfig.to.target;
        delete animConfig.to.paused;
        delete animConfig.to.callback;
        delete animConfig.to.scope;
        delete animConfig.to.duration;
        delete animConfig.to.easing;
        delete animConfig.to.concurrent;
        delete animConfig.to.block;
        delete animConfig.to.stopAnimation;
        delete animConfig.to.delay;
        return animConfig;
    },

    
    slideIn: function(anchor, obj, slideOut) {
        var me = this,
            dom = me.dom,
            elStyle = dom.style,
            beforeAnim,
            wrapAnim,
            restoreScroll,
            wrapDomParentNode;

        anchor = anchor || "t";
        obj = obj || {};

        beforeAnim = function() {
            var animScope = this,
                listeners = obj.listeners,
                el = Ext.fly(dom, '_anim'),
                box, originalStyles, anim, wrap;

            if (!slideOut) {
                el.fixDisplay();
            }

            box = el.getBox();
            if ((anchor == 't' || anchor == 'b') && box.height === 0) {
                box.height = dom.scrollHeight;
            }
            else if ((anchor == 'l' || anchor == 'r') && box.width === 0) {
                box.width = dom.scrollWidth;
            }

            originalStyles = el.getStyles('width', 'height', 'left', 'right', 'top', 'bottom', 'position', 'z-index', true);
            el.setSize(box.width, box.height);

            
            if (obj.preserveScroll) {
                restoreScroll = el.cacheScrollValues();
            }

            wrap = el.wrap({
                id: Ext.id() + '-anim-wrap-for-' + el.dom.id,
                style: {
                    visibility: slideOut ? 'visible' : 'hidden'
                }
            });
            wrapDomParentNode = wrap.dom.parentNode;
            wrap.setPositioning(el.getPositioning(true));
            if (wrap.isStyle('position', 'static')) {
                wrap.position('relative');
            }
            el.clearPositioning('auto');
            wrap.clip();

            
            if (restoreScroll) {
                restoreScroll();
            }

            
            
            
            el.setStyle({
                visibility: '',
                position: 'absolute'
            });
            if (slideOut) {
                wrap.setSize(box.width, box.height);
            }

            switch (anchor) {
                case 't':
                    anim = {
                        from: {
                            width: box.width + 'px',
                            height: '0px'
                        },
                        to: {
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.bottom = '0px';
                    break;
                case 'l':
                    anim = {
                        from: {
                            width: '0px',
                            height: box.height + 'px'
                        },
                        to: {
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    me.anchorAnimX(anchor);
                    break;
                case 'r':
                    anim = {
                        from: {
                            x: box.x + box.width,
                            width: '0px',
                            height: box.height + 'px'
                        },
                        to: {
                            x: box.x,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    me.anchorAnimX(anchor);
                    break;
                case 'b':
                    anim = {
                        from: {
                            y: box.y + box.height,
                            width: box.width + 'px',
                            height: '0px'
                        },
                        to: {
                            y: box.y,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    break;
                case 'tl':
                    anim = {
                        from: {
                            x: box.x,
                            y: box.y,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.bottom = '0px';
                    me.anchorAnimX('l');
                    break;
                case 'bl':
                    anim = {
                        from: {
                            y: box.y + box.height,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            y: box.y,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    me.anchorAnimX('l');
                    break;
                case 'br':
                    anim = {
                        from: {
                            x: box.x + box.width,
                            y: box.y + box.height,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            x: box.x,
                            y: box.y,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    me.anchorAnimX('r');
                    break;
                case 'tr':
                    anim = {
                        from: {
                            x: box.x + box.width,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            x: box.x,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.bottom = '0px';
                    me.anchorAnimX('r');
                    break;
            }

            wrap.show();
            wrapAnim = Ext.apply({}, obj);
            delete wrapAnim.listeners;
            wrapAnim = new Ext.fx.Anim(Ext.applyIf(wrapAnim, {
                target: wrap,
                duration: 500,
                easing: 'ease-out',
                from: slideOut ? anim.to : anim.from,
                to: slideOut ? anim.from : anim.to
            }));

            
            wrapAnim.on('afteranimate', function() {
                var el = Ext.fly(dom, '_anim');
                
                el.setStyle(originalStyles);
                if (slideOut) {
                    if (obj.useDisplay) {
                        el.setDisplayed(false);
                    } else {
                        el.hide();
                    }
                }
                if (wrap.dom) {
                    if (wrap.dom.parentNode) {
                        wrap.dom.parentNode.insertBefore(el.dom, wrap.dom);
                    } else {
                        wrapDomParentNode.appendChild(el.dom);
                    }
                    wrap.remove();
                }
                
                if (restoreScroll) {
                    restoreScroll();
                }
                
                animScope.end();
            });
            
            if (listeners) {
                wrapAnim.on(listeners);
            }
        };

        me.animate({
            
            duration: obj.duration ? Math.max(obj.duration, 500) * 2 : 1000,
            listeners: {
                beforeanimate: beforeAnim 
            }
        });
        return me;
    },


    
    slideOut: function(anchor, o) {
        return this.slideIn(anchor, o, true);
    },

    
    puff: function(obj) {
        var me = this,
            dom = me.dom,
            beforeAnim,
            box = me.getBox(),
            originalStyles = me.getStyles('width', 'height', 'left', 'right', 'top', 'bottom', 'position', 'z-index', 'font-size', 'opacity', true);

       obj = Ext.applyIf(obj || {}, {
            easing: 'ease-out',
            duration: 500,
            useDisplay: false
        });

        beforeAnim = function() {
            var el = Ext.fly(dom, '_anim');
            
            el.clearOpacity();
            el.show();
            this.to = {
                width: box.width * 2,
                height: box.height * 2,
                x: box.x - (box.width / 2),
                y: box.y - (box.height /2),
                opacity: 0,
                fontSize: '200%'
            };
            this.on('afteranimate',function() {
                var el = Ext.fly(dom, '_anim');
                if (el) {
                    if (obj.useDisplay) {
                        el.setDisplayed(false);
                    } else {
                        el.hide();
                    }
                    el.setStyle(originalStyles);
                    Ext.callback(obj.callback, obj.scope);
                }
            });
        };

        me.animate({
            duration: obj.duration,
            easing: obj.easing,
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            }
        });
        return me;
    },

    
    switchOff: function(obj) {
        var me = this,
            dom = me.dom,
            beforeAnim;

        obj = Ext.applyIf(obj || {}, {
            easing: 'ease-in',
            duration: 500,
            remove: false,
            useDisplay: false
        });

        beforeAnim = function() {
            var el = Ext.fly(dom, '_anim'),
                animScope = this,
                size = el.getSize(),
                xy = el.getXY(),
                keyframe, position;
                
            el.clearOpacity();
            el.clip();
            position = el.getPositioning();

            keyframe = new Ext.fx.Animator({
                target: dom,
                duration: obj.duration,
                easing: obj.easing,
                keyframes: {
                    33: {
                        opacity: 0.3
                    },
                    66: {
                        height: 1,
                        y: xy[1] + size.height / 2
                    },
                    100: {
                        width: 1,
                        x: xy[0] + size.width / 2
                    }
                }
            });
            keyframe.on('afteranimate', function() {
                var el = Ext.fly(dom, '_anim');
                if (obj.useDisplay) {
                    el.setDisplayed(false);
                } else {
                    el.hide();
                }
                el.clearOpacity();
                el.setPositioning(position);
                el.setSize(size);
                
                animScope.end();
            });
        };
        
        me.animate({
            
            duration: (Math.max(obj.duration, 500) * 2),
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            },
            callback: obj.callback,
            scope: obj.scope
        });
        return me;
    },

    
    frame : function(color, count, obj){
        var me = this,
            dom = me.dom,
            beforeAnim;

        color = color || '#C3DAF9';
        count = count || 1;
        obj = obj || {};

        beforeAnim = function() {
            var el = Ext.fly(dom, '_anim'),
                animScope = this,
                box,
                proxy, proxyAnim;
                
            el.show();
            box = el.getBox();
            proxy = Ext.getBody().createChild({
                id: el.dom.id + '-anim-proxy',
                style: {
                    position : 'absolute',
                    'pointer-events': 'none',
                    'z-index': 35000,
                    border : '0px solid ' + color
                }
            });
            
            proxyAnim = new Ext.fx.Anim({
                target: proxy,
                duration: obj.duration || 1000,
                iterations: count,
                from: {
                    top: box.y,
                    left: box.x,
                    borderWidth: 0,
                    opacity: 1,
                    height: box.height,
                    width: box.width
                },
                to: {
                    top: box.y - 20,
                    left: box.x - 20,
                    borderWidth: 10,
                    opacity: 0,
                    height: box.height + 40,
                    width: box.width + 40
                }
            });
            proxyAnim.on('afteranimate', function() {
                proxy.remove();
                
                animScope.end();
            });
        };

        me.animate({
            
            duration: (Math.max(obj.duration, 500) * 2) || 2000,
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            },
            callback: obj.callback,
            scope: obj.scope
        });
        return me;
    },

    
    ghost: function(anchor, obj) {
        var me = this,
            dom = me.dom,
            beforeAnim;

        anchor = anchor || "b";
        beforeAnim = function() {
            var el = Ext.fly(dom, '_anim'),
                width = el.getWidth(),
                height = el.getHeight(),
                xy = el.getXY(),
                position = el.getPositioning(),
                to = {
                    opacity: 0
                };
            switch (anchor) {
                case 't':
                    to.y = xy[1] - height;
                    break;
                case 'l':
                    to.x = xy[0] - width;
                    break;
                case 'r':
                    to.x = xy[0] + width;
                    break;
                case 'b':
                    to.y = xy[1] + height;
                    break;
                case 'tl':
                    to.x = xy[0] - width;
                    to.y = xy[1] - height;
                    break;
                case 'bl':
                    to.x = xy[0] - width;
                    to.y = xy[1] + height;
                    break;
                case 'br':
                    to.x = xy[0] + width;
                    to.y = xy[1] + height;
                    break;
                case 'tr':
                    to.x = xy[0] + width;
                    to.y = xy[1] - height;
                    break;
            }
            this.to = to;
            this.on('afteranimate', function () {
                var el = Ext.fly(dom, '_anim');
                if (el) {
                    el.hide();
                    el.clearOpacity();
                    el.setPositioning(position);
                }
            });
        };

        me.animate(Ext.applyIf(obj || {}, {
            duration: 500,
            easing: 'ease-out',
            listeners: {
                beforeanimate: beforeAnim
            }
        }));
        return me;
    },

    
    highlight: function(color, o) {
        var me = this,
            dom = me.dom,
            from = {},
            restore, to, attr, lns, event, fn;

        
        if (dom.tagName.match(me.tableTagRe)) {
            return me.select('div').highlight(color, o);
        }

        o = o || {};
        lns = o.listeners || {};
        attr = o.attr || 'backgroundColor';
        from[attr] = color || 'ffff9c';

        if (!o.to) {
            to = {};
            to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
        }
        else {
            to = o.to;
        }

        
        o.listeners = Ext.apply(Ext.apply({}, lns), {
            beforeanimate: function() {
                restore = dom.style[attr];
                var el = Ext.fly(dom, '_anim');
                el.clearOpacity();
                el.show();

                event = lns.beforeanimate;
                if (event) {
                    fn = event.fn || event;
                    return fn.apply(event.scope || lns.scope || window, arguments);
                }
            },
            afteranimate: function() {
                if (dom) {
                    dom.style[attr] = restore;
                }

                event = lns.afteranimate;
                if (event) {
                    fn = event.fn || event;
                    fn.apply(event.scope || lns.scope || window, arguments);
                }
            }
        });

        me.animate(Ext.apply({}, o, {
            duration: 1000,
            easing: 'ease-in',
            from: from,
            to: to
        }));
        return me;
    },

   
    pause: function(ms) {
        var me = this;
        Ext.fx.Manager.setFxDefaults(me.id, {
            delay: ms
        });
        return me;
    },

    
    fadeIn: function(o) {
        var me = this,
            dom = me.dom;
            
        me.animate(Ext.apply({}, o, {
            opacity: 1,
            internalListeners: {
                beforeanimate: function(anim){
                    
                    
                    var el = Ext.fly(dom, '_anim');
                    if (el.isStyle('display', 'none')) {
                        el.setDisplayed('');
                    } else {
                        el.show();
                    } 
                }
            }
        }));
        return this;
    },

    
    fadeOut: function(o) {
        var me = this,
            dom = me.dom;
            
        o = Ext.apply({
            opacity: 0,
            internalListeners: {
                afteranimate: function(anim){
                    if (dom && anim.to.opacity === 0) {
                        var el = Ext.fly(dom, '_anim');
                        if (o.useDisplay) {
                            el.setDisplayed(false);
                        } else {
                            el.hide();
                        }
                    }         
                }
            }
        }, o);
        me.animate(o);
        return me;
    },

    
    scale: function(w, h, o) {
        this.animate(Ext.apply({}, o, {
            width: w,
            height: h
        }));
        return this;
    },

    
    shift: function(config) {
        this.animate(config);
        return this;
    },

    
    anchorAnimX: function(anchor) {
        var xName = (anchor === 'l') ? 'right' : 'left';
        this.dom.style[xName] = '0px';
    }
});



Ext.define('Ext.dom.Element_dd', {
    override: 'Ext.dom.Element',

    
    initDD : function(group, config, overrides){
        var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);
        return Ext.apply(dd, overrides);
    },

    
    initDDProxy : function(group, config, overrides){
        var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);
        return Ext.apply(dd, overrides);
    },

    
    initDDTarget : function(group, config, overrides){
        var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);
        return Ext.apply(dd, overrides);
    }
});



Ext.define('Ext.dom.Element_fx', {
    override: 'Ext.dom.Element'
},
function() {

var Element         = Ext.dom.Element,
    VISIBILITY      = "visibility",
    DISPLAY         = "display",
    NONE            = "none",
    HIDDEN          = 'hidden',
    VISIBLE         = 'visible',
    OFFSETS         = "offsets",
    ASCLASS         = "asclass",
    NOSIZE          = 'nosize',
    ORIGINALDISPLAY = 'originalDisplay',
    VISMODE         = 'visibilityMode',
    ISVISIBLE       = 'isVisible',
    OFFSETCLASS     = Ext.baseCSSPrefix + 'hide-offsets',
    getDisplay = function(el) {
        var data = (el.$cache || el.getCache()).data,
            display = data[ORIGINALDISPLAY];
            
        if (display === undefined) {
            data[ORIGINALDISPLAY] = display = '';
        }
        return display;
    },
    getVisMode = function(el){
        var data = (el.$cache || el.getCache()).data,
            visMode = data[VISMODE];
            
        if (visMode === undefined) {
            data[VISMODE] = visMode = Element.VISIBILITY;
        }
        return visMode;
    };

Element.override({
    
    originalDisplay : "",
    visibilityMode : 1,

    
    setVisible : function(visible, animate) {
        var me = this,
            dom = me.dom,
            visMode = getVisMode(me);

        
        if (typeof animate == 'string') {
            switch (animate) {
                case DISPLAY:
                    visMode = Element.DISPLAY;
                    break;
                case VISIBILITY:
                    visMode = Element.VISIBILITY;
                    break;
                case OFFSETS:
                    visMode = Element.OFFSETS;
                    break;
                case NOSIZE:
                case ASCLASS:
                    visMode = Element.ASCLASS;
                    break;
            }
            me.setVisibilityMode(visMode);
            animate = false;
        }

        if (!animate || !me.anim) {
            if (visMode == Element.DISPLAY) {
                return me.setDisplayed(visible);
            } else if (visMode == Element.OFFSETS) {
                me[visible?'removeCls':'addCls'](OFFSETCLASS);
            } else if (visMode == Element.VISIBILITY) {
                me.fixDisplay();
                
                dom.style.visibility = visible ? '' : HIDDEN;
            } else if (visMode == Element.ASCLASS) {
                me[visible?'removeCls':'addCls'](me.visibilityCls || Element.visibilityCls);
            }
        } else {
            
            if (visible) {
                me.setOpacity(0.01);
                me.setVisible(true);
            }
            if (!Ext.isObject(animate)) {
                animate = {
                    duration: 350,
                    easing: 'ease-in'
                };
            }
            me.animate(Ext.applyIf({
                callback: function() {
                    if (!visible) {
                        
                        
                        Ext.fly(dom, '_internal').setVisible(false).setOpacity(1);
                    }
                },
                to: {
                    opacity: (visible) ? 1 : 0
                }
            }, animate));
        }
        (me.$cache || me.getCache()).data[ISVISIBLE] = visible;
        return me;
    },

    
    hasMetrics  : function(){
        var visMode = getVisMode(this);
        return this.isVisible() || (visMode == Element.OFFSETS) || (visMode == Element.VISIBILITY);
    },

    
    toggle : function(animate){
        var me = this;
        me.setVisible(!me.isVisible(), me.anim(animate));
        return me;
    },

    
    setDisplayed : function(value) {
        if(typeof value == "boolean"){
           value = value ? getDisplay(this) : NONE;
        }
        this.setStyle(DISPLAY, value);
        return this;
    },

    
    fixDisplay : function(){
        var me = this;
        if (me.isStyle(DISPLAY, NONE)) {
            me.setStyle(VISIBILITY, HIDDEN);
            me.setStyle(DISPLAY, getDisplay(me)); 
            if (me.isStyle(DISPLAY, NONE)) { 
                me.setStyle(DISPLAY, "block");
            }
        }
    },

    
    hide : function(animate){
        
        if (typeof animate == 'string'){
            this.setVisible(false, animate);
            return this;
        }
        this.setVisible(false, this.anim(animate));
        return this;
    },

    
    show : function(animate){
        
        if (typeof animate == 'string'){
            this.setVisible(true, animate);
            return this;
        }
        this.setVisible(true, this.anim(animate));
        return this;
    }
});

});



Ext.define('Ext.dom.Element_position', {
    override: 'Ext.dom.Element'
},
function() {

var flyInstance,
    Element = this,
    LEFT = "left",
    RIGHT = "right",
    TOP = "top",
    BOTTOM = "bottom",
    POSITION = "position",
    STATIC = "static",
    RELATIVE = "relative",
    ZINDEX = "z-index",
    BODY = 'BODY',

    PADDING = 'padding',
    BORDER = 'border',
    SLEFT = '-left',
    SRIGHT = '-right',
    STOP = '-top',
    SBOTTOM = '-bottom',
    SWIDTH = '-width',
    
    borders = {l: BORDER + SLEFT + SWIDTH, r: BORDER + SRIGHT + SWIDTH, t: BORDER + STOP + SWIDTH, b: BORDER + SBOTTOM + SWIDTH},
    paddings = {l: PADDING + SLEFT, r: PADDING + SRIGHT, t: PADDING + STOP, b: PADDING + SBOTTOM},
    paddingsTLRB = [paddings.l, paddings.r, paddings.t, paddings.b],
    bordersTLRB = [borders.l,  borders.r,  borders.t,  borders.b],
    round = Math.round,
    doc = document,
    fly = function (el) {
        if (!flyInstance) {
            flyInstance = new Ext.Element.Fly();
        }
        flyInstance.attach(el);
        return flyInstance;
    };

    Element.override({

        pxRe: /^\d+(?:\.\d*)?px$/i,

        inheritableStatics: {
            getX: function(el) {
                return Element.getXY(el)[0];
            },

            getXY: function(el) {
                var bd = doc.body,
                    docEl = doc.documentElement,
                    leftBorder = 0,
                    topBorder = 0,
                    ret = [0,0],
                    box,
                    scroll;

                el = Ext.getDom(el);

                if(el != doc && el != bd){
                    
                    
                    if (Ext.isIE) {
                        try {
                            box = el.getBoundingClientRect();
                            
                            
                            topBorder = docEl.clientTop || bd.clientTop;
                            leftBorder = docEl.clientLeft || bd.clientLeft;
                        } catch (ex) {
                            box = { left: 0, top: 0 };
                        }
                    } else {
                        box = el.getBoundingClientRect();
                    }

                    scroll = fly(doc).getScroll();
                    ret = [
                        round(box.left + scroll.left - leftBorder),
                        round(box.top + scroll.top - topBorder)
                    ];
                }
                return ret;
            },

            getY: function(el) {
                return Element.getXY(el)[1];
            },

            setX: function(el, x) {
                Element.setXY(el, [x, false]);
            },

            setXY: function(el, xy) {
                (el = Ext.fly(el, '_setXY')).position();

                var pts = el.translatePoints(xy),
                    style = el.dom.style,
                    pos;

                
                
                style.right = 'auto';
                for (pos in pts) {
                    if (!isNaN(pts[pos])) {
                        style[pos] = pts[pos] + "px";
                    }
                }
            },

            setY: function(el, y) {
                Element.setXY(el, [false, y]);
            }
        },

        
        center: function(centerIn){
            return this.alignTo(centerIn || doc, 'c-c');
        },

        
        clearPositioning: function(value) {
            value = value || '';
            return this.setStyle({
                left : value,
                right : value,
                top : value,
                bottom : value,
                'z-index' : '',
                position : STATIC
            });
        },

        getAnchorToXY: function(el, anchor, local, mySize) {
            return el.getAnchorXY(anchor, local, mySize);
        },

        
        getBottom: function(local) {
            return (local ? this.getLocalY() : this.getY()) + this.getHeight();
        },

        getBorderPadding: function() {
            var paddingWidth = this.getStyle(paddingsTLRB),
                bordersWidth = this.getStyle(bordersTLRB);

            return {
                beforeX: (parseFloat(bordersWidth[borders.l]) || 0) + (parseFloat(paddingWidth[paddings.l]) || 0),
                afterX: (parseFloat(bordersWidth[borders.r]) || 0) + (parseFloat(paddingWidth[paddings.r]) || 0),
                beforeY: (parseFloat(bordersWidth[borders.t]) || 0) + (parseFloat(paddingWidth[paddings.t]) || 0),
                afterY: (parseFloat(bordersWidth[borders.b]) || 0) + (parseFloat(paddingWidth[paddings.b]) || 0)
            };
        },

        
        getCenterXY: function(){
            return this.getAlignToXY(doc, 'c-c');
        },

        
        getLeft: function(local) {
            return local ? this.getLocalX() : this.getX();
        },

        
        getLocalX: function() {
            var me = this,
                offsetParent = me.dom.offsetParent,
                x = me.getStyle('left');

            if (!x || x === 'auto') {
                x = 0;
            } else if (me.pxRe.test(x)) {
                x = parseFloat(x);
            } else {
                x = me.getX();
                if (offsetParent) {
                    x -= Element.getX(offsetParent);
                }
            }

            return x;
        },

        
        getLocalXY: function() {
            var me = this,
                offsetParent = me.dom.offsetParent,
                style = me.getStyle(['left', 'top']),
                x = style.left,
                y = style.top;

            if (!x || x === 'auto') {
                x = 0;
            } else if (me.pxRe.test(x)) {
                x = parseFloat(x);
            } else {
                x = me.getX();
                if (offsetParent) {
                    x -= Element.getX(offsetParent);
                }
            }

            if (!y || y === 'auto') {
                y = 0;
            } else if (me.pxRe.test(y)) {
                y = parseFloat(y);
            } else {
                y = me.getY();
                if (offsetParent) {
                    y -= Element.getY(offsetParent);
                }
            }

            return [x, y];
        },

        
        getLocalY: function() {
            var me = this,
                offsetParent = me.dom.offsetParent,
                y = me.getStyle('top');

            if (!y || y === 'auto') {
                y = 0;
            } else if (me.pxRe.test(y)) {
                y = parseFloat(y);
            } else {
                y = me.getY();
                if (offsetParent) {
                    y -= Element.getY(offsetParent);
                }
            }

            return y;
        },

        
        getPageBox: function(getRegion) {
            var me = this,
                dom = me.dom,
                isDoc = dom.nodeName == BODY,
                w = isDoc ? Ext.Element.getViewWidth() : dom.offsetWidth,
                h = isDoc ? Ext.Element.getViewHeight() : dom.offsetHeight,
                xy = me.getXY(),
                t = xy[1],
                r = xy[0] + w,
                b = xy[1] + h,
                l = xy[0];

            if (getRegion) {
                return new Ext.util.Region(t, r, b, l);
            }
            else {
                return {
                    left: l,
                    top: t,
                    width: w,
                    height: h,
                    right: r,
                    bottom: b
                };
            }
        },

        
        getPositioning: function(autoPx){
            var styles = this.getStyle(['left', 'top', 'position', 'z-index']),
                dom = this.dom;

            if(autoPx) {
                if(styles.left === 'auto') {
                    styles.left = dom.offsetLeft + 'px';
                }
                if(styles.top === 'auto') {
                    styles.top = dom.offsetTop + 'px';
                }
            }

            return styles;
        },

        
        getRight: function(local) {
            return (local ? this.getLocalX() : this.getX()) + this.getWidth();
        },

        
        getTop: function(local) {
            return local ? this.getLocalY() : this.getY();
        },

        
        getX: function() {
            return Element.getX(this.dom);
        },

        
        getXY: function() {
            return Element.getXY(this.dom);
        },

        
        getY: function() {
            return Element.getY(this.dom);
        },

        
        moveTo: function(x, y, animate) {
            return this.setXY([x, y], animate);
        },

        
        position: function(pos, zIndex, x, y) {
            var me = this;

            if (!pos && me.isStyle(POSITION, STATIC)) {
                me.setStyle(POSITION, RELATIVE);
            } else if (pos) {
                me.setStyle(POSITION, pos);
            }
            if (zIndex) {
                me.setStyle(ZINDEX, zIndex);
            }
            if (x || y) {
                me.setXY([x || false, y || false]);
            }
        },

        
        setBottom: function(bottom) {
            this.dom.style[BOTTOM] = this.addUnits(bottom);
            return this;
        },

        
        setBounds: function(x, y, width, height, animate) {
            return this.setBox({
                x: x,
                y: y,
                width: width,
                height: height
            }, animate);
        },

        
        setLeft: function(left) {
            this.dom.style[LEFT] = this.addUnits(left); 
            return this;
        },

        
        setLeftTop: function(left, top) {
            var me = this,
                style = me.dom.style;

            style.left = me.addUnits(left);
            style.top = me.addUnits(top);

            return me;
        },

        setLocalX: function(x) {
            var style = this.dom.style;

            
            style.right = 'auto';
            style.left = (x === null) ? 'auto' : x + 'px';
        },

        setLocalXY: function(x, y) {
            var style = this.dom.style;

            
            style.right = 'auto';

            if (x && x.length) {
                y = x[1];
                x = x[0];
            }

            if (x === null) {
                style.left = 'auto';
            } else if (x !== undefined) {
                style.left = x + 'px';
            }

            if (y === null) {
                style.top = 'auto';
            } else if (y !== undefined) {
                style.top = y + 'px';
            }
        },

        setLocalY: function(y) {
            this.dom.style.top = (y === null) ? 'auto' : y + 'px';
        },

        
        setLocation: function(x, y, animate) {
            return this.setXY([x, y], animate);
        },

        
        setPositioning: function(pc) {
            return this.setStyle(pc);
        },

        
        setRight: function(right) {
            this.dom.style[RIGHT] = this.addUnits(right);
            return this;
        },

        
        setTop: function(top) {
            this.dom.style[TOP] = this.addUnits(top);
            return this;
        },

        setX: function(x, animate) {
            return this.setXY([x, this.getY()], animate);
        },

        setXY: function(xy, animate) {
            var me = this;

            if (!animate || !me.anim) {
                Element.setXY(me.dom, xy);
            } else {
                if (!Ext.isObject(animate)) {
                    animate = {};
                }
                me.animate(Ext.applyIf({ to: { x: xy[0], y: xy[1] } }, animate));
            }
            return this;
        },

        setY: function(y, animate) {
            return this.setXY([this.getX(), y], animate);
        }
    });

    
    Element.getTrueXY = Element.getXY;

});



Ext.define('Ext.dom.Element_scroll', {
    override: 'Ext.dom.Element',

    
    isScrollable: function() {
        var dom = this.dom;
        return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
    },

    
    getScroll: function() {
        var me = this,
            dom = me.dom,
            doc = document,
            body = doc.body,
            docElement = doc.documentElement,
            left, top;

        if (dom === doc || dom === body) {
            
            
            
            
            
            
            
            
            left = docElement.scrollLeft || (body ? body.scrollLeft : 0);
            top = docElement.scrollTop || (body ? body.scrollTop : 0);
        } else {
            left = dom.scrollLeft;
            top = dom.scrollTop;
        }

        return {
            left: left,
            top: top
        };
    },
    
    
    getScrollLeft: function() {
        var dom = this.dom,
            doc = document;
            
        if (dom === doc || dom === doc.body) {
            return this.getScroll().left;
        } else {
            return dom.scrollLeft;
        }
    },
    
    
    getScrollTop: function(){
        var dom = this.dom,
            doc = document;
            
        if (dom === doc || dom === doc.body) {
            return this.getScroll().top;
        } else {
            return dom.scrollTop;
        }
    },
    
    
    setScrollLeft: function(left){
        this.dom.scrollLeft = left;
        return this;
    },
    
    
    setScrollTop: function(top) {
        this.dom.scrollTop = top;
        return this;
    },

    
    scrollBy: function(deltaX, deltaY, animate) {
        var me = this,
            dom = me.dom;

        
        if (deltaX.length) {
            animate = deltaY;
            deltaY = deltaX[1];
            deltaX = deltaX[0];
        } else if (typeof deltaX != 'number') { 
            animate = deltaY;
            deltaY = deltaX.y;
            deltaX = deltaX.x;
        }

        if (deltaX) {
            me.scrollTo('left', me.constrainScrollLeft(dom.scrollLeft + deltaX), animate);
        }
        if (deltaY) {
            me.scrollTo('top', me.constrainScrollTop(dom.scrollTop + deltaY), animate);
        }

        return me;
    },

    
    scrollTo: function(side, value, animate) {
        
        var top = /top/i.test(side),
            me = this,
            prop = top ? 'scrollTop' : 'scrollLeft',
            dom = me.dom,
            animCfg;

        if (!animate || !me.anim) {
            
            dom[prop] = value;
            
            dom[prop] = value;
        }
        else {
            animCfg = {
                to: {}
            };
            animCfg.to[prop] = value;
            if (Ext.isObject(animate)) {
                Ext.applyIf(animCfg, animate);
            }
            me.animate(animCfg);
        }
        return me;
    },

    
    scrollIntoView: function(container, hscroll, animate, highlight) {
        var me = this,
            dom = me.dom,
            offsets = me.getOffsetsTo(container = Ext.getDom(container) || Ext.getBody().dom),
        
            left = offsets[0] + container.scrollLeft,
            top = offsets[1] + container.scrollTop,
            bottom = top + dom.offsetHeight,
            right = left + dom.offsetWidth,
        
            ctClientHeight = container.clientHeight,
            ctScrollTop = parseInt(container.scrollTop, 10),
            ctScrollLeft = parseInt(container.scrollLeft, 10),
            ctBottom = ctScrollTop + ctClientHeight,
            ctRight = ctScrollLeft + container.clientWidth,
            newPos;

        
        if (highlight) {
            if (animate) {
                animate = Ext.apply({
                    listeners: {
                        afteranimate: function() {
                            me.scrollChildFly.attach(dom).highlight();
                        }
                    }
                }, animate);
            } else {
                me.scrollChildFly.attach(dom).highlight();
            }
        }

        if (dom.offsetHeight > ctClientHeight || top < ctScrollTop) {
            newPos = top;
        } else if (bottom > ctBottom) {
            newPos = bottom - ctClientHeight;
        }
        if (newPos != null) {
            me.scrollChildFly.attach(container).scrollTo('top', newPos, animate);
        }

        if (hscroll !== false) {
            newPos = null;
            if (dom.offsetWidth > container.clientWidth || left < ctScrollLeft) {
                newPos = left;
            } else if (right > ctRight) {
                newPos = right - container.clientWidth;
            }
            if (newPos != null) {
                me.scrollChildFly.attach(container).scrollTo('left', newPos, animate);
            }
        }
        return me;
    },

    
    scrollChildIntoView: function(child, hscroll) {
        this.scrollChildFly.attach(Ext.getDom(child)).scrollIntoView(this, hscroll);
    },

    
    scroll: function(direction, distance, animate) {
        if (!this.isScrollable()) {
            return false;
        }
        var me = this,
            dom = me.dom,
            side = direction === 'r' || direction === 'l' ? 'left' : 'top',
            scrolled = false,
            currentScroll, constrainedScroll;

        if (direction === 'r') {
            distance = -distance;
        }

        if (side === 'left') {
            currentScroll = dom.scrollLeft;
            constrainedScroll = me.constrainScrollLeft(currentScroll + distance);
        } else {
            currentScroll = dom.scrollTop;
            constrainedScroll = me.constrainScrollTop(currentScroll + distance);
        }

        if (constrainedScroll !== currentScroll) {
            this.scrollTo(side, constrainedScroll, animate);
            scrolled = true;
        }

        return scrolled;
    },

    constrainScrollLeft: function(left) {
        var dom = this.dom;
        return Math.max(Math.min(left, dom.scrollWidth - dom.clientWidth), 0);
    },

    constrainScrollTop: function(top) {
        var dom = this.dom;
        return Math.max(Math.min(top, dom.scrollHeight - dom.clientHeight), 0);
    }
}, function() {
    this.prototype.scrollChildFly = new this.Fly();
    this.prototype.scrolltoFly = new this.Fly();
});



Ext.define('Ext.dom.Element_style', {
    override: 'Ext.dom.Element'
},
function() {

var Element = this,
    view = document.defaultView,
    adjustDirect2DTableRe = /table-row|table-.*-group/,
    INTERNAL = '_internal',
    HIDDEN = 'hidden',
    HEIGHT = 'height',
    WIDTH = 'width',
    ISCLIPPED = 'isClipped',
    OVERFLOW = 'overflow',
    OVERFLOWX = 'overflow-x',
    OVERFLOWY = 'overflow-y',
    ORIGINALCLIP = 'originalClip',
    DOCORBODYRE = /#document|body/i,
    
    
    styleHooks, verticalStyleHooks90, verticalStyleHooks270,
    edges, k, edge, borderWidth;

if (!view || !view.getComputedStyle) {
    Element.prototype.getStyle = function (property, inline) {
        var me = this,
            dom = me.dom,
            multiple = typeof property != 'string',
            hooks = me.styleHooks,
            prop = property,
            props = prop,
            len = 1,
            isInline = inline,
            camel, domStyle, values, hook, out, style, i;

        if (multiple) {
            values = {};
            prop = props[0];
            i = 0;
            if (!(len = props.length)) {
                return values;
            }
        }

        if (!dom || dom.documentElement) {
            return values || '';
        }

        domStyle = dom.style;

        if (inline) {
            style = domStyle;
        } else {
            style = dom.currentStyle;

            
            if (!style) {
                isInline = true;
                style = domStyle;
            }
        }

        do {
            hook = hooks[prop];

            if (!hook) {
                hooks[prop] = hook = { name: Element.normalize(prop) };
            }

            if (hook.get) {
                out = hook.get(dom, me, isInline, style);
            } else {
                camel = hook.name;

                
                
                
                
                if (hook.canThrow) {
                    try {
                        out = style[camel];
                    } catch (e) {
                        out = '';
                    }
                } else {
                    
                    
                    out = style ? style[camel] : '';
                }
            }

            if (!multiple) {
                return out;
            }

            values[prop] = out;
            prop = props[++i];
        } while (i < len);

        return values;
    };
}

Element.override({
    getHeight: function(contentHeight, preciseHeight) {
        var me = this,
            hidden = me.isStyle('display', 'none'),
            height,
            floating;

        if (hidden) {
            return 0;
        }

        height = me.dom.offsetHeight;

        
        if (Ext.supports.Direct2DBug) {
            floating = me.adjustDirect2DDimension(HEIGHT);
            if (preciseHeight) {
                height += floating;
            }
            else if (floating > 0 && floating < 0.5) {
                height++;
            }
        }

        if (contentHeight) {
            height -= me.getBorderWidth("tb") + me.getPadding("tb");
        }

        return (height < 0) ? 0 : height;
    },

    getWidth: function(contentWidth, preciseWidth) {
        var me = this,
            dom = me.dom,
            hidden = me.isStyle('display', 'none'),
            rect, width, floating;

        if (hidden) {
            return 0;
        }

        
        
        
        
        
        
        if (preciseWidth && Ext.supports.BoundingClientRect) {
            rect = dom.getBoundingClientRect();
            
            
            
            
            width = (me.vertical && !Ext.isIE9 && !Ext.supports.RotatedBoundingClientRect) ?
                    (rect.bottom - rect.top) : (rect.right - rect.left);
        } else {
            width = dom.offsetWidth;
        }

        
        
        
        if (Ext.supports.Direct2DBug && !me.vertical) {
            
            floating = me.adjustDirect2DDimension(WIDTH);
            if (preciseWidth) {
                width += floating;
            }
            
            
            
            else if (floating > 0 && floating < 0.5) {
                width++;
            }
        }

        if (contentWidth) {
            width -= me.getBorderWidth("lr") + me.getPadding("lr");
        }

        return (width < 0) ? 0 : width;
    },

    setWidth: function(width, animate) {
        var me = this;
        width = me.adjustWidth(width);
        if (!animate || !me.anim) {
            me.dom.style.width = me.addUnits(width);
        }
        else {
            if (!Ext.isObject(animate)) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    width: width
                }
            }, animate));
        }
        return me;
    },

    setHeight : function(height, animate) {
        var me = this;

        height = me.adjustHeight(height);
        if (!animate || !me.anim) {
            me.dom.style.height = me.addUnits(height);
        }
        else {
            if (!Ext.isObject(animate)) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    height: height
                }
            }, animate));
        }

        return me;
    },

    applyStyles: function(style) {
        Ext.DomHelper.applyStyles(this.dom, style);
        return this;
    },

    setSize: function(width, height, animate) {
        var me = this;

        if (Ext.isObject(width)) { 
            animate = height;
            height = width.height;
            width = width.width;
        }

        width = me.adjustWidth(width);
        height = me.adjustHeight(height);

        if (!animate || !me.anim) {
            me.dom.style.width = me.addUnits(width);
            me.dom.style.height = me.addUnits(height);
        }
        else {
            if (animate === true) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    width: width,
                    height: height
                }
            }, animate));
        }

        return me;
    },

    getViewSize : function() {
        var me = this,
            dom = me.dom,
            isDoc = DOCORBODYRE.test(dom.nodeName),
            ret;

        
        if (isDoc) {
            ret = {
                width : Element.getViewWidth(),
                height : Element.getViewHeight()
            };
        } else {
            ret = {
                width : dom.clientWidth,
                height : dom.clientHeight
            };
        }

        return ret;
    },

    getSize: function(contentSize) {
        return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
    },

    

    
    adjustWidth : function(width) {
        var me = this,
            isNum = (typeof width == 'number');

        if (isNum && me.autoBoxAdjust && !me.isBorderBox()) {
            width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
        }
        return (isNum && width < 0) ? 0 : width;
    },

    
    adjustHeight : function(height) {
        var me = this,
            isNum = (typeof height == "number");

        if (isNum && me.autoBoxAdjust && !me.isBorderBox()) {
            height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
        }
        return (isNum && height < 0) ? 0 : height;
    },

    
    getColor : function(attr, defaultValue, prefix) {
        var v = this.getStyle(attr),
            color = prefix || prefix === '' ? prefix : '#',
            h, len, i=0;

        if (!v || (/transparent|inherit/.test(v))) {
            return defaultValue;
        }
        if (/^r/.test(v)) {
             v = v.slice(4, v.length - 1).split(',');
             len = v.length;
             for (; i<len; i++) {
                h = parseInt(v[i], 10);
                color += (h < 16 ? '0' : '') + h.toString(16);
            }
        } else {
            v = v.replace('#', '');
            color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
        }
        return(color.length > 5 ? color.toLowerCase() : defaultValue);
    },

    
    setOpacity: function(opacity, animate) {
        var me = this;

        if (!me.dom) {
            return me;
        }

        if (!animate || !me.anim) {
            me.setStyle('opacity', opacity);
        }
        else {
            if (typeof animate != 'object') {
                animate = {
                    duration: 350,
                    easing: 'ease-in'
                };
            }

            me.animate(Ext.applyIf({
                to: {
                    opacity: opacity
                }
            }, animate));
        }
        return me;
    },

    
    clearOpacity : function() {
        return this.setOpacity('');
    },

    
    adjustDirect2DDimension: function(dimension) {
        var me = this,
            dom = me.dom,
            display = me.getStyle('display'),
            inlineDisplay = dom.style.display,
            inlinePosition = dom.style.position,
            originIndex = dimension === WIDTH ? 0 : 1,
            currentStyle = dom.currentStyle,
            floating;

        if (display === 'inline') {
            dom.style.display = 'inline-block';
        }

        dom.style.position = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';

        
        
        
        
        
        floating = (parseFloat(currentStyle[dimension]) || parseFloat(currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;

        dom.style.position = inlinePosition;

        if (display === 'inline') {
            dom.style.display = inlineDisplay;
        }

        return floating;
    },

    
    clip : function() {
        var me = this,
            data = (me.$cache || me.getCache()).data,
            style;

        if (!data[ISCLIPPED]) {
            data[ISCLIPPED] = true;
            style = me.getStyle([OVERFLOW, OVERFLOWX, OVERFLOWY]);
            data[ORIGINALCLIP] = {
                o: style[OVERFLOW],
                x: style[OVERFLOWX],
                y: style[OVERFLOWY]
            };
            me.setStyle(OVERFLOW, HIDDEN);
            me.setStyle(OVERFLOWX, HIDDEN);
            me.setStyle(OVERFLOWY, HIDDEN);
        }
        return me;
    },

    
    unclip : function() {
        var me = this,
            data = (me.$cache || me.getCache()).data,
            clip;

        if (data[ISCLIPPED]) {
            data[ISCLIPPED] = false;
            clip = data[ORIGINALCLIP];
            if (clip.o) {
                me.setStyle(OVERFLOW, clip.o);
            }
            if (clip.x) {
                me.setStyle(OVERFLOWX, clip.x);
            }
            if (clip.y) {
                me.setStyle(OVERFLOWY, clip.y);
            }
        }
        return me;
    },

    
    boxWrap : function(cls) {
        cls = cls || Ext.baseCSSPrefix + 'box';
        var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Element.boxMarkup, cls) + "</div>"));
        Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
        return el;
    },

    
    getComputedHeight : function() {
        var me = this,
            h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
        if (!h) {
            h = parseFloat(me.getStyle(HEIGHT)) || 0;
            if (!me.isBorderBox()) {
                h += me.getFrameWidth('tb');
            }
        }
        return h;
    },

    
    getComputedWidth : function() {
        var me = this,
            w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);

        if (!w) {
            w = parseFloat(me.getStyle(WIDTH)) || 0;
            if (!me.isBorderBox()) {
                w += me.getFrameWidth('lr');
            }
        }
        return w;
    },

    
    getFrameWidth : function(sides, onlyContentBox) {
        return (onlyContentBox && this.isBorderBox()) ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
    },

    
    addClsOnOver : function(className, testFn, scope) {
        var me = this,
            dom = me.dom,
            hasTest = Ext.isFunction(testFn);
            
        me.hover(
            function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return;
                }
                Ext.fly(dom, INTERNAL).addCls(className);
            },
            function() {
                Ext.fly(dom, INTERNAL).removeCls(className);
            }
        );
        return me;
    },

    
    addClsOnFocus : function(className, testFn, scope) {
        var me = this,
            dom = me.dom,
            hasTest = Ext.isFunction(testFn);
            
        me.on("focus", function() {
            if (hasTest && testFn.call(scope || me, me) === false) {
                return false;
            }
            Ext.fly(dom, INTERNAL).addCls(className);
        });
        me.on("blur", function() {
            Ext.fly(dom, INTERNAL).removeCls(className);
        });
        return me;
    },

    
    addClsOnClick : function(className, testFn, scope) {
        var me = this,
            dom = me.dom,
            hasTest = Ext.isFunction(testFn);
            
        me.on("mousedown", function() {
            if (hasTest && testFn.call(scope || me, me) === false) {
                return false;
            }
            Ext.fly(dom, INTERNAL).addCls(className);
            var d = Ext.getDoc(),
                fn = function() {
                    Ext.fly(dom, INTERNAL).removeCls(className);
                    d.removeListener("mouseup", fn);
                };
            d.on("mouseup", fn);
        });
        return me;
    },

    
    getStyleSize : function() {
        var me = this,
            d = this.dom,
            isDoc = DOCORBODYRE.test(d.nodeName),
            s ,
            w, h;

        
        if (isDoc) {
            return {
                width : Element.getViewWidth(),
                height : Element.getViewHeight()
            };
        }

        s = me.getStyle([HEIGHT, WIDTH], true);  
        
        if (s.width && s.width != 'auto') {
            w = parseFloat(s.width);
            if (me.isBorderBox()) {
                w -= me.getFrameWidth('lr');
            }
        }
        
        if (s.height && s.height != 'auto') {
            h = parseFloat(s.height);
            if (me.isBorderBox()) {
                h -= me.getFrameWidth('tb');
            }
        }
        
        return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
    },

    statics: {
        selectableCls: Ext.baseCSSPrefix + 'selectable',
        unselectableCls: Ext.baseCSSPrefix + 'unselectable'
    },

    
    selectable : function() {
        var me = this;

        
        
        me.dom.unselectable = '';

        me.removeCls(Element.unselectableCls);
        me.addCls(Element.selectableCls);

        return me;
    },

    
    unselectable : function() {
        
        
        
        
        
        
        
        var me = this;

        
        
        
        
        
        if (Ext.isOpera) {
            me.dom.unselectable = 'on';
        }

        
        
        
        
        
        
        
        
        
        me.removeCls(Element.selectableCls);
        me.addCls(Element.unselectableCls);

        return me;
    },

    
    setVertical: function(angle, cls) {
        var me = this,
            proto = Element.prototype,
            hooks;

        me.vertical = true;
        if (cls) {
            me.addCls(me.verticalCls = cls);
        }

        me.setWidth = proto.setHeight;
        me.setHeight = proto.setWidth;
        if (!Ext.isIE9m) {
            
            
            
            
            me.getWidth = proto.getHeight;
            me.getHeight = proto.getWidth;
        }

        
        me.styleHooks = (angle === 270) ?
            Element.prototype.verticalStyleHooks270 : Element.prototype.verticalStyleHooks90;
    },

    
    setHorizontal: function() {
        var me = this,
            cls = me.verticalCls;

        delete me.vertical;
        if (cls) {
            delete me.verticalCls;
            me.removeCls(cls);
        }

        
        delete me.setWidth;
        delete me.setHeight;
        if (!Ext.isIE9m) {
            delete me.getWidth;
            delete me.getHeight;
        }

        
        delete me.styleHooks;
    }
});

Element.prototype.styleHooks = styleHooks = Ext.dom.AbstractElement.prototype.styleHooks;



Element.prototype.verticalStyleHooks90 = verticalStyleHooks90 = Ext.Object.chain(Element.prototype.styleHooks);
Element.prototype.verticalStyleHooks270 = verticalStyleHooks270 = Ext.Object.chain(Element.prototype.styleHooks);

verticalStyleHooks90.width = { name: 'height' };
verticalStyleHooks90.height = { name: 'width' };
verticalStyleHooks90['margin-top'] = { name: 'marginLeft' };
verticalStyleHooks90['margin-right'] = { name: 'marginTop' };
verticalStyleHooks90['margin-bottom'] = { name: 'marginRight' };
verticalStyleHooks90['margin-left'] = { name: 'marginBottom' };
verticalStyleHooks90['padding-top'] = { name: 'paddingLeft' };
verticalStyleHooks90['padding-right'] = { name: 'paddingTop' };
verticalStyleHooks90['padding-bottom'] = { name: 'paddingRight' };
verticalStyleHooks90['padding-left'] = { name: 'paddingBottom' };
verticalStyleHooks90['border-top'] = { name: 'borderLeft' };
verticalStyleHooks90['border-right'] = { name: 'borderTop' };
verticalStyleHooks90['border-bottom'] = { name: 'borderRight' };
verticalStyleHooks90['border-left'] = { name: 'borderBottom' };

verticalStyleHooks270.width = { name: 'height' };
verticalStyleHooks270.height = { name: 'width' };
verticalStyleHooks270['margin-top'] = { name: 'marginRight' };
verticalStyleHooks270['margin-right'] = { name: 'marginBottom' };
verticalStyleHooks270['margin-bottom'] = { name: 'marginLeft' };
verticalStyleHooks270['margin-left'] = { name: 'marginTop' };
verticalStyleHooks270['padding-top'] = { name: 'paddingRight' };
verticalStyleHooks270['padding-right'] = { name: 'paddingBottom' };
verticalStyleHooks270['padding-bottom'] = { name: 'paddingLeft' };
verticalStyleHooks270['padding-left'] = { name: 'paddingTop' };
verticalStyleHooks270['border-top'] = { name: 'borderRight' };
verticalStyleHooks270['border-right'] = { name: 'borderBottom' };
verticalStyleHooks270['border-bottom'] = { name: 'borderLeft' };
verticalStyleHooks270['border-left'] = { name: 'borderTop' };

if (Ext.isIE7m) {
    styleHooks.fontSize = styleHooks['font-size'] = {
        name: 'fontSize',
        canThrow: true
    };
    
    styleHooks.fontStyle = styleHooks['font-style'] = {
        name: 'fontStyle',
        canThrow: true
    };
    
    styleHooks.fontFamily = styleHooks['font-family'] = {
        name: 'fontFamily',
        canThrow: true
    };
}


if (Ext.isIEQuirks || Ext.isIE && Ext.ieVersion <= 8) {
    function getBorderWidth (dom, el, inline, style) {
        if (style[this.styleName] == 'none') {
            return '0px';
        }
        return style[this.name];
    }

    edges = ['Top','Right','Bottom','Left'];
    k = edges.length;

    while (k--) {
        edge = edges[k];
        borderWidth = 'border' + edge + 'Width';

        styleHooks['border-'+edge.toLowerCase()+'-width'] = styleHooks[borderWidth] = {
            name: borderWidth,
            styleName: 'border' + edge + 'Style',
            get: getBorderWidth
        };
    }
}

































Ext.getDoc().on('selectstart', function(ev, dom) {
    var doc = document.documentElement,
        selectableCls = Element.selectableCls,
        unselectableCls = Element.unselectableCls,
        tagName = dom && dom.tagName;

    tagName = tagName && tagName.toLowerCase();

    
    
    
    if (tagName === 'input' || tagName === 'textarea') {
        return;
    }

    
    while (dom && dom.nodeType === 1 && dom !== doc) {
        var el = Ext.fly(dom);

        
        if (el.hasCls(selectableCls)) {
            return;
        }

        
        if (el.hasCls(unselectableCls)) {
            ev.stopEvent();
            return;
        }

        dom = dom.parentNode;
    }
});

});

Ext.onReady(function () {
    var opacityRe = /alpha\(opacity=(.*)\)/i,
        trimRe = /^\s+|\s+$/g,
        hooks = Ext.dom.Element.prototype.styleHooks;

    
    hooks.opacity = {
        name: 'opacity',
        afterSet: function(dom, value, el) {
            if (el.isLayer) {
                el.onOpacitySet(value);
            }
        }
    };
    if (!Ext.supports.Opacity && Ext.isIE) {
        Ext.apply(hooks.opacity, {
            get: function (dom) {
                var filter = dom.style.filter,
                    match, opacity;
                if (filter.match) {
                    match = filter.match(opacityRe);
                    if (match) {
                        opacity = parseFloat(match[1]);
                        if (!isNaN(opacity)) {
                            return opacity ? opacity / 100 : 0;
                        }
                    }
                }
                return 1;
            },
            set: function (dom, value) {
                var style = dom.style,
                    val = style.filter.replace(opacityRe, '').replace(trimRe, '');

                style.zoom = 1; 

                
                if (typeof(value) == 'number' && value >= 0 && value < 1) {
                    value *= 100;
                    style.filter = val + (val.length ? ' ' : '') + 'alpha(opacity='+value+')';
                } else {
                    style.filter = val;
                }
            }  
        });
    }
    
    
});



Ext.define('Ext.util.Positionable', {

    _positionTopLeft: ['position', 'top', 'left'],

    _alignRe: /^([a-z]+)-([a-z]+)(\?)?$/,

    
    
    afterSetPosition: Ext.emptyFn,


    
    
    adjustForConstraints: function(xy, parent) {
        var vector = this.getConstrainVector(parent, xy);
        if (vector) {
            xy[0] += vector[0];
            xy[1] += vector[1];
        }
        return xy;
    },

    
    alignTo: function(element, position, offsets, animate) {
        var me = this,
            el = me.el;

        return me.setXY(me.getAlignToXY(element, position, offsets),
                el.anim && !!animate ? el.anim(animate) : false);
    },

    
    anchorTo: function(anchorToEl, alignment, offsets, animate, monitorScroll, callback) {
        var me = this,
            scroll = !Ext.isEmpty(monitorScroll),
            action = function() {
                me.alignTo(anchorToEl, alignment, offsets, animate);
                Ext.callback(callback, me);
            },
            anchor = me.getAnchor();

        
        me.removeAnchor();
        Ext.apply(anchor, {
            fn: action,
            scroll: scroll
        });

        Ext.EventManager.onWindowResize(action, null);

        if (scroll) {
            Ext.EventManager.on(window, 'scroll', action, null,
                    {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
        }
        action(); 
        return me;
    },

    
    calculateAnchorXY: function(anchor, extraX, extraY, mySize) {
        
        
        var me = this,
            el = me.el,
            doc = document,
            isViewport = el.dom == doc.body || el.dom == doc,
            round = Math.round,
            xy, myWidth, myHeight;

        anchor = (anchor || "tl").toLowerCase();
        mySize = mySize || {};

        myWidth = mySize.width || isViewport ? Ext.Element.getViewWidth() : me.getWidth();
        myHeight = mySize.height || isViewport ? Ext.Element.getViewHeight() : me.getHeight();

        
        
        switch (anchor) {
            case 'tl' : xy = [0, 0];
                        break;
            case 'bl' : xy = [0, myHeight];
                        break;
            case 'tr' : xy = [myWidth, 0];
                        break;
            case 'c'  : xy = [round(myWidth * 0.5), round(myHeight * 0.5)];
                        break;
            case 't'  : xy = [round(myWidth * 0.5), 0];
                        break;
            case 'l'  : xy = [0, round(myHeight * 0.5)];
                        break;
            case 'r'  : xy = [myWidth, round(myHeight * 0.5)];
                        break;
            case 'b'  : xy = [round(myWidth * 0.5), myHeight];
                        break;
            case 'tc' : xy = [round(myWidth * 0.5), 0];
                        break;
            case 'bc' : xy = [round(myWidth * 0.5), myHeight];
                        break;
            case 'br' : xy = [myWidth, myHeight];
        }
        return [xy[0] + extraX, xy[1] + extraY];
    },

    
    convertPositionSpec: Ext.identityFn,

    
    getAlignToXY: function(alignToEl, posSpec, offset) {
        var me = this,
            viewportWidth = Ext.Element.getViewWidth() - 10, 
            viewportHeight = Ext.Element.getViewHeight() - 10, 
            doc = document,
            docElement = doc.documentElement,
            docBody = doc.body,
            scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0),
            scrollY = (docElement.scrollTop  || docBody.scrollTop  || 0),
            alignMatch, myPosition, alignToElPosition, myWidth, myHeight,
            alignToElRegion, swapY, swapX, constrain, align1, align2,
            p1y, p1x, p2y, p2x, x, y;

        alignToEl = Ext.get(alignToEl.el || alignToEl);

        if (!alignToEl || !alignToEl.dom) {
        }

        offset = offset || [0,0];
        posSpec = (!posSpec || posSpec == "?" ? "tl-bl?" :
            (!(/-/).test(posSpec) && posSpec !== "" ? "tl-" + posSpec : posSpec || "tl-bl")).toLowerCase();

        posSpec = me.convertPositionSpec(posSpec);

        alignMatch = posSpec.match(me._alignRe);


        align1 = alignMatch[1];
        align2 = alignMatch[2];
        constrain = !!alignMatch[3];

        
        
        myPosition = me.getAnchorXY(align1, true);
        alignToElPosition = me.getAnchorToXY(alignToEl, align2, false);

        x = alignToElPosition[0] - myPosition[0] + offset[0];
        y = alignToElPosition[1] - myPosition[1] + offset[1];

        
        if (constrain) {
            myWidth = me.getWidth();
            myHeight = me.getHeight();
            alignToElRegion = alignToEl.getRegion();
            
            
            
            
            p1y = align1.charAt(0);
            p1x = align1.charAt(align1.length - 1);
            p2y = align2.charAt(0);
            p2x = align2.charAt(align2.length - 1);
            swapY = ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t"));
            swapX = ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r"));

            if (x + myWidth > viewportWidth + scrollX) {
                x = swapX ? alignToElRegion.left - myWidth : viewportWidth + scrollX - myWidth;
            }
            if (x < scrollX) {
                x = swapX ? alignToElRegion.right : scrollX;
            }
            if (y + myHeight > viewportHeight + scrollY) {
                y = swapY ? alignToElRegion.top - myHeight : viewportHeight + scrollY - myHeight;
            }
            if (y < scrollY) {
                y = swapY ? alignToElRegion.bottom : scrollY;
            }
        }
        return [x,y];
    },

    
    getAnchor: function(){
        var el = this.el,
            data = (el.$cache || el.getCache()).data,
            anchor;
            
        if (!el.dom) {
            return;
        }
        anchor = data._anchor;

        if(!anchor){
            anchor = data._anchor = {};
        }
        return anchor;
    },

    
    getAnchorXY: function(anchor, local, mySize) {
        var me = this,
            myPos = me.getXY(),
            el = me.el,
            doc = document,
            isViewport = el.dom == doc.body || el.dom == doc,
            scroll = el.getScroll(),
            extraX = isViewport ? scroll.left : local ? 0 : myPos[0],
            extraY = isViewport ? scroll.top : local ? 0 : myPos[1];

        return me.calculateAnchorXY(anchor, extraX, extraY, mySize);
    },

    
    getBox: function(contentBox, local) {
        var me = this,
            xy = local ? me.getLocalXY() : me.getXY(),
            x = xy[0],
            y = xy[1],
            w = me.getWidth(),
            h = me.getHeight(),
            borderPadding, beforeX, beforeY;

        if (contentBox) {
            borderPadding = me.getBorderPadding();
            beforeX = borderPadding.beforeX;
            beforeY = borderPadding.beforeY;

            x += beforeX;
            y += beforeY;
            w -= (beforeX + borderPadding.afterX);
            h -= (beforeY + borderPadding.afterY);
        }

        return {
            x: x,
            left: x,
            0: x,
            y: y,
            top: y,
            1: y,
            width: w,
            height: h,
            right: x + w,
            bottom: y + h
        };
    },

    
    calculateConstrainedPosition: function(constrainTo, proposedPosition, local, proposedSize) {
        var me = this,
            vector,
            fp = me.floatParent,
            parentNode = fp ? fp.getTargetEl() : null,
            parentOffset,
            borderPadding,
            proposedConstrainPosition,
            xy = false;

        if (local && fp) {
            parentOffset = parentNode.getXY();
            borderPadding = parentNode.getBorderPadding();
            parentOffset[0] += borderPadding.beforeX;
            parentOffset[1] += borderPadding.beforeY;
            if (proposedPosition) {
                proposedConstrainPosition = [proposedPosition[0] + parentOffset[0], proposedPosition[1] + parentOffset[1]];
            }
        } else {
            proposedConstrainPosition = proposedPosition;
        }
        
        
        
        constrainTo = constrainTo || me.constrainTo || parentNode || me.container || me.el.parent();
        vector = (me.constrainHeader ? me.header : me).getConstrainVector(constrainTo, proposedConstrainPosition, proposedSize);

        
        if (vector) {
            xy = proposedPosition || me.getPosition(local);
            xy[0] += vector[0];
            xy[1] += vector[1];
        }
        return xy;
    },

    
    getConstrainVector: function(constrainTo, proposedPosition, proposedSize) {
        var thisRegion = this.getRegion(),
            vector = [0, 0],
            shadowSize = (this.shadow && this.constrainShadow && !this.shadowDisabled) ? this.shadow.getShadowSize() : undefined,
            overflowed = false,
            constraintInsets = this.constraintInsets;

        if (!(constrainTo instanceof Ext.util.Region)) {
            constrainTo = Ext.get(constrainTo.el || constrainTo).getViewRegion();
        }

        
        if (constraintInsets) {
            constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets);
            constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.length);
        }

        
        if (proposedPosition) {
            thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
        }
        
        if (proposedSize) {
            thisRegion.right = thisRegion.left + proposedSize[0];
            thisRegion.bottom = thisRegion.top + proposedSize[1];
        }

        
        if (shadowSize) {
            constrainTo.adjust(shadowSize[0], -shadowSize[1], -shadowSize[2], shadowSize[3]);
        }

        
        if (thisRegion.right > constrainTo.right) {
            overflowed = true;
            vector[0] = (constrainTo.right - thisRegion.right);    
        }
        if (thisRegion.left + vector[0] < constrainTo.left) {
            overflowed = true;
            vector[0] = (constrainTo.left - thisRegion.left);      
        }

        
        if (thisRegion.bottom > constrainTo.bottom) {
            overflowed = true;
            vector[1] = (constrainTo.bottom - thisRegion.bottom);  
        }
        if (thisRegion.top + vector[1] < constrainTo.top) {
            overflowed = true;
            vector[1] = (constrainTo.top - thisRegion.top);        
        }
        return overflowed ? vector : false;
    },

    
    getOffsetsTo: function(offsetsTo) {
        var o = this.getXY(),
                e = Ext.fly(offsetsTo.el || offsetsTo, '_internal').getXY();
        return [o[0] - e[0],o[1] - e[1]];
    },

    
    getRegion: function() {
        var box = this.getBox();
        return new Ext.util.Region(box.top, box.right, box.bottom, box.left);
    },

    
    getViewRegion: function() {
        var me = this,
            el = me.el,
            isBody = el.dom.nodeName === 'BODY',
            borderPadding, scroll, pos, top, left, width, height;

        
        if (isBody) {
            scroll = el.getScroll();
            left = scroll.left;
            top = scroll.top;
            width = Ext.dom.AbstractElement.getViewportWidth();
            height = Ext.dom.AbstractElement.getViewportHeight();
        }
        else {
            borderPadding = me.getBorderPadding();
            pos = me.getXY();
            left = pos[0] + borderPadding.beforeX;
            top = pos[1] + borderPadding.beforeY;
            width = me.getWidth(true);
            height = me.getHeight(true);
        }

        return new Ext.util.Region(top, left + width, top + height, left);
    },

    
    move: function(direction, distance, animate) {
        var me = this,
            xy = me.getXY(),
            x = xy[0],
            y = xy[1],
            left = [x - distance, y],
            right = [x + distance, y],
            top = [x, y - distance],
            bottom = [x, y + distance],
            hash = {
                l: left,
                left: left,
                r: right,
                right: right,
                t: top,
                top: top,
                up: top,
                b: bottom,
                bottom: bottom,
                down: bottom
            };

        direction = direction.toLowerCase();
        me.setXY([hash[direction][0], hash[direction][1]], animate);
    },

    
    removeAnchor: function() {
        var anchor = this.getAnchor();

        if (anchor && anchor.fn) {
            Ext.EventManager.removeResizeListener(anchor.fn);
            if (anchor.scroll) {
                Ext.EventManager.un(window, 'scroll', anchor.fn);
            }
            delete anchor.fn;
        }
        return this;
    },

    
    setBox: function(box, animate) {
        var me = this,
            el = me.el,
            x = box.x,
            y = box.y,
            xy = [x, y],
            w = box.width,
            h = box.height,
            doConstrain = (me.constrain || me.constrainHeader),
            constrainedPos = doConstrain && me.calculateConstrainedPosition(null, [x, y], false, [w, h]);

        
        if (constrainedPos) {
            x = constrainedPos[0];
            y = constrainedPos[1];
        }
        if (!animate || !el.anim) {
            me.setSize(w, h);
            me.setXY([x, y]);
            me.afterSetPosition(x, y);
        } else {
            me.animate(Ext.applyIf({
                to: {
                    x: x,
                    y: y,
                    width: el.adjustWidth(w),
                    height: el.adjustHeight(h)
                },
                listeners: {
                    afteranimate: Ext.Function.bind(me.afterSetPosition, me, [x, y])
                }
            }, animate));
        }
        return me;
    },

    
    setRegion: function(region, animate) {
        return this.setBox({
            x: region.left,
            y: region.top,
            width: region.right - region.left,
            height: region.bottom - region.top
        }, animate);
    },

    
    translatePoints: function(x, y) {
        var pos = this.translateXY(x, y);

        return {
            left: pos.x,
            top: pos.y
        };
    },

    
    translateXY: function(x, y) {
        var me = this,
            el = me.el,
            styles = el.getStyle(me._positionTopLeft),
            relative = styles.position == 'relative',
            left = parseFloat(styles.left),
            top = parseFloat(styles.top),
            xy = me.getXY();

        if (Ext.isArray(x)) {
             y = x[1];
             x = x[0];
        }
        if (isNaN(left)) {
            left = relative ? 0 : el.dom.offsetLeft;
        }
        if (isNaN(top)) {
            top = relative ? 0 : el.dom.offsetTop;
        }
        left = (typeof x == 'number') ? x - xy[0] + left : undefined;
        top = (typeof y == 'number') ? y - xy[1] + top : undefined;
        return {
            x: left,
            y: top
        };
    }
});



Ext.define('Ext.dom.Element', function(Element) {
    var HIDDEN          = 'hidden',
        DOC             = document,
        VISIBILITY      = "visibility",
        DISPLAY         = "display",
        NONE            = "none",
        XMASKED         = Ext.baseCSSPrefix + "masked",
        XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
        EXTELMASKMSG    = Ext.baseCSSPrefix + "mask-msg",
        bodyRe          = /^body/i,
        visFly,

        
        noBoxAdjust = Ext.isStrict ? {
            select: 1
        }: {
            input: 1,
            select: 1,
            textarea: 1
        },

        
        isScrolled = function(c) {
            var r = [], ri = -1,
                i, ci;
            for (i = 0; ci = c[i]; i++) {
                if (ci.scrollTop > 0 || ci.scrollLeft > 0) {
                    r[++ri] = ci;
                }
            }
            return r;
        };

    return {

        extend:  Ext.dom.AbstractElement ,

        alternateClassName: ['Ext.Element', 'Ext.core.Element'],

                   
                            
                                   
                                 
                                 
                                       
                                     
                                   
          
        
        tableTagRe: /^(?:tr|td|table|tbody)$/i,

        mixins: [
             Ext.util.Positionable 
        ],

        addUnits: function() {
            return Element.addUnits.apply(Element, arguments);
        },

        
        focus: function(defer,  dom) {
            var me = this;

            dom = dom || me.dom;
            try {
                if (Number(defer)) {
                    Ext.defer(me.focus, defer, me, [null, dom]);
                } else {
                    dom.focus();
                }
            } catch(e) {
            }
            return me;
        },

        
        blur: function() {
            var me = this,
                dom = me.dom;
            
            
            if (dom !== document.body) {
                try {
                    dom.blur();
                } catch(e) {
                }
                return me;
            } else {
                return me.focus(undefined, dom);
            }
        },

        
        isBorderBox: function() {
            var box = Ext.isBorderBox;
            
            
            if (box && Ext.isIE7m) {
                box = !((this.dom.tagName || "").toLowerCase() in noBoxAdjust);
            }
            return box;
        },

        
        hover: function(overFn, outFn, scope, options) {
            var me = this;
            me.on('mouseenter', overFn, scope || me.dom, options);
            me.on('mouseleave', outFn, scope || me.dom, options);
            return me;
        },

        
        getAttributeNS: function(ns, name) {
            return this.getAttribute(name, ns);
        },

        getAttribute: (Ext.isIE && !(Ext.isIE9p && DOC.documentMode >= 9)) ?

            
            
            
            
            
            
            
            
            

            function(name, ns) {
                var d = this.dom,
                        type;
                if (ns) {
                    type = typeof d[ns + ":" + name];
                    if (type != 'undefined' && type != 'unknown') {
                        return d[ns + ":" + name] || null;
                    }
                    return null;
                }
                if (name === "for") {
                    name = "htmlFor";
                }
                return d[name] || null;
            } : function(name, ns) {
                var d = this.dom;
                if (ns) {
                    return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
                }
                return  d.getAttribute(name) || d[name] || null;
            },

        
        cacheScrollValues: function() {
            var me = this,
                scrolledDescendants,
                el, i,
                scrollValues = [],
                result = function() {
                    for (i = 0; i < scrolledDescendants.length; i++) {
                        el = scrolledDescendants[i];
                        el.scrollLeft = scrollValues[i][0];
                        el.scrollTop  = scrollValues[i][1];
                    }
                };

            if (!Ext.DomQuery.pseudos.isScrolled) {
                Ext.DomQuery.pseudos.isScrolled = isScrolled;
            }
            scrolledDescendants = me.query(':isScrolled');
            for (i = 0; i < scrolledDescendants.length; i++) {
                el = scrolledDescendants[i];
                scrollValues[i] = [el.scrollLeft, el.scrollTop];
            }
            return result;
        },

        
        autoBoxAdjust: true,

        
        isVisible : function(deep) {
            var me = this,
                dom = me.dom,
                stopNode = dom.ownerDocument.documentElement;

            if (!visFly) {
                visFly = new Element.Fly();
            }

            while (dom !== stopNode) {
                
                
                if (!dom || dom.nodeType === 11 || (visFly.attach(dom)).isStyle(VISIBILITY, HIDDEN) || visFly.isStyle(DISPLAY, NONE)) {
                    return false;
                }
                
                if (!deep) {
                    break;
                }
                dom = dom.parentNode;
            }
            return true;
        },

        
        isDisplayed : function() {
            return !this.isStyle(DISPLAY, NONE);
        },

        
        enableDisplayMode : function(display) {
            var me = this;

            me.setVisibilityMode(Element.DISPLAY);

            if (!Ext.isEmpty(display)) {
                (me.$cache || me.getCache()).data.originalDisplay = display;
            }

            return me;
        },

        
        mask : function(msg, msgCls , elHeight) {
            var me            = this,
                dom           = me.dom,
                
                
                setExpression = dom.style.setExpression,
                data          = (me.$cache || me.getCache()).data,
                maskShimEl    = data.maskShimEl,
                maskEl        = data.maskEl,
                maskMsg       = data.maskMsg,
                widthExpression, heightExpression;

            if (!(bodyRe.test(dom.tagName) && me.getStyle('position') == 'static')) {
                me.addCls(XMASKEDRELATIVE);
            }

            
            if (maskEl) {
                maskEl.remove();
            }

            if (maskMsg) {
                maskMsg.remove();
            }

            if (maskShimEl) {
                maskShimEl.remove();
            }

            if (Ext.isIE6) {
                maskShimEl = Ext.DomHelper.append(dom, {
                    tag: 'iframe',
                    cls : Ext.baseCSSPrefix + 'shim ' + Ext.baseCSSPrefix + 'mask-shim'
                }, true);
                data.maskShimEl = maskShimEl;
                maskShimEl.setDisplayed(true);
            }

            Ext.DomHelper.append(dom, [{
                cls : Ext.baseCSSPrefix + "mask",
                style: 'top:0;left:0;'
            }, {
                cls : msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG,
                cn  : {
                    tag: 'div',
                    cls: Ext.baseCSSPrefix + 'mask-msg-inner',
                    cn: {
                        tag: 'div',
                        cls: Ext.baseCSSPrefix + 'mask-msg-text',
                        html: msg || ''
                    }
                }
            }]);

            maskMsg = Ext.get(dom.lastChild);
            maskEl = Ext.get(maskMsg.dom.previousSibling);
            data.maskMsg = maskMsg;
            data.maskEl = maskEl;

            me.addCls(XMASKED);
            maskEl.setDisplayed(true);

            if (typeof msg == 'string') {
                maskMsg.setDisplayed(true);
                maskMsg.center(me);
            } else {
                maskMsg.setDisplayed(false);
            }
            
            
            
            
            
            if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
                
                try {
                    maskEl.dom.style.setExpression('width', 'this.parentNode.clientWidth + "px"');
                    widthExpression = 'this.parentNode.clientWidth + "px"';
                    if (maskShimEl) {
                        maskShimEl.dom.style.setExpression('width', widthExpression);
                    }
                    maskEl.dom.style.setExpression('width', widthExpression);
                } catch (e) {}
            }

            
            
            if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
                
                try {
                    heightExpression = 'this.parentNode.' + (dom == DOC.body ? 'scrollHeight' : 'offsetHeight') + ' + "px"';
                    if (maskShimEl) {
                        maskShimEl.dom.style.setExpression('height', heightExpression);
                    }
                    maskEl.dom.style.setExpression('height', heightExpression);
                } catch (e) {}
            }
            
            else if (Ext.isIE9m && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
                if (maskShimEl) {
                    maskShimEl.setSize(undefined, elHeight || me.getHeight());
                }
                maskEl.setSize(undefined, elHeight || me.getHeight());
            }
            return maskEl;
        },

        
        unmask : function() {
            var me      = this,
                data    = (me.$cache || me.getCache()).data,
                maskEl  = data.maskEl,
                maskShimEl = data.maskShimEl,
                maskMsg = data.maskMsg,
                style;

            if (maskEl) {
                style = maskEl.dom.style;
                
                if (style.clearExpression) {
                    style.clearExpression('width');
                    style.clearExpression('height');
                }

                if (maskEl) {
                    maskEl.remove();
                    delete data.maskEl;
                }

                if (maskMsg) {
                    maskMsg.remove();
                    delete data.maskMsg;
                }

                me.removeCls([XMASKED, XMASKEDRELATIVE]);

                if (maskShimEl) {
                    style = maskShimEl.dom.style;
                    
                    if (style.clearExpression) {
                        style.clearExpression('width');
                        style.clearExpression('height');
                    }

                    maskShimEl.remove();
                    delete data.maskShimEl;
                }
            }
        },

        
        isMasked : function() {
            var me      = this,
                data    = (me.$cache || me.getCache()).data,
                maskEl  = data.maskEl,
                maskMsg = data.maskMsg,
                hasMask = false; 

            if (maskEl && maskEl.isVisible()) {
                if (maskMsg) {
                    maskMsg.center(me);
                }
                hasMask = true;
            }
            return hasMask;
        },

        
        createShim : function() {
            var el = DOC.createElement('iframe'),
                shim;

            el.frameBorder = '0';
            el.className = Ext.baseCSSPrefix + 'shim';
            el.src = Ext.SSL_SECURE_URL;
            shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
            shim.autoBoxAdjust = false;
            return shim;
        },

        
        addKeyListener : function(key, fn, scope){
            var config;
            if(typeof key != 'object' || Ext.isArray(key)){
                config = {
                    target: this,
                    key: key,
                    fn: fn,
                    scope: scope
                };
            }else{
                config = {
                    target: this,
                    key : key.key,
                    shift : key.shift,
                    ctrl : key.ctrl,
                    alt : key.alt,
                    fn: fn,
                    scope: scope
                };
            }
            return new Ext.util.KeyMap(config);
        },

        
        addKeyMap : function(config) {
            return new Ext.util.KeyMap(Ext.apply({
                target: this
            }, config));
        },

        
        
        
        
        
        
        
        
        
        
        

        
        
        
        

        
        
        
        
        
        
        

        
        
        
        
        
        
        

        
        
        
        

        
        
        
        
        
        
        
        

        
        on: function(eventName, fn, scope, options) {
            Ext.EventManager.on(this, eventName, fn, scope || this, options);
            return this;
        },

        
        un: function(eventName, fn, scope) {
            Ext.EventManager.un(this, eventName, fn, scope || this);
            return this;
        },

        
        removeAllListeners: function() {
            Ext.EventManager.removeAll(this);
            return this;
        },

        
        purgeAllListeners: function() {
            Ext.EventManager.purgeElement(this);
            return this;
        },

        select: function(selector) {
            return Element.select(selector, false,  this.dom);
        }
    };
}, function() {

    var DOC             = document,
        EC              = Ext.cache,
        Element         = this,
        AbstractElement = Ext.dom.AbstractElement,
        focusRe         = /^a|button|embed|iframe|input|object|select|textarea$/i,
        nonSpaceRe      = /\S/,
        scriptTagRe     = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        replaceScriptTagRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        srcRe           = /\ssrc=([\'\"])(.*?)\1/i,
        typeRe          = /\stype=([\'\"])(.*?)\1/i,
        useDocForId     = !Ext.isIE8m,
        internalFly;

    Element.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
    

    
    
    
    function garbageCollect() {
        if (!Ext.enableGarbageCollector) {
            clearInterval(Element.collectorThreadId);
        } else {
            var eid,
                d,
                o,
                t;

            for (eid in EC) {
                if (!EC.hasOwnProperty(eid)) {
                    continue;
                }

                o = EC[eid];

                
                if (o.skipGarbageCollection) {
                    continue;
                }

                d = o.dom;


                
                
                
                
                
                
                
                
                
                
                
                
                
                
                if (d && (!d.parentNode || (!d.offsetParent && !Ext.getElementById(eid)))) {
                    if (Ext.enableListenerCollection) {
                        Ext.EventManager.removeAll(d);
                    }
                    delete EC[eid];
                }
            }
            
            if (Ext.isIE) {
                t = {};
                for (eid in EC) {
                    if (!EC.hasOwnProperty(eid)) {
                        continue;
                    }
                    t[eid] = EC[eid];
                }
                EC = Ext.cache = t;
            }
        }
    }

    Element.collectorThreadId = setInterval(garbageCollect, 30000);

    
    Element.addMethods({

        
        monitorMouseLeave: function(delay, handler, scope) {
            var me = this,
                timer,
                listeners = {
                    mouseleave: function(e) {
                        timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
                    },
                    mouseenter: function() {
                        clearTimeout(timer);
                    },
                    freezeEvent: true
                };

            me.on(listeners);
            return listeners;
        },

        
        swallowEvent : function(eventName, preventDefault) {
            var me = this,
                e, eLen,
                fn = function(e) {
                    e.stopPropagation();
                    if (preventDefault) {
                        e.preventDefault();
                    }
                };

            if (Ext.isArray(eventName)) {
                eLen = eventName.length;

                for (e = 0; e < eLen; e++) {
                    me.on(eventName[e], fn);
                }

                return me;
            }
            me.on(eventName, fn);
            return me;
        },

        
        relayEvent : function(eventName, observable) {
            this.on(eventName, function(e) {
                observable.fireEvent(eventName, e);
            });
        },

        
        clean : function(forceReclean) {
            var me   = this,
                dom  = me.dom,
                data = (me.$cache || me.getCache()).data,
                n    = dom.firstChild,
                ni   = -1,
                nx;

            if (data.isCleaned && forceReclean !== true) {
                return me;
            }

            while (n) {
                nx = n.nextSibling;
                if (n.nodeType == 3) {
                    
                    if (!(nonSpaceRe.test(n.nodeValue))) {
                        dom.removeChild(n);
                    
                    } else if (nx && nx.nodeType == 3) {
                        n.appendData(Ext.String.trim(nx.data));
                        dom.removeChild(nx);
                        nx = n.nextSibling;
                        n.nodeIndex = ++ni;
                    }
                } else {
                    
                    internalFly.attach(n).clean();
                    n.nodeIndex = ++ni;
                }
                n = nx;
            }

            data.isCleaned = true;
            return me;
        },

        
        load : function(options) {
            this.getLoader().load(options);
            return this;
        },

        
        getLoader : function() {
            var me = this,
                data = (me.$cache || me.getCache()).data,
                loader = data.loader;

            if (!loader) {
                data.loader = loader = new Ext.ElementLoader({
                    target: me
                });
            }
            return loader;
        },

        
        syncContent: function(source) {
            source = Ext.getDom(source);
            var sourceNodes = source.childNodes,
                sourceLen = sourceNodes.length,
                dest = this.dom,
                destNodes = dest.childNodes,
                destLen = destNodes.length,
                i,  destNode, sourceNode,
                nodeType, newAttrs, attLen, attName;

            
            
            
            
            if (Ext.isIE9m && dest.mergeAttributes) {
                dest.mergeAttributes(source, true);

                
                
                dest.src = source.src;
            } else {
                newAttrs = source.attributes;
                attLen = newAttrs.length;
                for (i = 0; i < attLen; i++) {
                    attName = newAttrs[i].name;
                    if (attName !== 'id') {
                        dest.setAttribute(attName, newAttrs[i].value);
                    }
                }
            }

            
            if (sourceLen !== destLen) {
                dest.innerHTML = source.innerHTML;
                return;
            }

            
            
            for (i = 0; i < sourceLen; i++) {
                sourceNode = sourceNodes[i];
                destNode = destNodes[i];
                nodeType = sourceNode.nodeType;

                
                if (nodeType !== destNode.nodeType || (nodeType === 1 && sourceNode.tagName !== destNode.tagName)) {
                    dest.innerHTML = source.innerHTML;
                    return;
                }

                
                if (nodeType === 3) {
                    destNode.data = sourceNode.data;
                }
                
                else {
                    if (sourceNode.id && destNode.id !== sourceNode.id) {
                        destNode.id = sourceNode.id;
                    }
                    destNode.style.cssText = sourceNode.style.cssText;
                    destNode.className = sourceNode.className;
                    internalFly.attach(destNode).syncContent(sourceNode);
                }
            }
        },

        
        update : function(html, loadScripts, callback) {
            var me = this,
                id,
                dom,
                interval;

            if (!me.dom) {
                return me;
            }
            html = html || '';
            dom = me.dom;

            if (loadScripts !== true) {
                dom.innerHTML = html;
                Ext.callback(callback, me);
                return me;
            }

            id  = Ext.id();
            html += '<span id="' + id + '"></span>';

            interval = setInterval(function() {
                var hd,
                    match,
                    attrs,
                    srcMatch,
                    typeMatch,
                    el,
                    s;
                if (!(el = DOC.getElementById(id))) {
                    return false;
                }
                clearInterval(interval);
                Ext.removeNode(el);
                hd = Ext.getHead().dom;

                while ((match = scriptTagRe.exec(html))) {
                    attrs = match[1];
                    srcMatch = attrs ? attrs.match(srcRe) : false;
                    if (srcMatch && srcMatch[2]) {
                       s = DOC.createElement("script");
                       s.src = srcMatch[2];
                       typeMatch = attrs.match(typeRe);
                       if (typeMatch && typeMatch[2]) {
                           s.type = typeMatch[2];
                       }
                       hd.appendChild(s);
                    } else if (match[2] && match[2].length > 0) {
                        if (window.execScript) {
                           window.execScript(match[2]);
                        } else {
                           window.eval(match[2]);
                        }
                    }
                }
                Ext.callback(callback, me);
            }, 20);
            dom.innerHTML = html.replace(replaceScriptTagRe, '');
            return me;
        },

        
        removeAllListeners : function() {
            this.removeAnchor();
            Ext.EventManager.removeAll(this.dom);
            return this;
        },

        
        createProxy : function(config, renderTo, matchBox) {
            config = (typeof config == 'object') ? config : {tag : "div", cls: config};

            var me = this,
                proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
                                   Ext.DomHelper.insertBefore(me.dom, config, true);

            proxy.setVisibilityMode(Element.DISPLAY);
            proxy.hide();
            if (matchBox && me.setBox && me.getBox) { 
               proxy.setBox(me.getBox());
            }
            return proxy;
        },
        
        
        needsTabIndex: function() {
            if (this.dom) {
                if ((this.dom.nodeName === 'a') && (!this.dom.href)) {
                    return true;
                }
                return !focusRe.test(this.dom.nodeName);
            }
        },

        
        isFocusable: function ( asFocusEl) {
            var dom = this.dom,
                tabIndexAttr = dom.getAttributeNode('tabIndex'),
                tabIndex,
                nodeName = dom.nodeName,
                canFocus = false;

            
            
            
            
            
            
            
            
            
            
            if (tabIndexAttr && tabIndexAttr.specified) {
                tabIndex = tabIndexAttr.value;
            }
            if (dom && !dom.disabled) {
                
                
                if (tabIndex == -1) { 
                    canFocus = Ext.FocusManager && Ext.FocusManager.enabled && asFocusEl;
                }
                else {
                    
                    if (focusRe.test(nodeName)) {
                        if ((nodeName !== 'a') || dom.href) {
                            canFocus = true;
                        }
                    }
                    
                    else {
                        canFocus = tabIndex != null && tabIndex >= 0;
                    }
                }
                canFocus = canFocus && this.isVisible(true);
            }
            return canFocus;
        }
    });

    if (Ext.isIE) {
        Element.prototype.getById = function (id, asDom) {
            var dom = this.dom,
                cacheItem, el, ret;

            if (dom) {
                
                
                el = (useDocForId && DOC.getElementById(id)) || dom.all[id];
                if (el) {
                    if (asDom) {
                        ret = el;
                    } else {
                        
                        
                        cacheItem = EC[id];
                        if (cacheItem && cacheItem.el) {
                            ret = Ext.updateCacheEntry(cacheItem, el).el;
                        } else {
                            ret = new Element(el);
                        }
                    }
                    return ret;
                }
            }

            return asDom ? Ext.getDom(id) : Element.get(id);
        };
    }

    Element.createAlias({
        
        addListener: 'on',
        
        removeListener: 'un',
        
        clearListeners: 'removeAllListeners',
        
        focusable: 'isFocusable'
    });

    Element.Fly = AbstractElement.Fly = new Ext.Class({
        extend: Element,

        isFly: true,

        constructor: function(dom) {
            this.dom = dom;
            
            
            
            this.el = this;
        },
        
        attach: AbstractElement.Fly.prototype.attach
    });
    
    internalFly = new Element.Fly();

    if (Ext.isIE) {
        Ext.getElementById = function (id) {
            var el = DOC.getElementById(id),
                detachedBodyEl;

            if (!el && (detachedBodyEl = AbstractElement.detachedBodyEl)) {
                el = detachedBodyEl.dom.all[id];
            }

            return el;
        };
    } else if (!DOC.querySelector) {
        Ext.getDetachedBody = Ext.getBody;

        Ext.getElementById = function (id) {
            return DOC.getElementById(id);
        };
    }
});



Ext.define('Ext.dom.CompositeElementLite', {
    alternateClassName: 'Ext.CompositeElementLite',

                                                   

    statics: {
        
        importElementMethods: function() {
            var name,
                elementPrototype = Ext.dom.Element.prototype,
                prototype = this.prototype;

            for (name in elementPrototype) {
                if (typeof elementPrototype[name] == 'function'){
                    (function(key) {
                        prototype[key] = prototype[key] || function() {
                            return this.invoke(key, arguments);
                        };
                    }).call(prototype, name);

                }
            }
        }
    },

    constructor: function(elements, root) {
        
        this.elements = [];
        this.add(elements, root);
        this.el = new Ext.dom.AbstractElement.Fly();
    },

    
    isComposite: true,

    
    getElement: function(el) {
        
        return this.el.attach(el);
    },

    
    transformElement: function(el) {
        return Ext.getDom(el);
    },

    
    getCount: function() {
        return this.elements.length;
    },

    
    add: function(els, root) {
        var elements = this.elements,
            i, ln;

        if (!els) {
            return this;
        }

        if (typeof els == "string") {
            els = Ext.dom.Element.selectorFunction(els, root);
        }
        else if (els.isComposite) {
            els = els.elements;
        }
        else if (!Ext.isIterable(els)) {
            els = [els];
        }

        for (i = 0, ln = els.length; i < ln; ++i) {
            elements.push(this.transformElement(els[i]));
        }

        return this;
    },

    invoke: function(fn, args) {
        var elements = this.elements,
            ln = elements.length,
            element,
            i;

        fn = Ext.dom.Element.prototype[fn];
        for (i = 0; i < ln; i++) {
            element = elements[i];

            if (element) {
                fn.apply(this.getElement(element), args);
            }
        }
        return this;
    },

    
    item: function(index) {
        var el = this.elements[index],
            out = null;

        if (el) {
            out = this.getElement(el);
        }

        return out;
    },

    
    slice: function() {
        return this.elements.slice.apply(this.elements, arguments);
    },

    
    addListener: function(eventName, handler, scope, opt) {
        var els = this.elements,
                len = els.length,
                i, e;

        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                Ext.EventManager.on(e, eventName, handler, scope || e, opt);
            }
        }
        return this;
    },
    
    each: function(fn, scope) {
        var me  = this,
            els = me.elements,
            len = els.length,
            i, e;

        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                e = this.getElement(e);
                if (fn.call(scope || e, e, me, i) === false) {
                    break;
                }
            }
        }
        return me;
    },

    
    fill: function(els) {
        var me = this;
        me.elements = [];
        me.add(els);
        return me;
    },

    insert: function(index, nodes) {
        Ext.Array.insert(this.elements, index, nodes);
    },

    
    filter: function(selector) {
        var me  = this,
            els = me.elements,
            len = els.length,
            out = [],
            i = 0,
            isFunc = typeof selector == 'function',
            add,
            el;

        for (; i < len; i++) {
            el = els[i];
            add = false;
            if (el) {
                el = me.getElement(el);

                if (isFunc) {
                    add = selector.call(el, el, me, i) !== false;
                } else {
                    add = el.is(selector);
                }
                
                if (add) {
                    out.push(me.transformElement(el));
                }
            }
        }

        me.elements = out;
        return me;
    },

    
    indexOf: function(el) {
        return Ext.Array.indexOf(this.elements, this.transformElement(el));
    },

    
    replaceElement: function(el, replacement, domReplace) {
        var index = !isNaN(el) ? el : this.indexOf(el),
                d;
        if (index > -1) {
            replacement = Ext.getDom(replacement);
            if (domReplace) {
                d = this.elements[index];
                d.parentNode.insertBefore(replacement, d);
                Ext.removeNode(d);
            }
            Ext.Array.splice(this.elements, index, 1, replacement);
        }
        return this;
    },

    
    clear: function(removeDom) {
        var me  = this,
            els = me.elements,
            i = els.length - 1;
        
        if (removeDom) {
            for (; i >= 0; i--) {
                Ext.removeNode(els[i]);
            }
        }
        this.elements = [];
    },

    addElements: function(els, root) {
        if (!els) {
            return this;
        }

        if (typeof els == "string") {
            els = Ext.dom.Element.selectorFunction(els, root);
        }

        var yels = this.elements,
            eLen = els.length,
            e;

        for (e = 0; e < eLen; e++) {
            yels.push(Ext.get(els[e]));
        }

        return this;
    },

    
    first: function() {
        return this.item(0);
    },

    
    last: function() {
        return this.item(this.getCount() - 1);
    },

    
    contains: function(el) {
        return this.indexOf(el) != -1;
    },

    
    removeElement: function(keys, removeDom) {
        keys = [].concat(keys);

        var me       = this,
            elements = me.elements,
            kLen     = keys.length,
            val, el, k;

        for (k = 0; k < kLen; k++) {
            val = keys[k];

            if ((el = (elements[val] || elements[val = me.indexOf(val)]))) {
                if (removeDom) {
                    if (el.dom) {
                        el.remove();
                    } else {
                        Ext.removeNode(el);
                    }
                }
                Ext.Array.erase(elements, val, 1);
            }
        }

        return me;
    }

}, function() {
    this.importElementMethods();

    this.prototype.on = this.prototype.addListener;

    if (Ext.DomQuery){
        Ext.dom.Element.selectorFunction = Ext.DomQuery.select;
    }

    
   Ext.dom.Element.select = function(selector, root) {
        var elements;

        if (typeof selector == "string") {
            elements = Ext.dom.Element.selectorFunction(selector, root);
        }
        else if (selector.length !== undefined) {
            elements = selector;
        }
        else {
        }

        return new Ext.CompositeElementLite(elements);
    };

    
    Ext.select = function() {
        return Ext.dom.Element.select.apply(Ext.dom.Element, arguments);
    };
});



Ext.define('Ext.dom.CompositeElement', {
    alternateClassName: 'Ext.CompositeElement',

    extend:  Ext.dom.CompositeElementLite ,

    
    getElement: function(el) {
        
        return el;
    },

    
    transformElement: function(el) {
        return Ext.get(el);
    }

}, function() {
    

    Ext.dom.Element.select = function(selector, unique, root) {
        var elements;

        if (typeof selector == "string") {
            elements = Ext.dom.Element.selectorFunction(selector, root);
        }
        else if (selector.length !== undefined) {
            elements = selector;
        }
        else {
        }
        return (unique === true) ? new Ext.CompositeElement(elements) : new Ext.CompositeElementLite(elements);
    };
});


Ext.select = Ext.Element.select;


Ext.define('Ext.util.HashMap', {
    mixins: {
        observable:  Ext.util.Observable 
    },

    
    generation: 0,
    
    

    
    constructor: function(config) {
        config = config || {};

        var me = this,
            keyFn = config.keyFn;

        me.initialConfig = config;
        me.addEvents(
            
            'add',
            
            'clear',
            
            'remove',
            
            'replace'
        );

        me.mixins.observable.constructor.call(me, config);
        me.clear(true);

        if (keyFn) {
            me.getKey = keyFn;
        }
    },

    
    getCount: function() {
        return this.length;
    },

    
    getData: function(key, value) {
        
        if (value === undefined) {
            value = key;
            key = this.getKey(value);
        }

        return [key, value];
    },

    
    getKey: function(o) {
        return o.id;
    },

    
    add: function(key, value) {
        var me = this;

        
        
        if (arguments.length === 1) {
            value = key;
            key = me.getKey(value);
        }

        if (me.containsKey(key)) {
            return me.replace(key, value);
        }

        me.map[key] = value;
        ++me.length;
        me.generation++;
        if (me.hasListeners.add) {
            me.fireEvent('add', me, key, value);
        }
        return value;
    },

    
    replace: function(key, value) {
        var me = this,
            map = me.map,
            old;

        
        
        if (arguments.length === 1) {
            value = key;
            key = me.getKey(value);
        }

        if (!me.containsKey(key)) {
            me.add(key, value);
        }
        old = map[key];
        map[key] = value;
        me.generation++;
        if (me.hasListeners.replace) {
            me.fireEvent('replace', me, key, value, old);
        }
        return value;
    },

    
    remove: function(o) {
        var key = this.findKey(o);
        if (key !== undefined) {
            return this.removeAtKey(key);
        }
        return false;
    },

    
    removeAtKey: function(key) {
        var me = this,
            value;

        if (me.containsKey(key)) {
            value = me.map[key];
            delete me.map[key];
            --me.length;
            me.generation++;
            if (me.hasListeners.remove) {
                me.fireEvent('remove', me, key, value);
            }
            return true;
        }
        return false;
    },

    
    get: function(key) {
        var map = this.map;
        return map.hasOwnProperty(key) ? map[key] : undefined;
    },

    
    clear: function( initial) {
        var me = this;

        
        if (initial || me.generation) {
            me.map = {};
            me.length = 0;
            me.generation = initial ? 0 : me.generation + 1;
        }
        if (initial !== true && me.hasListeners.clear) {
            me.fireEvent('clear', me);
        }
        return me;
    },

    
    containsKey: function(key) {
        var map = this.map;
        return map.hasOwnProperty(key) && map[key] !== undefined;
    },

    
    contains: function(value) {
        return this.containsKey(this.findKey(value));
    },

    
    getKeys: function() {
        return this.getArray(true);
    },

    
    getValues: function() {
        return this.getArray(false);
    },

    
    getArray: function(isKey) {
        var arr = [],
            key,
            map = this.map;
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                arr.push(isKey ? key: map[key]);
            }
        }
        return arr;
    },

    
    each: function(fn, scope) {
        
        var items = Ext.apply({}, this.map),
            key,
            length = this.length;

        scope = scope || this;
        for (key in items) {
            if (items.hasOwnProperty(key)) {
                if (fn.call(scope, key, items[key], length) === false) {
                    break;
                }
            }
        }
        return this;
    },

    
    clone: function() {
        var hash = new this.self(this.initialConfig),
            map = this.map,
            key;

        hash.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                hash.add(key, map[key]);
            }
        }
        hash.resumeEvents();
        return hash;
    },

    
    findKey: function(value) {
        var key,
            map = this.map;

        for (key in map) {
            if (map.hasOwnProperty(key) && map[key] === value) {
                return key;
            }
        }
        return undefined;
    }
});


Ext.define('Ext.AbstractManager', {

    

                                   

    

    typeName: 'type',

    constructor: function(config) {
        Ext.apply(this, config || {});

        
        this.all = new Ext.util.HashMap();

        this.types = {};
    },

    
    get : function(id) {
        return this.all.get(id);
    },

    
    register: function(item) {
        this.all.add(item);
    },

    
    unregister: function(item) {
        this.all.remove(item);
    },

    
    registerType : function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
    },

    
    isRegistered : function(type){
        return this.types[type] !== undefined;
    },

    
    create: function(config, defaultType) {
        var type        = config[this.typeName] || config.type || defaultType,
            Constructor = this.types[type];


        return new Constructor(config);
    },

    
    onAvailable : function(id, fn, scope){
        var all = this.all,
            item,
            callback;
        
        if (all.containsKey(id)) {
            item = all.get(id);
            fn.call(scope || item, item);
        } else {
            callback = function(map, key, item){
                if (key == id) {
                    fn.call(scope || item, item);
                    all.un('add', callback);
                }
            }; 
            all.on('add', callback);
        }
    },
    
    
    each: function(fn, scope){
        this.all.each(fn, scope || this);    
    },
    
    
    getCount: function(){
        return this.all.getCount();
    }
});


Ext.define('Ext.ComponentManager', {
    extend:  Ext.AbstractManager ,
    alternateClassName: 'Ext.ComponentMgr',
    
    singleton: true,
    
    typeName: 'xtype',
    
    
    create: function(component, defaultType){
        if (typeof component == 'string') {
            return Ext.widget(component);
        }
        if (component.isComponent) {
            return component;
        }
        return Ext.widget(component.xtype || defaultType, component);
    },

    registerType: function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
        cls.prototype[this.typeName] = type;
    }
},
function () {
    
    Ext.getCmp = function(id) {
        return Ext.ComponentManager.get(id);
    };
});


Ext.define('Ext.ComponentQuery', {
    singleton: true 
               
                               
                       
     
}, function() {

    var cq = this,
        domQueryOperators = Ext.dom.Query.operators,
        nthRe = /(\d*)n\+?(\d*)/,
        nthRe2 = /\D/,

        
        
        filterFnPattern = [
            'var r = [],',
                'i = 0,',
                'it = items,',
                'l = it.length,',
                'c;',
            'for (; i < l; i++) {',
                'c = it[i];',
                'if (c.{0}) {',
                   'r.push(c);',
                '}',
            '}',
            'return r;'
        ].join(''),

        filterItems = function(items, operation) {
            
            
            
            return operation.method.apply(this, [ items ].concat(operation.args));
        },

        getItems = function(items, mode) {
            var result = [],
                i = 0,
                length = items.length,
                candidate,
                deep = mode !== '>';
                
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getRefItems) {
                    result = result.concat(candidate.getRefItems(deep));
                }
            }
            return result;
        },

        getAncestors = function(items) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                while (!!(candidate = candidate.getRefOwner())) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByXType = function(items, xtype, shallow) {
            if (xtype === '*') {
                return items.slice();
            }
            else {
                var result = [],
                    i = 0,
                    length = items.length,
                    candidate;
                for (; i < length; i++) {
                    candidate = items[i];
                    if (candidate.isXType(xtype, shallow)) {
                        result.push(candidate);
                    }
                }
                return result;
            }
        },

        
        filterByClassName = function(items, className) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.hasCls(className)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByAttribute = function(items, property, operator, compareTo) {
            var result = [],
                i = 0,
                length = items.length,
                mustBeOwnProperty,
                presenceOnly,
                candidate, propValue,
                j, propLen;

            
            if (property.charAt(0) === '@') {
                mustBeOwnProperty = true;
                property = property.substr(1);
            }
            if (property.charAt(0) === '?') {
                mustBeOwnProperty = true;
                presenceOnly = true;
                property = property.substr(1);
            }

            for (; i < length; i++) {
                candidate = items[i];

                
                if (!mustBeOwnProperty || candidate.hasOwnProperty(property)) {

                    
                    propValue = candidate[property];

                    if (presenceOnly) {
                        result.push(candidate);
                    }
                    
                    else if (operator === '~=') {
                        if (propValue) {
                            
                            if (!Ext.isArray(propValue)) {
                                propValue = propValue.split(' ');
                            }

                            for (j = 0, propLen = propValue.length; j < propLen; j++) {
                                if (domQueryOperators[operator](Ext.coerce(propValue[j], compareTo), compareTo)) {
                                    result.push(candidate);
                                    break;
                                }
                            }
                        }
                    } else if (!compareTo ? !!candidate[property] : domQueryOperators[operator](Ext.coerce(propValue, compareTo), compareTo)) {
                        result.push(candidate);
                    }
                }
            }
            return result;
        },

        
        filterById = function(items, id) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getItemId() === id) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByPseudo = function(items, name, value) {
            return cq.pseudos[name](items, value);
        },

        
        
        modeRe = /^(\s?([>\^])\s?|\s|$)/,

        
        tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/,

        matchers = [{
            
            re: /^\.([\w\-]+)(?:\((true|false)\))?/,
            method: filterByXType
        }, {
            
            
            
            re: /^(?:\[((?:@|\?)?[\w\-\$]*[^\^\$\*~%!])\s?(?:(=|.=)\s?['"]?(.*?)["']?)?\])/,
            method: filterByAttribute
        }, {
            
            re: /^#([\w\-]+)/,
            method: filterById
        }, {
            
            re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
            method: filterByPseudo
        }, {
            
            re: /^(?:\{([^\}]+)\})/,
            method: filterFnPattern
        }];

    
    cq.Query = Ext.extend(Object, {
        constructor: function(cfg) {
            cfg = cfg || {};
            Ext.apply(this, cfg);
        },

        
        
        
        
        
        
        
        
        execute : function(root) {
            var operations = this.operations,
                i = 0,
                length = operations.length,
                operation,
                workingItems;

            
            if (!root) {
                workingItems = Ext.ComponentManager.all.getArray();
            }
            
            else if (Ext.isIterable(root)) {
                workingItems = root;
            }
            
            else if (root.isMixedCollection) {
                workingItems = root.items;
            }

            
            
            for (; i < length; i++) {
                operation = operations[i];

                
                
                
                
                
                
                if (operation.mode === '^') {
                    workingItems = getAncestors(workingItems || [root]);
                }
                else if (operation.mode) {
                    workingItems = getItems(workingItems || [root], operation.mode);
                }
                else {
                    workingItems = filterItems(workingItems || getItems([root]), operation);
                }

                
                
                if (i === length -1) {
                    return workingItems;
                }
            }
            return [];
        },

        is: function(component) {
            var operations = this.operations,
                components = Ext.isArray(component) ? component : [component],
                originalLength = components.length,
                lastOperation = operations[operations.length-1],
                ln, i;

            components = filterItems(components, lastOperation);
            if (components.length === originalLength) {
                if (operations.length > 1) {
                    for (i = 0, ln = components.length; i < ln; i++) {
                        if (Ext.Array.indexOf(this.execute(), components[i]) === -1) {
                            return false;
                        }
                    }
                }
                return true;
            }
            return false;
        }
    });

    Ext.apply(this, {

        
        cache: {},

        
        pseudos: {
            not: function(components, selector){
                var CQ = Ext.ComponentQuery,
                    i = 0,
                    length = components.length,
                    results = [],
                    index = -1,
                    component;
                
                for(; i < length; ++i) {
                    component = components[i];
                    if (!CQ.is(component, selector)) {
                        results[++index] = component;
                    }
                }
                return results;
            },
            first: function(components) {
                var ret = [];
                    
                if (components.length > 0) {
                    ret.push(components[0]);
                }
                return ret;       
            },
            last: function(components) {
                var len = components.length,
                    ret = [];
                    
                if (len > 0) {
                    ret.push(components[len - 1]);
                }
                return ret;
            },
            focusable: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;

                for (; i < len; i++) {
                    c = cmps[i];
                    
                    
                    if (c.isFocusable()) {
                        results.push(c);
                    }
                }

                return results;
            },
            "nth-child" : function(c, a) {
                var result = [],
                    m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
                    f = (m[1] || 1) - 0, l = m[2] - 0,
                    i, n, nodeIndex;
                for (i = 0; n = c[i]; i++) {
                    nodeIndex = i + 1;
                    if (f == 1) {
                        if (l == 0 || nodeIndex == l) {
                            result.push(n);
                        }
                    } else if ((nodeIndex + l) % f == 0){
                        result.push(n);
                    }
                }

                return result;
            }
        },

        
        query: function(selector, root) {
            var selectors = selector.split(','),
                length = selectors.length,
                i = 0,
                results = [],
                noDupResults = [], 
                dupMatcher = {}, 
                query, resultsLn, cmp;

            for (; i < length; i++) {
                selector = Ext.String.trim(selectors[i]);
                query = this.cache[selector] || (this.cache[selector] = this.parse(selector));
                results = results.concat(query.execute(root));
            }

            
            
            if (length > 1) {
                resultsLn = results.length;
                for (i = 0; i < resultsLn; i++) {
                    cmp = results[i];
                    if (!dupMatcher[cmp.id]) {
                        noDupResults.push(cmp);
                        dupMatcher[cmp.id] = true;
                    }
                }
                results = noDupResults;
            }
            return results;
        },

        
        is: function(component, selector) {
            if (!selector) {
                return true;
            }
            var selectors = selector.split(','),
                length = selectors.length,
                i = 0,
                query;

            for (; i < length; i++) {
                selector = Ext.String.trim(selectors[i]);
                query = this.cache[selector] || (this.cache[selector] = this.parse(selector));
                if (query.is(component)) {
                    return true;
                }
            }
            return false;
        },

        parse: function(selector) {
            var operations = [],
                length = matchers.length,
                lastSelector,
                tokenMatch,
                matchedChar,
                modeMatch,
                selectorMatch,
                i, matcher, method;

            
            
            
            while (selector && lastSelector !== selector) {
                lastSelector = selector;

                
                tokenMatch = selector.match(tokenRe);

                if (tokenMatch) {
                    matchedChar = tokenMatch[1];

                    
                    if (matchedChar === '#') {
                        operations.push({
                            method: filterById,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    
                    
                    else if (matchedChar === '.') {
                        operations.push({
                            method: filterByClassName,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    
                    
                    else {
                        operations.push({
                            method: filterByXType,
                            args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
                        });
                    }

                    
                    selector = selector.replace(tokenMatch[0], '');
                }

                
                
                
                while (!(modeMatch = selector.match(modeRe))) {
                    
                    
                    for (i = 0; selector && i < length; i++) {
                        matcher = matchers[i];
                        selectorMatch = selector.match(matcher.re);
                        method = matcher.method;

                        
                        
                        
                        if (selectorMatch) {
                            operations.push({
                                method: Ext.isString(matcher.method)
                                    
                                    
                                    
                                    ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1))))
                                    : matcher.method,
                                args: selectorMatch.slice(1)
                            });
                            selector = selector.replace(selectorMatch[0], '');
                            break; 
                        }
                        
                        if (i === (length - 1)) {
                            Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
                        }
                    }
                }

                
                
                
                
                if (modeMatch[1]) { 
                    operations.push({
                        mode: modeMatch[2]||modeMatch[1]
                    });
                    selector = selector.replace(modeMatch[0], '');
                }
            }

            
            
            return new cq.Query({
                operations: operations
            });
        }
    });
});




Ext.define('Ext.util.ProtoElement', (function () {
    var splitWords = Ext.String.splitWords,
        toMap = Ext.Array.toMap;

    return {
        
        isProtoEl: true,
        
        
        clsProp: 'cls',

        
        styleProp: 'style',
        
        
        removedProp: 'removed',

        
        styleIsText: false,

        constructor: function (config) {
            var me = this;

            Ext.apply(me, config);

            me.classList = splitWords(me.cls);
            me.classMap = toMap(me.classList);
            delete me.cls;

            if (Ext.isFunction(me.style)) {
                me.styleFn = me.style;
                delete me.style;
            } else if (typeof me.style == 'string') {
                me.style = Ext.Element.parseStyles(me.style);
            } else if (me.style) {
                me.style = Ext.apply({}, me.style); 
            }
        },
        
        
        flush: function(){
            this.flushClassList = [];
            this.removedClasses = {};
            
            delete this.style;
            delete this.unselectableAttr;
        },

        
        addCls: function (cls) {
            var me = this,
                add = (typeof cls === 'string') ? splitWords(cls) : cls,
                length = add.length,
                list = me.classList,
                map = me.classMap,
                flushList = me.flushClassList,
                i = 0,
                c;

            for (; i < length; ++i) {
                c = add[i];
                if (!map[c]) {
                    map[c] = true;
                    list.push(c);
                    if (flushList) {
                        flushList.push(c);
                        delete me.removedClasses[c];
                    }
                }
            }

            return me;
        },

        
        hasCls: function (cls) {
            return cls in this.classMap;
        },

        
        removeCls: function (cls) {
            var me = this,
                list = me.classList,
                newList = (me.classList = []),
                remove = toMap(splitWords(cls)),
                length = list.length,
                map = me.classMap,
                removedClasses = me.removedClasses,
                i, c;

            for (i = 0; i < length; ++i) {
                c = list[i];
                if (remove[c]) {
                    if (removedClasses) {
                        if (map[c]) {
                            removedClasses[c] = true;
                            Ext.Array.remove(me.flushClassList, c);
                        }
                    }
                    delete map[c];
                } else {
                    newList.push(c);
                }
            }

            return me;
        },

        
        setStyle: function (prop, value) {
            var me = this,
                style = me.style || (me.style = {});

            if (typeof prop == 'string') {
                if (arguments.length === 1) {
                    me.setStyle(Ext.Element.parseStyles(prop));
                } else {
                    style[prop] = value;
                }
            } else {
                Ext.apply(style, prop);
            }

            return me;
        },

        unselectable: function() {
            
            this.addCls(Ext.dom.Element.unselectableCls);

            if (Ext.isOpera) {
                this.unselectableAttr = true;
            }
        },

        
        writeTo: function (to) {
            var me = this,
                classList = me.flushClassList || me.classList,
                removedClasses = me.removedClasses,
                style;

            if (me.styleFn) {
                style = Ext.apply({}, me.styleFn());
                Ext.apply(style, me.style);
            } else {
                style = me.style;
            }

            to[me.clsProp] = classList.join(' ');

            if (style) {
                to[me.styleProp] = me.styleIsText ? Ext.DomHelper.generateStyles(style) : style;
            }
            
            if (removedClasses) {
                removedClasses = Ext.Object.getKeys(removedClasses);
                if (removedClasses.length) {
                    to[me.removedProp] = removedClasses.join(' ');
                }
            }

            if (me.unselectableAttr) {
                to.unselectable = 'on';
            }

            return to;
        }
    };
}()));


Ext.define('Ext.PluginManager', {
    extend:  Ext.AbstractManager ,
    alternateClassName: 'Ext.PluginMgr',
    singleton: true,
    typeName: 'ptype',

    
    create : function(config, defaultType, host) {
        var result;

        if (config.init) {
            result = config;
        } else {
            
            if (host) {
                config = Ext.apply({}, config); 
                config.cmp = host;
            }
            
            else {
                host = config.cmp;
            }

            if (config.xclass) {
                result = Ext.create(config);
            } else {
                
                result = Ext.ClassManager.getByAlias(('plugin.' + (config.ptype || defaultType)));

                if (typeof result === 'function') {
                    result = new result(config);
                }
            }
        }

        
        if (result && host && result.setCmp && !result.setCmpCalled) {
            result.setCmp(host);
            result.setCmpCalled = true;
        }
        return result;
    },

    
    findByType: function(type, defaultsOnly) {
        var matches = [],
            types   = this.types,
            name,
            item;

        for (name in types) {
            if (!types.hasOwnProperty(name)) {
                continue;
            }
            item = types[name];

            if (item.type == type && (!defaultsOnly || (defaultsOnly === true && item.isDefault))) {
                matches.push(item);
            }
        }

        return matches;
    }
}, function() {
    
    Ext.preg = function() {
        return Ext.PluginManager.registerType.apply(Ext.PluginManager, arguments);
    };
});


Ext.define('Ext.util.Filter', {

    
    

    

    
    id: null,

    
    anyMatch: false,

    
    exactMatch: false,

    
    caseSensitive: false,

    
    disabled: false,

    
    operator: null,

    

    statics: {
        
        createFilterFn: function(filters) {
            return filters && filters.length ? function(candidate) {
                var isMatch = true,
                    length = filters.length,
                    i, filter;

                for (i = 0; isMatch && i < length; i++) {
                    filter = filters[i];

                    
                    if (!filter.disabled) {
                        isMatch = isMatch && filter.filterFn.call(filter.scope || filter, candidate);
                    }
                }
                return isMatch;
            } : function() {
                return true;
            };
        }
    },

    operatorFns: {
        "<": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) < this.value;
        },
        "<=": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) <= this.value;
        },
        "=": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) == this.value;
        },
        ">=": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) >= this.value;
        },
        ">": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) > this.value;
        },
        "!=": function(candidate) {
            return Ext.coerce(this.getRoot(candidate)[this.property], this.value) != this.value;
        }
    },

    
    constructor: function(config) {
        var me = this;
        me.initialConfig = config;
        Ext.apply(me, config);

        
        
        me.filter = me.filter || me.filterFn;

        if (me.filter === undefined) {
            me.setValue(config.value);
        }
    },

    
    setValue: function(value) {
        var me = this;
        me.value = value;
        if (me.property === undefined || me.value === undefined) {
            
            

            
        } else {
            me.filter = me.createFilterFn();
        }

        me.filterFn = me.filter;
    },

    
    setFilterFn: function(filterFn) {
        this.filterFn = this.filter = filterFn;
    },

    
    createFilterFn: function() {
        var me       = this,
            matcher  = me.createValueMatcher(),
            property = me.property;

        if (me.operator) {
            return me.operatorFns[me.operator];
        } else {
            return function(item) {
                var value = me.getRoot(item)[property];
                return matcher === null ? value === null : matcher.test(value);
            };
        }
    },

    
    getRoot: function(item) {
        var root = this.root;
        return root === undefined ? item : item[root];
    },

    
    createValueMatcher : function() {
        var me            = this,
            value         = me.value,
            anyMatch      = me.anyMatch,
            exactMatch    = me.exactMatch,
            caseSensitive = me.caseSensitive,
            escapeRe      = Ext.String.escapeRegex;

        if (value === null) {
            return value;
        }

        if (!value.exec) { 
            value = String(value);

            if (anyMatch === true) {
                value = escapeRe(value);
            } else {
                value = '^' + escapeRe(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
         }

         return value;
    },

    serialize: function() {
        var me = this,
            result = Ext.apply({}, me.initialConfig);

        result.value = me.value;
        return result;
    }
}, function() {
    
    this.prototype.operatorFns['=='] = this.prototype.operatorFns['='];
});


Ext.define('Ext.util.AbstractMixedCollection', {
                                  

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    isMixedCollection: true,

    
    generation: 0,
    
    
    indexGeneration: 0,
    
    constructor: function(allowFunctions, keyFn) {
        var me = this;

        
        if (arguments.length === 1 && Ext.isObject(allowFunctions)) {
            me.initialConfig = allowFunctions;
            Ext.apply(me, allowFunctions);
        }
        
        else {
            me.allowFunctions = allowFunctions === true;
            if (keyFn) {
                me.getKey = keyFn;
            }
            me.initialConfig = {
                allowFunctions: me.allowFunctions,
                getKey: me.getKey
            };
        }

        me.items = [];
        me.map = {};
        me.keys = [];
        me.indexMap = {};
        me.length = 0;

        

        

        
       
        

        me.mixins.observable.constructor.call(me);
    },

    
    allowFunctions : false,

    
    add : function(key, obj) {
        var len = this.length,
            out;
        
        if (arguments.length === 1) {
            out = this.insert(len, key);
        } else {
            out = this.insert(len, key, obj);
        }
        return out;
    },

    
    getKey : function(o) {
         return o.id;
    },

    
    replace : function(key, o) {
        var me = this,
            old,
            index;

        if (arguments.length == 1) {
            o = arguments[0];
            key = me.getKey(o);
        }
        old = me.map[key];
        if (typeof key == 'undefined' || key === null || typeof old == 'undefined') {
             return me.add(key, o);
        }
        me.generation++;
        index = me.indexOfKey(key);
        me.items[index] = o;
        me.map[key] = o;
        if (me.hasListeners.replace) {
            me.fireEvent('replace', key, old, o);
        }
        return o;
    },
    
    
    updateKey: function(oldKey, newKey) {
        var me = this,
            map = me.map,
            indexMap = me.indexMap,
            index = me.indexOfKey(oldKey),
            item;
            
        if (index > -1) {
            item = map[oldKey];
            delete map[oldKey];
            delete indexMap[oldKey];
            map[newKey] = item;
            indexMap[newKey] = index;
            me.keys[index] = newKey;
            me.generation++;
            
        }
    },

    
    addAll : function(objs) {
        var me = this,
            key;

        if (arguments.length > 1 || Ext.isArray(objs)) {
            me.insert(me.length, arguments.length > 1 ? arguments : objs);
        } else {
            for (key in objs) {
                if (objs.hasOwnProperty(key)) {
                    if (me.allowFunctions || typeof objs[key] != 'function') {
                        me.add(key, objs[key]);
                    }
                }
            }
        }
    },

    
    each : function(fn, scope){
        var items = Ext.Array.push([], this.items), 
            i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (fn.call(scope || item, item, i, len) === false) {
                break;
            }
        }
    },

    
    eachKey : function(fn, scope){
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = keys.length;

        for (; i < len; i++) {
            fn.call(scope || window, keys[i], items[i], i, len);
        }
    },

    
    findBy : function(fn, scope) {
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || window, items[i], keys[i])) {
                return items[i];
            }
        }
        return null;
    },

    
    find : function() {
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.util.MixedCollection: find has been deprecated. Use findBy instead.');
        }
        return this.findBy.apply(this, arguments);
    },

    
    insert : function(index, key, obj) {
        var out;
        if (Ext.isIterable(key)) {
            out = this.doInsert(index, key, obj);
        } else {
            if (arguments.length > 2) {
                out = this.doInsert(index, [key], [obj]);
            } else {
                out = this.doInsert(index, [key]);
            }
            out = out[0];
        }
        return out;
    },
 
    
    doInsert : function(index, keys, objects) {
        var me = this,
            itemKey,
            removeIndex,
            i, len = keys.length,
            deDupedLen = len,
            fireAdd = me.hasListeners.add,
            syncIndices,
            newKeys = {},
            passedDuplicates,
            oldKeys, oldObjects;

        
        
        if (objects != null) {
            me.useLinearSearch = true;
        }
        
        else {
            objects = keys;
            keys = new Array(len);
            for (i = 0; i < len; i++) {
                keys[i] = this.getKey(objects[i]);
            }
        }

        
        me.suspendEvents();
        for (i = 0; i < len; i++) {
            itemKey = keys[i];

            
            removeIndex = me.indexOfKey(itemKey);
            if (removeIndex !== -1) {
                if (removeIndex < index) {
                    index--;
                }
                me.removeAt(removeIndex);
            }

            if (itemKey != null) {
                
                if (newKeys[itemKey] != null) {
                    passedDuplicates = true;
                    deDupedLen--;
                }
                newKeys[itemKey] = i;
            }
        }
        me.resumeEvents();

        
        if (passedDuplicates) {
            oldKeys = keys;
            oldObjects = objects;
            keys = new Array(deDupedLen);
            objects = new Array(deDupedLen);
            i = 0;

            
            
            for (itemKey in newKeys) {
                keys[i] = oldKeys[newKeys[itemKey]];
                objects[i] = oldObjects[newKeys[itemKey]];
                i++;
            }
            len = deDupedLen;
        }

        
        syncIndices = index === me.length && me.indexGeneration === me.generation;

        
        Ext.Array.insert(me.items, index, objects);
        Ext.Array.insert(me.keys,  index, keys);
        me.length += len;
        me.generation++;
        if (syncIndices) {
            me.indexGeneration = me.generation;
        }
        for (i = 0; i < len; i++, index++) {
            itemKey = keys[i];
            if (itemKey != null) {
                me.map[itemKey] = objects[i];

                
                if (syncIndices) {
                    me.indexMap[itemKey] = index;
                }
            }
            if (fireAdd) {
                me.fireEvent('add', index, objects[i], itemKey);
            }
        }
        return objects;
    },

    
    remove : function(o) {
        var me = this,
            removeKey,
            index;

        
        
        
        
        
        if (!me.useLinearSearch && (removeKey = me.getKey(o))) {
            index = me.indexOfKey(removeKey);
        }

        
        else {
            index = Ext.Array.indexOf(me.items, o);
        }

        return (index === -1) ? false : me.removeAt(index);
    },

    
    removeAll : function(items) {
        var me = this, 
            i;

        if (items || me.hasListeners.remove) {
            
            if (items) {
                for (i = items.length - 1; i >= 0; --i) {
                    me.remove(items[i]);
                }
            } else {
                while (me.length) {
                    me.removeAt(0);
                }
            }
        } else {
            me.length = me.items.length = me.keys.length = 0;
            me.map = {};
            me.indexMap = {};
            me.generation++;
            me.indexGeneration = me.generation;
        }
    },
    
    
    removeAt : function(index) {
        var me = this,
            o,
            key;

        if (index < me.length && index >= 0) {
            me.length--;
            o = me.items[index];
            Ext.Array.erase(me.items, index, 1);
            key = me.keys[index];
            if (typeof key != 'undefined') {
                delete me.map[key];
            }
            Ext.Array.erase(me.keys, index, 1);
            if (me.hasListeners.remove) {
                me.fireEvent('remove', o, key);
            }
            me.generation++;
            return o;
        }
        return false;
    },

    
    removeRange : function(index, removeCount) {
        var me = this,
            o,
            key,
            i,
            limit,
            syncIndices,
            trimming;

        if (index < me.length && index >= 0) {
            if (!removeCount) {
                removeCount = 1;
            }
            limit = Math.min(index + removeCount, me.length);
            removeCount = limit - index;

            
            trimming = limit === me.length;
            syncIndices = trimming && me.indexGeneration === me.generation;

            
            for (i = index; i < limit; i++) {
                key = me.keys[i];
                if (key != null) {
                    delete me.map[key];
                    if (syncIndices) {
                        delete me.indexMap[key];
                    }
                }
            }
            
            o = me.items[i - 1];
            
            me.length -= removeCount;
            me.generation++;
            if (syncIndices) {
                me.indexGeneration = me.generation;
            }

            
            
            
            
            if (trimming) {
                me.items.length = me.keys.length = me.length;
            } else {
                me.items.splice(index, removeCount);
                me.keys.splice(index, removeCount);
            }

            
            return o;
        }
        return false;
    },

    
    removeAtKey : function(key) {
        var me = this,
            keys = me.keys,
            i;

        
        if (key == null) {
            for (i = keys.length - 1; i >=0; i--) {
                if (keys[i] == null) {
                    me.removeAt(i);
                }
            }
        }
        
        else {
            return me.removeAt(me.indexOfKey(key));
        }
    },

    
    getCount : function() {
        return this.length;
    },

    
    indexOf : function(o) {
        var me = this,
            key;

        if (o != null) {
            
            
            
            
            
            if (!me.useLinearSearch && (key = me.getKey(o))) {
                return this.indexOfKey(key);
            }

            
            return Ext.Array.indexOf(me.items, o);
        }

        
        return -1;
    },

    
    indexOfKey : function(key) {
        if (!this.map.hasOwnProperty(key)) {
            return -1;
        }
        if (this.indexGeneration !== this.generation) {
            this.rebuildIndexMap();
        }
        return this.indexMap[key];
    },
    
    rebuildIndexMap: function() {
        var me = this,
            indexMap = me.indexMap = {},
            keys = me.keys,
            len = keys.length,
            i;

        for (i = 0; i < len; i++) {
            indexMap[keys[i]] = i;
        }
        me.indexGeneration = me.generation;
    },

    
    get : function(key) {
        var me = this,
            mk = me.map[key],
            item = mk !== undefined ? mk : (typeof key == 'number') ? me.items[key] : undefined;
        return typeof item != 'function' || me.allowFunctions ? item : null; 
    },

    
    getAt : function(index) {
        return this.items[index];
    },

    
    getByKey : function(key) {
        return this.map[key];
    },

    
    contains : function(o) {
        var me = this,
            key;

        if (o != null) {
            
            
            
            
            
            if (!me.useLinearSearch && (key = me.getKey(o))) {
                return this.map[key] != null;
            }

            
            return Ext.Array.indexOf(this.items, o) !== -1;
        }
        
        return false;
    },

    
    containsKey : function(key) {
        return this.map.hasOwnProperty(key);
    },

    
    clear : function() {
        var me = this;

        
        if (me.generation) {
            me.length = 0;
            me.items = [];
            me.keys = [];
            me.map = {};
            me.indexMap = {};

            me.generation++;
            me.indexGeneration = me.generation;
        }
        if (me.hasListeners.clear) {
            me.fireEvent('clear');
        }
    },

    
    first : function() {
        return this.items[0];
    },

    
    last : function() {
        return this.items[this.length - 1];
    },

    
    sum: function(property, root, start, end) {
        var values = this.extractValues(property, root),
            length = values.length,
            sum    = 0,
            i;

        start = start || 0;
        end   = (end || end === 0) ? end : length - 1;

        for (i = start; i <= end; i++) {
            sum += values[i];
        }

        return sum;
    },

    
    collect: function(property, root, allowNull) {
        var values = this.extractValues(property, root),
            length = values.length,
            hits   = {},
            unique = [],
            value, strValue, i;

        for (i = 0; i < length; i++) {
            value = values[i];
            strValue = String(value);

            if ((allowNull || !Ext.isEmpty(value)) && !hits[strValue]) {
                hits[strValue] = true;
                unique.push(value);
            }
        }

        return unique;
    },

    
    extractValues: function(property, root) {
        var values = this.items;

        if (root) {
            values = Ext.Array.pluck(values, root);
        }

        return Ext.Array.pluck(values, property);
    },

    
    hasRange: function(start, end) {
        return (end < this.length);
    },

    
    getRange : function(start, end){
        var me = this,
            items = me.items,
            range = [],
            len = items.length,
            tmp, reverse;

        if (len < 1) {
            return range;
        }
        
        if (start > end) {
            reverse = true;
            tmp = start;
            start = end;
            end = tmp;
        }

        if (start < 0) {
            start = 0;
        }
        
        if (end == null || end >= len) {
            end = len - 1;    
        }
        
        range = items.slice(start, end + 1);
        if (reverse && range.length) {
            range.reverse();
        }
        return range;
    },

    
    filter : function(property, value, anyMatch, caseSensitive) {
        var filters = [];

        
        if (Ext.isString(property)) {
            filters.push(new Ext.util.Filter({
                property     : property,
                value        : value,
                anyMatch     : anyMatch,
                caseSensitive: caseSensitive
            }));
        } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) {
            filters = filters.concat(property);
        }

        
        
        
        return this.filterBy(Ext.util.Filter.createFilterFn(filters));
    },

    
    filterBy : function(fn, scope) {
        var me = this,
            newMC  = new me.self(me.initialConfig),
            keys   = me.keys,
            items  = me.items,
            length = items.length,
            i;

        newMC.getKey = me.getKey;

        for (i = 0; i < length; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                newMC.add(keys[i], items[i]);
            }
        }

        return newMC;
    },

    
    findIndex : function(property, value, start, anyMatch, caseSensitive){
        if(Ext.isEmpty(value, false)){
            return -1;
        }
        value = this.createValueMatcher(value, anyMatch, caseSensitive);
        return this.findIndexBy(function(o){
            return o && value.test(o[property]);
        }, null, start);
    },

    
    findIndexBy : function(fn, scope, start){
        var me = this,
            keys = me.keys,
            items = me.items,
            i = start || 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                return i;
            }
        }
        return -1;
    },

    
    createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
        if (!value.exec) { 
            var er = Ext.String.escapeRegex;
            value = String(value);

            if (anyMatch === true) {
                value = er(value);
            } else {
                value = '^' + er(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
        }
        return value;
    },

    
    clone : function() {
        var me = this,
            copy = new this.self(me.initialConfig);

        copy.add(me.keys, me.items);
        return copy;
    }
});


Ext.define('Ext.util.Sorter', {

    

    

    

    

    
    direction: "ASC",

    constructor: function(config) {
        var me = this;

        Ext.apply(me, config);


        me.updateSortFunction();
    },

    
    createSortFunction: function(sorterFn) {
        var me        = this,
            direction = me.direction || "ASC",
            modifier  = direction.toUpperCase() == "DESC" ? -1 : 1;

        
        
        return function(o1, o2) {
            return modifier * sorterFn.call(me, o1, o2);
        };
    },

    
    defaultSorterFn: function(o1, o2) {
        var me = this,
            transform = me.transform,
            v1 = me.getRoot(o1)[me.property],
            v2 = me.getRoot(o2)[me.property];

        if (transform) {
            v1 = transform(v1);
            v2 = transform(v2);
        }

        return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
    },

    
    getRoot: function(item) {
        return this.root === undefined ? item : item[this.root];
    },

    
    setDirection: function(direction) {
        var me = this;
        me.direction = direction ? direction.toUpperCase() : direction;
        me.updateSortFunction();
    },

    
    toggle: function() {
        var me = this;
        me.direction = Ext.String.toggle(me.direction, "ASC", "DESC");
        me.updateSortFunction();
    },

    
    updateSortFunction: function(fn) {
        var me = this;
        fn = fn || me.sorterFn || me.defaultSorterFn;
        me.sort = me.createSortFunction(fn);
    },

    serialize: function() {
        return {
            root: this.root,
            property: this.property,
            direction: this.direction
        };
    }
});


Ext.define("Ext.util.Sortable", {
    
    isSortable: true,

    
    defaultSortDirection: "ASC",

               
                         
      

    statics: {
        
        createComparator: function(sorters) {
            return sorters && sorters.length ? function(r1, r2) {
                var result = sorters[0].sort(r1, r2),
                    length = sorters.length,
                    i = 1;

                
                for (; i < length; i++) {
                    result = result || sorters[i].sort.call(this, r1, r2);
                }
                return result;
            }: function() {
                return 0;
            };
        }
    },

    

    

    
    initSortable: function() {
        var me = this,
            sorters = me.sorters;

        
        me.sorters = new Ext.util.AbstractMixedCollection(false, function(item) {
            return item.id || item.property;
        });

        if (sorters) {
            me.sorters.addAll(me.decodeSorters(sorters));
        }
    },

    
    sort: function(sorters, direction, where, doSort) {
        var me = this,
            sorter,
            newSorters;

        if (Ext.isArray(sorters)) {
            doSort = where;
            where = direction;
            newSorters = sorters;
        }
        else if (Ext.isObject(sorters)) {
            doSort = where;
            where = direction;
            newSorters = [sorters];
        }
        else if (Ext.isString(sorters)) {
            sorter = me.sorters.get(sorters);

            if (!sorter) {
                sorter = {
                    property : sorters,
                    direction: direction
                };
                newSorters = [sorter];
            }
            else if (direction === undefined) {
                sorter.toggle();
            }
            else {
                sorter.setDirection(direction);
            }
        }

        if (newSorters && newSorters.length) {
            newSorters = me.decodeSorters(newSorters);
            if (Ext.isString(where)) {
                if (where === 'prepend') {
                    me.sorters.insert(0, newSorters);
                }
                else {
                    me.sorters.addAll(newSorters);
                }
            }
            else {
                me.sorters.clear();
                me.sorters.addAll(newSorters);
            }
        }

        if (doSort !== false) {
            me.fireEvent('beforesort', me, newSorters);
            me.onBeforeSort(newSorters);
            
            sorters = me.sorters.items;
            if (sorters.length) {
                
                me.doSort(me.generateComparator());
            }
        }

        return sorters;
    },

    
    generateComparator: function() {
        var sorters = this.sorters.getRange();
        return sorters.length ? this.createComparator(sorters) : this.emptyComparator;
    },

    emptyComparator: function(){
        return 0;
    },

    onBeforeSort: Ext.emptyFn,

    
    decodeSorters: function(sorters) {
        if (!Ext.isArray(sorters)) {
            if (sorters === undefined) {
                sorters = [];
            } else {
                sorters = [sorters];
            }
        }

        var length = sorters.length,
            Sorter = Ext.util.Sorter,
            fields = this.model ? this.model.prototype.fields : null,
            field,
            config, i;

        for (i = 0; i < length; i++) {
            config = sorters[i];

            if (!(config instanceof Sorter)) {
                if (Ext.isString(config)) {
                    config = {
                        property: config
                    };
                }

                Ext.applyIf(config, {
                    root     : this.sortRoot,
                    direction: "ASC"
                });

                
                if (config.fn) {
                    config.sorterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }

                
                if (fields && !config.transform) {
                    field = fields.get(config.property);
                    config.transform = field && field.sortType !== Ext.identityFn ? field.sortType : undefined;
                }
                sorters[i] = new Ext.util.Sorter(config);
            }
        }

        return sorters;
    },

    getSorters: function() {
        return this.sorters.items;
    },
    
    
    getFirstSorter: function(){
        var sorters = this.sorters.items,
            len = sorters.length,
            i = 0,
            sorter;
            
        for (; i < len; ++i) {
            sorter = sorters[i];
            if (!sorter.isGrouper) {
                return sorter;    
            }
        }
        return null;
    }
}, function() {
    
    this.prototype.createComparator = this.createComparator;
});


Ext.define('Ext.util.MixedCollection', {
    extend:  Ext.util.AbstractMixedCollection ,
    mixins: {
        sortable:  Ext.util.Sortable 
    },

    

    
    constructor: function() {
        var me = this;
        me.callParent(arguments);
        me.addEvents('sort');
        me.mixins.sortable.initSortable.call(me);
    },

    doSort: function(sorterFn) {
        this.sortBy(sorterFn);
    },

    
    _sort : function(property, dir, fn) {
        var me = this,
            i, len,
            dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,

            
            c     = [],
            keys  = me.keys,
            items = me.items,
            o;

        
        fn = fn || function(a, b) {
            return a - b;
        };

        
        for (i = 0, len = items.length; i < len; i++) {
            c[c.length] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }

        
        Ext.Array.sort(c, function(a, b) {
            return fn(a[property], b[property]) * dsc ||
                
                (a.index < b.index ? -1 : 1);
        });

        
        
        for (i = 0, len = c.length; i < len; i++) {
            o = c[i];
            items[i] = o.value;
            keys[i]  = o.key;
            me.indexMap[o.key] = i;
        }
        me.generation++;
        me.indexGeneration = me.generation;
        me.fireEvent('sort', me);
    },

    
    sortBy: function(sorterFn) {
        var me     = this,
            items  = me.items,
            item,
            keys   = me.keys,
            key,
            length = items.length,
            i;

        
        for (i = 0; i < length; i++) {
            items[i].$extCollectionIndex = i;
        }

        Ext.Array.sort(items, function(a, b) {
            return sorterFn(a, b) ||
                
                (a.$extCollectionIndex < b.$extCollectionIndex ? -1 : 1);
        });

        
        for (i = 0; i < length; i++) {
            item = items[i];
            key = me.getKey(item);
            keys[i] = key;
            me.indexMap[key] = i;
            delete items.$extCollectionIndex;
        }
        me.generation++;
        me.indexGeneration = me.generation;
        me.fireEvent('sort', me, items, keys);
    },

    
    findInsertionIndex: function(newItem, sorterFn) {
        var me    = this,
            items = me.items,
            start = 0,
            end   = items.length - 1,
            middle,
            comparison;

        if (!sorterFn) {
            sorterFn = me.generateComparator();
        }
        while (start <= end) {
            middle = (start + end) >> 1;
            comparison = sorterFn(newItem, items[middle]);
            if (comparison >= 0) {
                start = middle + 1;
            } else if (comparison < 0) {
                end = middle - 1;
            }
        }
        return start;
    },

    
    reorder: function(mapping) {
        var me = this,
            items = me.items,
            index = 0,
            length = items.length,
            order = [],
            remaining = [],
            oldIndex;

        me.suspendEvents();

        
        for (oldIndex in mapping) {
            order[mapping[oldIndex]] = items[oldIndex];
        }

        for (index = 0; index < length; index++) {
            if (mapping[index] == undefined) {
                remaining.push(items[index]);
            }
        }

        for (index = 0; index < length; index++) {
            if (order[index] == undefined) {
                order[index] = remaining.shift();
            }
        }

        me.clear();
        me.addAll(order);

        me.resumeEvents();
        me.fireEvent('sort', me);
    },

    
    sortByKey : function(dir, fn){
        this._sort('key', dir, fn || function(a, b){
            var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
            return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
        });
    }
});


Ext.define('Ext.fx.target.Target', {

    isAnimTarget: true,

    
    constructor: function(target) {
        this.target = target;
        this.id = this.getId();
    },
    
    getId: function() {
        return this.target.id;
    }
});


Ext.define('Ext.fx.target.Element', {

    
    
    extend:  Ext.fx.target.Target ,
    
    

    type: 'element',

    getElVal: function(el, attr, val) {
        if (val == undefined) {
            if (attr === 'x') {
                val = el.getX();
            } else if (attr === 'y') {
                val = el.getY();
            } else if (attr === 'scrollTop') {
                val = el.getScroll().top;
            } else if (attr === 'scrollLeft') {
                val = el.getScroll().left;
            } else if (attr === 'height') {
                val = el.getHeight();
            } else if (attr === 'width') {
                val = el.getWidth();
            } else {
                val = el.getStyle(attr);
            }
        }
        return val;
    },

    getAttr: function(attr, val) {
        var el = this.target;
        return [[ el, this.getElVal(el, attr, val)]];
    },

    setAttr: function(targetData) {
        var target = this.target,
            ln = targetData.length,
            attrs, attr, o, i, j, ln2;
            
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                if (attrs.hasOwnProperty(attr)) {
                    ln2 = attrs[attr].length;
                    for (j = 0; j < ln2; j++) {
                        o = attrs[attr][j];
                        this.setElVal(o[0], attr, o[1]);
                    }
                }
            }
        }
    },
    
    setElVal: function(element, attr, value){
        if (attr === 'x') {
            element.setX(value);
        } else if (attr === 'y') {
            element.setY(value);
        } else if (attr === 'scrollTop') {
            element.scrollTo('top', value);
        } else if (attr === 'scrollLeft') {
            element.scrollTo('left',value);
        } else if (attr === 'width') {
            element.setWidth(value);
        } else if (attr === 'height') {
            element.setHeight(value);
        } else {
            element.setStyle(attr, value);
        }
    }
});


Ext.define('Ext.fx.target.ElementCSS', {

    

    extend:  Ext.fx.target.Element ,

    

    setAttr: function(targetData, isFirstFrame) {
        var cssArr = {
                attrs: [],
                duration: [],
                easing: []
            },
            ln = targetData.length,
            attributes,
            attrs,
            attr,
            easing,
            duration,
            o,
            i,
            j,
            ln2;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i];
            duration = attrs.duration;
            easing = attrs.easing;
            attrs = attrs.attrs;
            for (attr in attrs) {
                if (Ext.Array.indexOf(cssArr.attrs, attr) == -1) {
                    cssArr.attrs.push(attr.replace(/[A-Z]/g, function(v) {
                        return '-' + v.toLowerCase();
                    }));
                    cssArr.duration.push(duration + 'ms');
                    cssArr.easing.push(easing);
                }
            }
        }
        attributes = cssArr.attrs.join(',');
        duration = cssArr.duration.join(',');
        easing = cssArr.easing.join(', ');
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                ln2 = attrs[attr].length;
                for (j = 0; j < ln2; j++) {
                    o = attrs[attr][j];
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', isFirstFrame ? '' : attributes);
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', isFirstFrame ? '' : duration);
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', isFirstFrame ? '' : easing);
                    o[0].setStyle(attr, o[1]);

                    
                    if (isFirstFrame) {
                        o = o[0].dom.offsetWidth;
                    }
                    else {
                        
                        o[0].on(Ext.supports.CSS3TransitionEnd, function() {
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', null);
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', null);
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', null);
                        }, o[0], { single: true });
                    }
                }
            }
        }
    }
});


Ext.define('Ext.fx.target.CompositeElement', {

    

    extend:  Ext.fx.target.Element ,

    

    
    isComposite: true,
    
    constructor: function(target) {
        target.id = target.id || Ext.id(null, 'ext-composite-');
        this.callParent([target]);
    },

    getAttr: function(attr, val) {
        var out      = [],
            target = this.target,
            elements = target.elements,
            length   = elements.length,
            i,
            el;

        for (i = 0; i < length; i++) {
            el = elements[i];

            if (el) {
                el = target.getElement(el);
                out.push([el, this.getElVal(el, attr, val)]);
            }
        }

        return out;
    },
    
    setAttr: function(targetData){
        var target = this.target,
            ln = targetData.length,
            elements = target.elements,
            ln3 = elements.length,
            value, k,
            attrs, attr, o, i, j, ln2;
            
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                if (attrs.hasOwnProperty(attr)) {
                    ln2 = attrs[attr].length;
                    for (j = 0; j < ln2; j++) {
                        value = attrs[attr][j][1];
                        for (k = 0; k < ln3; ++k) {
                            el = elements[k];
                            if (el) {
                                el = target.getElement(el);
                                this.setElVal(el, attr, value);
                            }
                        }
                    }
                }
            }
        }
    }
});


Ext.define('Ext.fx.target.CompositeElementCSS', {

    

    extend:  Ext.fx.target.CompositeElement ,

                                           

    
    setAttr: function() {
        return Ext.fx.target.ElementCSS.prototype.setAttr.apply(this, arguments);
    }
});



Ext.define('Ext.fx.target.Sprite', {

    

    extend:  Ext.fx.target.Target ,

    

    type: 'draw',

    getFromPrim: function (sprite, attr) {
        var obj;
        switch (attr) {
            case 'rotate':
            case 'rotation':
                obj = sprite.attr.rotation;
                return {
                    x: obj.x || 0,
                    y: obj.y || 0,
                    degrees: obj.degrees || 0
                };
            case 'scale':
            case 'scaling':
                obj = sprite.attr.scaling;
                return {
                    x: obj.x || 1,
                    y: obj.y || 1,
                    cx: obj.cx || 0,
                    cy: obj.cy || 0
                };
            case 'translate':
            case 'translation':
                obj = sprite.attr.translation;
                return {
                    x: obj.x || 0,
                    y: obj.y || 0
                };
            default:
                return sprite.attr[attr];
        }
    },

    getAttr: function (attr, val) {
        return [
            [this.target, val != undefined ? val : this.getFromPrim(this.target, attr)]
        ];
    },

    setAttr: function (targetData) {
        var ln = targetData.length,
            spriteArr = [],
            attrsConf, attr, attrArr, attrs, sprite, idx, value, i, j, x, y, ln2;
        for (i = 0; i < ln; i++) {
            attrsConf = targetData[i].attrs;
            for (attr in attrsConf) {
                attrArr = attrsConf[attr];
                ln2 = attrArr.length;
                for (j = 0; j < ln2; j++) {
                    sprite = attrArr[j][0];
                    attrs = attrArr[j][1];
                    if (attr === 'translate' || attr === 'translation') {
                        value = {
                            x: attrs.x,
                            y: attrs.y
                        };
                    }
                    else if (attr === 'rotate' || attr === 'rotation') {
                        x = attrs.x;
                        if (isNaN(x)) {
                            x = null;
                        }
                        y = attrs.y;
                        if (isNaN(y)) {
                            y = null;
                        }
                        value = {
                            degrees: attrs.degrees,
                            x: x,
                            y: y
                        };
                    } else if (attr === 'scale' || attr === 'scaling') {
                        x = attrs.x;
                        if (isNaN(x)) {
                            x = null;
                        }
                        y = attrs.y;
                        if (isNaN(y)) {
                            y = null;
                        }
                        value = {
                            x: x,
                            y: y,
                            cx: attrs.cx,
                            cy: attrs.cy
                        };
                    }
                    else if (attr === 'width' || attr === 'height' || attr === 'x' || attr === 'y') {
                        value = parseFloat(attrs);
                    }
                    else {
                        value = attrs;
                    }
                    idx = Ext.Array.indexOf(spriteArr, sprite);
                    if (idx == -1) {
                        spriteArr.push([sprite, {}]);
                        idx = spriteArr.length - 1;
                    }
                    spriteArr[idx][1][attr] = value;
                }
            }
        }
        ln = spriteArr.length;
        for (i = 0; i < ln; i++) {
            spriteArr[i][0].setAttributes(spriteArr[i][1]);
        }
        this.target.redraw();
    }
});



Ext.define('Ext.fx.target.CompositeSprite', {

    

    extend:  Ext.fx.target.Sprite ,

    

    getAttr: function(attr, val) {
        var out     = [],
            sprites = [].concat(this.target.items),
            length  = sprites.length,
            i,
            sprite;

        for (i = 0; i < length; i++) {
            sprite = sprites[i];
            out.push([sprite, val != undefined ? val : this.getFromPrim(sprite, attr)]);
        }

        return out;
    }
});


Ext.define('Ext.fx.target.Component', {

    
   
    extend:  Ext.fx.target.Target ,
    
    

    type: 'component',

    
    getPropMethod: {
        top: function() {
            return this.getPosition(true)[1];
        },
        left: function() {
            return this.getPosition(true)[0];
        },
        x: function() {
            return this.getPosition()[0];
        },
        y: function() {
            return this.getPosition()[1];
        },
        height: function() {
            return this.getHeight();
        },
        width: function() {
            return this.getWidth();
        },
        opacity: function() {
            return this.el.getStyle('opacity');
        }
    },

    setMethods: {
        top:     'setPosition',
        left:    'setPosition',
        x:       'setPagePosition',
        y:       'setPagePosition',
        height:  'setSize',
        width:   'setSize',
        opacity: 'setOpacity'
    },

    
    getAttr: function(attr, val) {
        return [[this.target, val !== undefined ? val : this.getPropMethod[attr].call(this.target)]];
    },

    setAttr: function(targetData, isFirstFrame, isLastFrame) {
        var me = this,
            ln = targetData.length,
            attrs, attr, o, i, j, targets, left, top, w, h,
            methodsToCall = {},
            methodProps;

        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                targets = attrs[attr].length;
                for (j = 0; j < targets; j++) {
                    o = attrs[attr][j];
                    methodProps = methodsToCall[me.setMethods[attr]] || (methodsToCall[me.setMethods[attr]] = {});
                    methodProps.target = o[0];
                    methodProps[attr] = o[1];
                    
                }
            }
            if (methodsToCall.setPosition) {
                o = methodsToCall.setPosition;
                left = (o.left === undefined) ? undefined : parseFloat(o.left);
                top = (o.top === undefined) ? undefined : parseFloat(o.top);
                o.target.setPosition(left, top);
            }
            if (methodsToCall.setPagePosition) {
                o = methodsToCall.setPagePosition;
                o.target.setPagePosition(o.x, o.y);
            }
            if (methodsToCall.setSize) {
                o = methodsToCall.setSize;
                
                w = (o.width === undefined) ? o.target.getWidth() : parseFloat(o.width);
                h = (o.height === undefined) ? o.target.getHeight() : parseFloat(o.height);

                
                
                
                
                
                
                
                o.target.el.setSize(w, h);
                if (isLastFrame || me.dynamic) {
                    
                    
                    
                    Ext.globalEvents.on({
                        idle: Ext.Function.bind(o.target.setSize, o.target, [w, h]),
                        single: true
                    });
                }
            }
            if (methodsToCall.setOpacity) {
                o = methodsToCall.setOpacity;
                o.target.el.setStyle('opacity', o.opacity);
            }
        }
    }
});



Ext.define('Ext.fx.Queue', {

                                   

    constructor: function() {
        this.targets = new Ext.util.HashMap();
        this.fxQueue = {};
    },

    
    getFxDefaults: function(targetId) {
        var target = this.targets.get(targetId);
        if (target) {
            return target.fxDefaults;
        }
        return {};
    },

    
    setFxDefaults: function(targetId, obj) {
        var target = this.targets.get(targetId);
        if (target) {
            target.fxDefaults = Ext.apply(target.fxDefaults || {}, obj);
        }
    },

    
    stopAnimation: function(targetId) {
        var me = this,
            queue = me.getFxQueue(targetId),
            ln = queue.length;
        while (ln) {
            queue[ln - 1].end();
            ln--;
        }
    },

    
    getActiveAnimation: function(targetId) {
        var queue = this.getFxQueue(targetId);
        return (queue && !!queue.length) ? queue[0] : false;
    },

    
    hasFxBlock: function(targetId) {
        var queue = this.getFxQueue(targetId);
        return queue && queue[0] && queue[0].block;
    },

    
    getFxQueue: function(targetId) {
        if (!targetId) {
            return false;
        }
        var me = this,
            queue = me.fxQueue[targetId],
            target = me.targets.get(targetId);

        if (!target) {
            return false;
        }

        if (!queue) {
            me.fxQueue[targetId] = [];
            
            if (target.type != 'element') {
                target.target.on('destroy', function() {
                    me.fxQueue[targetId] = [];
                });
            }
        }
        return me.fxQueue[targetId];
    },

    
    queueFx: function(anim) {
        var me = this,
            target = anim.target,
            queue, ln;

        if (!target) {
            return;
        }

        queue = me.getFxQueue(target.getId());
        ln = queue.length;

        if (ln) {
            if (anim.concurrent) {
                anim.paused = false;
            }
            else {
                queue[ln - 1].on('afteranimate', function() {
                    anim.paused = false;
                });
            }
        }
        else {
            anim.paused = false;
        }
        anim.on('afteranimate', function() {
            Ext.Array.remove(queue, anim);
            if (queue.length === 0) {
                me.targets.remove(anim.target);
            }
            if (anim.remove) {
                if (target.type == 'element') {
                    var el = Ext.get(target.id);
                    if (el) {
                        el.remove();
                    }
                }
            }
        }, me, {
            single: true
        });
        queue.push(anim);
    }
});



Ext.define('Ext.fx.Manager', {

    

    singleton: true,

                                          
                                       
                                          
                                                
                                                   
                                      
                                               
                                          

    mixins: {
        queue:  Ext.fx.Queue 
    },

    

    constructor: function() {
        var me = this;
        me.items = new Ext.util.MixedCollection();
        me.mixins.queue.constructor.call(me);
        
        
        
        me.taskRunner = new Ext.util.TaskRunner();

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    },

    
    interval: 16,

    
    forceJS: true,

    
    createTarget: function(target) {
        var me = this,
            useCSS3 = !me.forceJS && Ext.supports.Transitions,
            targetObj;

        me.useCSS3 = useCSS3;

        if (target) {
            
            if (target.tagName || Ext.isString(target) || target.isFly) {
                target = Ext.get(target);
                targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
            }
            
            else if (target.dom) {
                targetObj = new Ext.fx.target['Element' + (useCSS3 ? 'CSS' : '')](target);
            }
            
            else if (target.isComposite) {
                targetObj = new Ext.fx.target['CompositeElement' + (useCSS3 ? 'CSS' : '')](target);
            }
            
            else if (target.isSprite) {
                targetObj = new Ext.fx.target.Sprite(target);
            }
            
            else if (target.isCompositeSprite) {
                targetObj = new Ext.fx.target.CompositeSprite(target);
            }
            
            else if (target.isComponent) {
                targetObj = new Ext.fx.target.Component(target);
            }
            else if (target.isAnimTarget) {
                return target;
            }
            else {
                return null;
            }
            me.targets.add(targetObj);
            return targetObj;
        }
        else {
            return null;
        }
    },

    
    addAnim: function(anim) {
        var me = this,
            items = me.items,
            task = me.task;

        
        
        
        items.add(anim.id, anim);
        

        
        if (!task && items.length) {
            task = me.task = {
                run: me.runner,
                interval: me.interval,
                scope: me
            };
            
            me.taskRunner.start(task);
        }
    },

    
    removeAnim: function(anim) {
        var me = this,
            items = me.items,
            task = me.task;
                
        items.removeAtKey(anim.id);
        
        
        
        if (task && !items.length) {
            
            me.taskRunner.stop(task);
            delete me.task;
        }
    },

    
    runner: function() {
        var me = this,
            items = me.items.getRange(),
            i = 0,
            len = items.length,
            anim;

        
        me.targetArr = {};

        
        me.timestamp = new Date();
        
        
        
        
        
        
        
        
        
        
        
        for (; i < len; i++) {
            anim = items[i];
            
            if (anim.isReady()) {
                
                me.startAnim(anim);
            }
        }
        
        for (i = 0; i < len; i++) {
            anim = items[i];
            
            if (anim.isRunning()) {
                
                me.runAnim(anim);
            }
        }

        
        me.applyPendingAttrs();
    },

    
    startAnim: function(anim) {
        anim.start(this.timestamp);
    },

    
    runAnim: function(anim) {
        if (!anim) {
            return;
        }
        var me = this,
            useCSS3 = me.useCSS3 && anim.target.type == 'element',
            elapsedTime = me.timestamp - anim.startTime,
            lastFrame = (elapsedTime >= anim.duration),
            target, o;

        target = this.collectTargetData(anim, elapsedTime, useCSS3, lastFrame);
        
        
        
        if (useCSS3) {
            
            
            
            anim.target.setAttr(target.anims[anim.id].attributes, true);

            
            me.collectTargetData(anim, anim.duration, useCSS3, lastFrame);

            
            anim.paused = true;

            target = anim.target.target;
            
            if (anim.target.isComposite) {
                target = anim.target.target.last();
            }

            
            o = {};
            o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
            o.scope = anim;
            o.single = true;
            target.on(o);
        }
    },

    
    collectTargetData: function(anim, elapsedTime, useCSS3, isLastFrame) {
        var targetId = anim.target.getId(),
            target = this.targetArr[targetId];
        
        if (!target) {
            
            
            
            
            target = this.targetArr[targetId] = {
                id: targetId,
                el: anim.target,
                anims: {}
            };
        }

        
        
        
        
        target.anims[anim.id] = {
            id: anim.id,
            anim: anim,
            elapsed: elapsedTime,
            isLastFrame: isLastFrame,
            
            attributes: [{
                duration: anim.duration,
                easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,
                
                
                attrs: anim.runAnim(elapsedTime)
            }]
        };
        
        return target;
    },
    
    
    applyPendingAttrs: function() {
        var targetArr = this.targetArr,
            target, targetId, animWrap, anim, animId;
        
        
        for (targetId in targetArr) {
            if (targetArr.hasOwnProperty(targetId)) {
                target = targetArr[targetId];
                
                
                for (animId in target.anims) {
                    if (target.anims.hasOwnProperty(animId)) {
                        animWrap = target.anims[animId];
                        anim = animWrap.anim;
                        
                        
                        if (animWrap.attributes && anim.isRunning()) {
                            
                            target.el.setAttr(animWrap.attributes, false, animWrap.isLastFrame);
                            
                            
                            if (animWrap.isLastFrame) {
                                
                                anim.lastFrame();
                            }
                        }
                    }
                }
            }
        }
    }
});


Ext.define('Ext.fx.Animator', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

                                 

    

    
    isAnimator: true,

    
    duration: 250,

    
    delay: 0,

    
    delayStart: 0,

    
    dynamic: false,

    
    easing: 'ease',

    
    running: false,

    
    paused: false,

    
    damper: 1,

    
    iterations: 1,

    
    currentIteration: 0,

    
    keyframeStep: 0,

    
    animKeyFramesRE: /^(from|to|\d+%?)$/,

    

     
    constructor: function(config) {
        var me = this;
        config = Ext.apply(me, config || {});
        me.config = config;
        me.id = Ext.id(null, 'ext-animator-');
        me.addEvents(
            
            'beforeanimate',
            
            'keyframe',
            
            'afteranimate'
        );
        me.mixins.observable.constructor.call(me, config);
        me.timeline = [];
        me.createTimeline(me.keyframes);
        if (me.target) {
            me.applyAnimator(me.target);
            Ext.fx.Manager.addAnim(me);
        }
    },

    
    sorter: function (a, b) {
        return a.pct - b.pct;
    },

    
    createTimeline: function(keyframes) {
        var me = this,
            attrs = [],
            to = me.to || {},
            duration = me.duration,
            prevMs, ms, i, ln, pct, attr;

        for (pct in keyframes) {
            if (keyframes.hasOwnProperty(pct) && me.animKeyFramesRE.test(pct)) {
                attr = {attrs: Ext.apply(keyframes[pct], to)};
                
                if (pct == "from") {
                    pct = 0;
                }
                else if (pct == "to") {
                    pct = 100;
                }
                
                attr.pct = parseInt(pct, 10);
                attrs.push(attr);
            }
        }
        
        Ext.Array.sort(attrs, me.sorter);
        
        
        
        

        ln = attrs.length;
        for (i = 0; i < ln; i++) {
            prevMs = (attrs[i - 1]) ? duration * (attrs[i - 1].pct / 100) : 0;
            ms = duration * (attrs[i].pct / 100);
            me.timeline.push({
                duration: ms - prevMs,
                attrs: attrs[i].attrs
            });
        }
    },

    
    applyAnimator: function(target) {
        var me = this,
            anims = [],
            timeline = me.timeline,
            ln = timeline.length,
            anim, easing, damper, attrs, i;

        if (me.fireEvent('beforeanimate', me) !== false) {
            for (i = 0; i < ln; i++) {
                anim = timeline[i];
                attrs = anim.attrs;
                easing = attrs.easing || me.easing;
                damper = attrs.damper || me.damper;
                delete attrs.easing;
                delete attrs.damper;
                anim = new Ext.fx.Anim({
                    target: target,
                    easing: easing,
                    damper: damper,
                    duration: anim.duration,
                    paused: true,
                    to: attrs
                });
                anims.push(anim);
            }
            me.animations = anims;
            me.target = anim.target;
            for (i = 0; i < ln - 1; i++) {
                anim = anims[i];
                anim.nextAnim = anims[i + 1];
                anim.on('afteranimate', function() {
                    this.nextAnim.paused = false;
                });
                anim.on('afteranimate', function() {
                    this.fireEvent('keyframe', this, ++this.keyframeStep);
                }, me);
            }
            anims[ln - 1].on('afteranimate', function() {
                this.lastFrame();
            }, me);
        }
    },

    
    start: function(startTime) {
        var me = this,
            delay = me.delay,
            delayStart = me.delayStart,
            delayDelta;
        if (delay) {
            if (!delayStart) {
                me.delayStart = startTime;
                return;
            }
            else {
                delayDelta = startTime - delayStart;
                if (delayDelta < delay) {
                    return;
                }
                else {
                    
                    startTime = new Date(delayStart.getTime() + delay);
                }
            }
        }
        if (me.fireEvent('beforeanimate', me) !== false) {
            me.startTime = startTime;
            me.running = true;
            me.animations[me.keyframeStep].paused = false;
        }
    },

    
    lastFrame: function() {
        var me = this,
            iter = me.iterations,
            iterCount = me.currentIteration;

        iterCount++;
        if (iterCount < iter) {
            me.startTime = new Date();
            me.currentIteration = iterCount;
            me.keyframeStep = 0;
            me.applyAnimator(me.target);
            me.animations[me.keyframeStep].paused = false;
        }
        else {
            me.currentIteration = 0;
            me.end();
        }
    },

    
    end: function() {
        var me = this;
        me.fireEvent('afteranimate', me, me.startTime, new Date() - me.startTime);
    },
    
    isReady: function() {
        return this.paused === false && this.running === false && this.iterations > 0;
    },
    
    isRunning: function() {
        
        return false;
    }
});


Ext.define('Ext.fx.CubicBezier', {

    

    singleton: true,

    

    cubicBezierAtTime: function(t, p1x, p1y, p2x, p2y, duration) {
        var cx = 3 * p1x,
            bx = 3 * (p2x - p1x) - cx,
            ax = 1 - cx - bx,
            cy = 3 * p1y,
            by = 3 * (p2y - p1y) - cy,
            ay = 1 - cy - by;
        function sampleCurveX(t) {
            return ((ax * t + bx) * t + cx) * t;
        }
        function solve(x, epsilon) {
            var t = solveCurveX(x, epsilon);
            return ((ay * t + by) * t + cy) * t;
        }
        function solveCurveX(x, epsilon) {
            var t0, t1, t2, x2, d2, i;
            for (t2 = x, i = 0; i < 8; i++) {
                x2 = sampleCurveX(t2) - x;
                if (Math.abs(x2) < epsilon) {
                    return t2;
                }
                d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
                if (Math.abs(d2) < 1e-6) {
                    break;
                }
                t2 = t2 - x2 / d2;
            }
            t0 = 0;
            t1 = 1;
            t2 = x;
            if (t2 < t0) {
                return t0;
            }
            if (t2 > t1) {
                return t1;
            }
            while (t0 < t1) {
                x2 = sampleCurveX(t2);
                if (Math.abs(x2 - x) < epsilon) {
                    return t2;
                }
                if (x > x2) {
                    t0 = t2;
                } else {
                    t1 = t2;
                }
                t2 = (t1 - t0) / 2 + t0;
            }
            return t2;
        }
        return solve(t, 1 / (200 * duration));
    },

    cubicBezier: function(x1, y1, x2, y2) {
        var fn = function(pos) {
            return Ext.fx.CubicBezier.cubicBezierAtTime(pos, x1, y1, x2, y2, 1);
        };
        fn.toCSS3 = function() {
            return 'cubic-bezier(' + [x1, y1, x2, y2].join(',') + ')';
        };
        fn.reverse = function() {
            return Ext.fx.CubicBezier.cubicBezier(1 - x2, 1 - y2, 1 - x1, 1 - y1);
        };
        return fn;
    }
});




Ext.require('Ext.fx.CubicBezier', function() {
    var math = Math,
        pi = math.PI,
        pow = math.pow,
        sin = math.sin,
        sqrt = math.sqrt,
        abs = math.abs,
        backInSeed = 1.70158;
        
    Ext.define('Ext.fx.Easing', {
        singleton: true,
        
        linear: Ext.identityFn,
        ease: function(n) {
            var q = 0.07813 - n / 2,
                alpha = -0.25,
                Q = sqrt(0.0066 + q * q),
                x = Q - q,
                X = pow(abs(x), 1/3) * (x < 0 ? -1 : 1),
                y = -Q - q,
                Y = pow(abs(y), 1/3) * (y < 0 ? -1 : 1),
                t = X + Y + 0.25;
            return pow(1 - t, 2) * 3 * t * 0.1 + (1 - t) * 3 * t * t + t * t * t;
        },
        easeIn: function (n) {
            return pow(n, 1.7);
        },
        easeOut: function (n) {
            return pow(n, 0.48);
        },
        easeInOut: function(n) {
            var q = 0.48 - n / 1.04,
                Q = sqrt(0.1734 + q * q),
                x = Q - q,
                X = pow(abs(x), 1/3) * (x < 0 ? -1 : 1),
                y = -Q - q,
                Y = pow(abs(y), 1/3) * (y < 0 ? -1 : 1),
                t = X + Y + 0.5;
            return (1 - t) * 3 * t * t + t * t * t;
        },
        backIn: function (n) {
            return n * n * ((backInSeed + 1) * n - backInSeed);
        },
        backOut: function (n) {
            n = n - 1;
            return n * n * ((backInSeed + 1) * n + backInSeed) + 1;
        },
        elasticIn: function (n) {
            if (n === 0 || n === 1) {
                return n;
            }
            var p = 0.3,
                s = p / 4;
            return pow(2, -10 * n) * sin((n - s) * (2 * pi) / p) + 1;
        },
        elasticOut: function (n) {
            return 1 - Ext.fx.Easing.elasticIn(1 - n);
        },
        bounceIn: function (n) {
            return 1 - Ext.fx.Easing.bounceOut(1 - n);
        },
        bounceOut: function (n) {
            var s = 7.5625,
                p = 2.75,
                l;
            if (n < (1 / p)) {
                l = s * n * n;
            } else {
                if (n < (2 / p)) {
                    n -= (1.5 / p);
                    l = s * n * n + 0.75;
                } else {
                    if (n < (2.5 / p)) {
                        n -= (2.25 / p);
                        l = s * n * n + 0.9375;
                    } else {
                        n -= (2.625 / p);
                        l = s * n * n + 0.984375;
                    }
                }
            }
            return l;
        }
    }, function(){
        var easing = Ext.fx.Easing.self,
            proto = easing.prototype;
            
        easing.implement({
            'back-in': proto.backIn,
            'back-out': proto.backOut,
            'ease-in': proto.easeIn,
            'ease-out': proto.easeOut,
            'elastic-in': proto.elasticIn,
            'elastic-out': proto.elasticOut,
            'bounce-in': proto.bounceIn,
            'bounce-out': proto.bounceOut,
            'ease-in-out': proto.easeInOut
        });        
    });
});


Ext.define('Ext.draw.Color', {

    

    

    colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,
    rgbRe: /\s*rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,
    hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/,

    
    lightnessFactor: 0.2,

    
    constructor : function(red, green, blue) {
        var me = this,
            clamp = Ext.Number.constrain;
        me.r = clamp(red, 0, 255);
        me.g = clamp(green, 0, 255);
        me.b = clamp(blue, 0, 255);
    },

    
    getRed: function() {
        return this.r;
    },

    
    getGreen: function() {
        return this.g;
    },

    
    getBlue: function() {
        return this.b;
    },

    
    getRGB: function() {
        var me = this;
        return [me.r, me.g, me.b];
    },

    
    getHSL: function() {
        var me = this,
            r = me.r / 255,
            g = me.g / 255,
            b = me.b / 255,
            max = Math.max(r, g, b),
            min = Math.min(r, g, b),
            delta = max - min,
            h,
            s = 0,
            l = 0.5 * (max + min);

        
        if (min != max) {
            s = (l < 0.5) ? delta / (max + min) : delta / (2 - max - min);
            if (r == max) {
                h = 60 * (g - b) / delta;
            } else if (g == max) {
                h = 120 + 60 * (b - r) / delta;
            } else {
                h = 240 + 60 * (r - g) / delta;
            }
            if (h < 0) {
                h += 360;
            }
            if (h >= 360) {
                h -= 360;
            }
        }
        return [h, s, l];
    },

    
    getLighter: function(factor) {
        var hsl = this.getHSL();
        factor = factor || this.lightnessFactor;
        hsl[2] = Ext.Number.constrain(hsl[2] + factor, 0, 1);
        return this.fromHSL(hsl[0], hsl[1], hsl[2]);
    },

    
    getDarker: function(factor) {
        factor = factor || this.lightnessFactor;
        return this.getLighter(-factor);
    },

    
    toString: function() {
        var me = this,
            round = Math.round,
            r = round(me.r).toString(16),
            g = round(me.g).toString(16),
            b = round(me.b).toString(16);
        r = (r.length == 1) ? '0' + r : r;
        g = (g.length == 1) ? '0' + g : g;
        b = (b.length == 1) ? '0' + b : b;
        return ['#', r, g, b].join('');
    },

    
    toHex: function(color) {
        if (Ext.isArray(color)) {
            color = color[0];
        }
        if (!Ext.isString(color)) {
            return '';
        }
        if (color.substr(0, 1) === '#') {
            return color;
        }
        var digits = this.colorToHexRe.exec(color),
            red,
            green,
            blue,
            rgb;

        if (Ext.isArray(digits)) {
            red = parseInt(digits[2], 10);
            green = parseInt(digits[3], 10);
            blue = parseInt(digits[4], 10);
            rgb = blue | (green << 8) | (red << 16);
            return digits[1] + '#' + ("000000" + rgb.toString(16)).slice(-6);
        }
        else {
            return color;
        }
    },

    
    fromString: function(str) {
        var values, r, g, b,
            parse = parseInt;

        if ((str.length == 4 || str.length == 7) && str.substr(0, 1) === '#') {
            values = str.match(this.hexRe);
            if (values) {
                r = parse(values[1], 16) >> 0;
                g = parse(values[2], 16) >> 0;
                b = parse(values[3], 16) >> 0;
                if (str.length == 4) {
                    r += (r * 16);
                    g += (g * 16);
                    b += (b * 16);
                }
            }
        }
        else {
            values = str.match(this.rgbRe);
            if (values) {
                r = values[1];
                g = values[2];
                b = values[3];
            }
        }

        return (typeof r == 'undefined') ? undefined : new Ext.draw.Color(r, g, b);
    },

    
    getGrayscale: function() {
        
        return this.r * 0.3 + this.g * 0.59 + this.b * 0.11;
    },

    
    fromHSL: function(h, s, l) {
        var C, X, m, i, rgb = [],
            abs = Math.abs,
            floor = Math.floor;

        if (s == 0 || h == null) {
            
            rgb = [l, l, l];
        }
        else {
            
            
            
            
            h /= 60;
            C = s * (1 - abs(2 * l - 1));
            X = C * (1 - abs(h - 2 * floor(h / 2) - 1));
            m = l - C / 2;
            switch (floor(h)) {
                case 0:
                    rgb = [C, X, 0];
                    break;
                case 1:
                    rgb = [X, C, 0];
                    break;
                case 2:
                    rgb = [0, C, X];
                    break;
                case 3:
                    rgb = [0, X, C];
                    break;
                case 4:
                    rgb = [X, 0, C];
                    break;
                case 5:
                    rgb = [C, 0, X];
                    break;
            }
            rgb = [rgb[0] + m, rgb[1] + m, rgb[2] + m];
        }
        return new Ext.draw.Color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255);
    }
}, function() {
    var prototype = this.prototype;

    
    this.addStatics({
        fromHSL: function() {
            return prototype.fromHSL.apply(prototype, arguments);
        },
        fromString: function() {
            return prototype.fromString.apply(prototype, arguments);
        },
        toHex: function() {
            return prototype.toHex.apply(prototype, arguments);
        }
    });
});


Ext.define('Ext.draw.Draw', {
    

    singleton: true,

                                 

    

    pathToStringRE: /,?([achlmqrstvxz]),?/gi,
    pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
    pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
    stopsRE: /^(\d+%?)$/,
    radian: Math.PI / 180,

    availableAnimAttrs: {
        along: "along",
        blur: null,
        "clip-rect": "csv",
        cx: null,
        cy: null,
        fill: "color",
        "fill-opacity": null,
        "font-size": null,
        height: null,
        opacity: null,
        path: "path",
        r: null,
        rotation: "csv",
        rx: null,
        ry: null,
        scale: "csv",
        stroke: "color",
        "stroke-opacity": null,
        "stroke-width": null,
        translation: "csv",
        width: null,
        x: null,
        y: null
    },

    is: function(o, type) {
        type = String(type).toLowerCase();
        return (type == "object" && o === Object(o)) ||
            (type == "undefined" && typeof o == type) ||
            (type == "null" && o === null) ||
            (type == "array" && Array.isArray && Array.isArray(o)) ||
            (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;
    },

    ellipsePath: function(sprite) {
        var attr = sprite.attr;
        return Ext.String.format("M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z", attr.x, attr.y - attr.ry, attr.rx, attr.ry, attr.y + attr.ry);
    },

    rectPath: function(sprite) {
        var attr = sprite.attr;
        if (attr.radius) {
            return Ext.String.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", attr.x + attr.radius, attr.y, attr.width - attr.radius * 2, attr.radius, -attr.radius, attr.height - attr.radius * 2, attr.radius * 2 - attr.width, attr.radius * 2 - attr.height);
        }
        else {
            return Ext.String.format("M{0},{1}L{2},{1},{2},{3},{0},{3}z", attr.x, attr.y, attr.width + attr.x, attr.height + attr.y);
        }
    },

    
    path2string: function () {
        return this.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
    },

    
    pathToString: function(arrayPath) {
        return arrayPath.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
    },

    parsePathString: function (pathString) {
        if (!pathString) {
            return null;
        }
        var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
            data = [],
            me = this;
        if (me.is(pathString, "array") && me.is(pathString[0], "array")) { 
            data = me.pathClone(pathString);
        }
        if (!data.length) {
            String(pathString).replace(me.pathCommandRE, function (a, b, c) {
                var params = [],
                    name = b.toLowerCase();
                c.replace(me.pathValuesRE, function (a, b) {
                    b && params.push(+b);
                });
                if (name == "m" && params.length > 2) {
                    data.push([b].concat(Ext.Array.splice(params, 0, 2)));
                    name = "l";
                    b = (b == "m") ? "l" : "L";
                }
                while (params.length >= paramCounts[name]) {
                    data.push([b].concat(Ext.Array.splice(params, 0, paramCounts[name])));
                    if (!paramCounts[name]) {
                        break;
                    }
                }
            });
        }
        data.toString = me.path2string;
        return data;
    },

    mapPath: function (path, matrix) {
        if (!matrix) {
            return path;
        }
        var x, y, i, ii, j, jj, pathi;
        path = this.path2curve(path);
        for (i = 0, ii = path.length; i < ii; i++) {
            pathi = path[i];
            for (j = 1, jj = pathi.length; j < jj-1; j += 2) {
                x = matrix.x(pathi[j], pathi[j + 1]);
                y = matrix.y(pathi[j], pathi[j + 1]);
                pathi[j] = x;
                pathi[j + 1] = y;
            }
        }
        return path;
    },

    pathClone: function(pathArray) {
        var res = [],
            j, jj, i, ii;
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { 
            pathArray = this.parsePathString(pathArray);
        }
        for (i = 0, ii = pathArray.length; i < ii; i++) {
            res[i] = [];
            for (j = 0, jj = pathArray[i].length; j < jj; j++) {
                res[i][j] = pathArray[i][j];
            }
        }
        res.toString = this.path2string;
        return res;
    },

    pathToAbsolute: function (pathArray) {
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { 
            pathArray = this.parsePathString(pathArray);
        }
        var res = [],
            x = 0,
            y = 0,
            mx = 0,
            my = 0,
            i = 0,
            ln = pathArray.length,
            r, pathSegment, j, ln2;
        
        if (ln && pathArray[0][0] == "M") {
            x = +pathArray[0][1];
            y = +pathArray[0][2];
            mx = x;
            my = y;
            i++;
            res[0] = ["M", x, y];
        }
        for (; i < ln; i++) {
            r = res[i] = [];
            pathSegment = pathArray[i];
            if (pathSegment[0] != pathSegment[0].toUpperCase()) {
                r[0] = pathSegment[0].toUpperCase();
                switch (r[0]) {
                    
                    case "A":
                        r[1] = pathSegment[1];
                        r[2] = pathSegment[2];
                        r[3] = pathSegment[3];
                        r[4] = pathSegment[4];
                        r[5] = pathSegment[5];
                        r[6] = +(pathSegment[6] + x);
                        r[7] = +(pathSegment[7] + y);
                        break;
                    
                    case "V":
                        r[1] = +pathSegment[1] + y;
                        break;
                    
                    case "H":
                        r[1] = +pathSegment[1] + x;
                        break;
                    case "M":
                    
                        mx = +pathSegment[1] + x;
                        my = +pathSegment[2] + y;
                    default:
                        j = 1;
                        ln2 = pathSegment.length;
                        for (; j < ln2; j++) {
                            r[j] = +pathSegment[j] + ((j % 2) ? x : y);
                        }
                }
            }
            else {
                j = 0;
                ln2 = pathSegment.length;
                for (; j < ln2; j++) {
                    res[i][j] = pathSegment[j];
                }
            }
            switch (r[0]) {
                
                case "Z":
                    x = mx;
                    y = my;
                    break;
                
                case "H":
                    x = r[1];
                    break;
                
                case "V":
                    y = r[1];
                    break;
                
                case "M":
                    pathSegment = res[i];
                    ln2 = pathSegment.length;
                    mx = pathSegment[ln2 - 2];
                    my = pathSegment[ln2 - 1];
                default:
                    pathSegment = res[i];
                    ln2 = pathSegment.length;
                    x = pathSegment[ln2 - 2];
                    y = pathSegment[ln2 - 1];
            }
        }
        res.toString = this.path2string;
        return res;
    },

    
    pathToRelative: function (pathArray) {
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) {
            pathArray = this.parsePathString(pathArray);
        }
        var res = [],
            x = 0,
            y = 0,
            mx = 0,
            my = 0,
            start = 0,
            r,
            pa,
            i,
            j,
            k,
            len,
            ii,
            jj,
            kk;
        
        if (pathArray[0][0] == "M") {
            x = pathArray[0][1];
            y = pathArray[0][2];
            mx = x;
            my = y;
            start++;
            res.push(["M", x, y]);
        }
        for (i = start, ii = pathArray.length; i < ii; i++) {
            r = res[i] = [];
            pa = pathArray[i];
            if (pa[0] != pa[0].toLowerCase()) {
                r[0] = pa[0].toLowerCase();
                switch (r[0]) {
                    case "a":
                        r[1] = pa[1];
                        r[2] = pa[2];
                        r[3] = pa[3];
                        r[4] = pa[4];
                        r[5] = pa[5];
                        r[6] = +(pa[6] - x).toFixed(3);
                        r[7] = +(pa[7] - y).toFixed(3);
                        break;
                    case "v":
                        r[1] = +(pa[1] - y).toFixed(3);
                        break;
                    case "m":
                        mx = pa[1];
                        my = pa[2];
                    default:
                        for (j = 1, jj = pa.length; j < jj; j++) {
                            r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
                        }
                }
            } else {
                r = res[i] = [];
                if (pa[0] == "m") {
                    mx = pa[1] + x;
                    my = pa[2] + y;
                }
                for (k = 0, kk = pa.length; k < kk; k++) {
                    res[i][k] = pa[k];
                }
            }
            len = res[i].length;
            switch (res[i][0]) {
                case "z":
                    x = mx;
                    y = my;
                    break;
                case "h":
                    x += +res[i][len - 1];
                    break;
                case "v":
                    y += +res[i][len - 1];
                    break;
                default:
                    x += +res[i][len - 2];
                    y += +res[i][len - 1];
            }
        }
        res.toString = this.path2string;
        return res;
    },

    
    path2curve: function (path) {
        var me = this,
            points = me.pathToAbsolute(path),
            ln = points.length,
            attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
            i, seg, segLn, point;
            
        for (i = 0; i < ln; i++) {
            points[i] = me.command2curve(points[i], attrs);
            if (points[i].length > 7) {
                    points[i].shift();
                    point = points[i];
                    while (point.length) {
                        Ext.Array.splice(points, i++, 0, ["C"].concat(Ext.Array.splice(point, 0, 6)));
                    }
                    Ext.Array.erase(points, i, 1);
                    ln = points.length;
                    i--;
                }
            seg = points[i];
            segLn = seg.length;
            attrs.x = seg[segLn - 2];
            attrs.y = seg[segLn - 1];
            attrs.bx = parseFloat(seg[segLn - 4]) || attrs.x;
            attrs.by = parseFloat(seg[segLn - 3]) || attrs.y;
        }
        return points;
    },
    
    interpolatePaths: function (path, path2) {
        var me = this,
            p = me.pathToAbsolute(path),
            p2 = me.pathToAbsolute(path2),
            attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
            attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
            fixArc = function (pp, i) {
                if (pp[i].length > 7) {
                    pp[i].shift();
                    var pi = pp[i];
                    while (pi.length) {
                        Ext.Array.splice(pp, i++, 0, ["C"].concat(Ext.Array.splice(pi, 0, 6)));
                    }
                    Ext.Array.erase(pp, i, 1);
                    ii = Math.max(p.length, p2.length || 0);
                }
            },
            fixM = function (path1, path2, a1, a2, i) {
                if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
                    Ext.Array.splice(path2, i, 0, ["M", a2.x, a2.y]);
                    a1.bx = 0;
                    a1.by = 0;
                    a1.x = path1[i][1];
                    a1.y = path1[i][2];
                    ii = Math.max(p.length, p2.length || 0);
                }
            },
            i, ii,
            seg, seg2, seglen, seg2len;
        for (i = 0, ii = Math.max(p.length, p2.length || 0); i < ii; i++) {
            p[i] = me.command2curve(p[i], attrs);
            fixArc(p, i);
            (p2[i] = me.command2curve(p2[i], attrs2));
            fixArc(p2, i);
            fixM(p, p2, attrs, attrs2, i);
            fixM(p2, p, attrs2, attrs, i);
            seg = p[i];
            seg2 = p2[i];
            seglen = seg.length;
            seg2len = seg2.length;
            attrs.x = seg[seglen - 2];
            attrs.y = seg[seglen - 1];
            attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
            attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
            attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);
            attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);
            attrs2.x = seg2[seg2len - 2];
            attrs2.y = seg2[seg2len - 1];
        }
        return [p, p2];
    },
    
    
    command2curve: function (pathCommand, d) {
        var me = this;
        if (!pathCommand) {
            return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
        }
        if (pathCommand[0] != "T" && pathCommand[0] != "Q") {
            d.qx = d.qy = null;
        }
        switch (pathCommand[0]) {
            case "M":
                d.X = pathCommand[1];
                d.Y = pathCommand[2];
                break;
            case "A":
                pathCommand = ["C"].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));
                break;
            case "S":
                pathCommand = ["C", d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));
                break;
            case "T":
                d.qx = d.x + (d.x - (d.qx || d.x));
                d.qy = d.y + (d.y - (d.qy || d.y));
                pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));
                break;
            case "Q":
                d.qx = pathCommand[1];
                d.qy = pathCommand[2];
                pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));
                break;
            case "L":
                pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);
                break;
            case "H":
                pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);
                break;
            case "V":
                pathCommand = ["C"].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);
                break;
            case "Z":
                pathCommand = ["C"].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);
                break;
        }
        return pathCommand;
    },

    quadratic2curve: function (x1, y1, ax, ay, x2, y2) {
        var _13 = 1 / 3,
            _23 = 2 / 3;
        return [
                _13 * x1 + _23 * ax,
                _13 * y1 + _23 * ay,
                _13 * x2 + _23 * ax,
                _13 * y2 + _23 * ay,
                x2,
                y2
            ];
    },
    
    rotate: function (x, y, rad) {
        var cos = Math.cos(rad),
            sin = Math.sin(rad),
            X = x * cos - y * sin,
            Y = x * sin + y * cos;
        return {x: X, y: Y};
    },

    arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
        
        
        var me = this,
            PI = Math.PI,
            radian = me.radian,
            _120 = PI * 120 / 180,
            rad = radian * (+angle || 0),
            res = [],
            math = Math,
            mcos = math.cos,
            msin = math.sin,
            msqrt = math.sqrt,
            mabs = math.abs,
            masin = math.asin,
            xy, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2,
            t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;
        if (!recursive) {
            xy = me.rotate(x1, y1, -rad);
            x1 = xy.x;
            y1 = xy.y;
            xy = me.rotate(x2, y2, -rad);
            x2 = xy.x;
            y2 = xy.y;
            x = (x1 - x2) / 2;
            y = (y1 - y2) / 2;
            h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
            if (h > 1) {
                h = msqrt(h);
                rx = h * rx;
                ry = h * ry;
            }
            rx2 = rx * rx;
            ry2 = ry * ry;
            k = (large_arc_flag == sweep_flag ? -1 : 1) *
                    msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
            cx = k * rx * y / ry + (x1 + x2) / 2;
            cy = k * -ry * x / rx + (y1 + y2) / 2;
            f1 = masin(((y1 - cy) / ry).toFixed(7));
            f2 = masin(((y2 - cy) / ry).toFixed(7));

            f1 = x1 < cx ? PI - f1 : f1;
            f2 = x2 < cx ? PI - f2 : f2;
            if (f1 < 0) {
                f1 = PI * 2 + f1;
            }
            if (f2 < 0) {
                f2 = PI * 2 + f2;
            }
            if (sweep_flag && f1 > f2) {
                f1 = f1 - PI * 2;
            }
            if (!sweep_flag && f2 > f1) {
                f2 = f2 - PI * 2;
            }
        }
        else {
            f1 = recursive[0];
            f2 = recursive[1];
            cx = recursive[2];
            cy = recursive[3];
        }
        df = f2 - f1;
        if (mabs(df) > _120) {
            f2old = f2;
            x2old = x2;
            y2old = y2;
            f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
            x2 = cx + rx * mcos(f2);
            y2 = cy + ry * msin(f2);
            res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
        }
        df = f2 - f1;
        c1 = mcos(f1);
        s1 = msin(f1);
        c2 = mcos(f2);
        s2 = msin(f2);
        t = math.tan(df / 4);
        hx = 4 / 3 * rx * t;
        hy = 4 / 3 * ry * t;
        m1 = [x1, y1];
        m2 = [x1 + hx * s1, y1 - hy * c1];
        m3 = [x2 + hx * s2, y2 - hy * c2];
        m4 = [x2, y2];
        m2[0] = 2 * m1[0] - m2[0];
        m2[1] = 2 * m1[1] - m2[1];
        if (recursive) {
            return [m2, m3, m4].concat(res);
        }
        else {
            res = [m2, m3, m4].concat(res).join().split(",");
            newres = [];
            ln = res.length;
            for (i = 0;  i < ln; i++) {
                newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;
            }
            return newres;
        }
    },

    
    rotateAndTranslatePath: function (sprite) {
        var alpha = sprite.rotation.degrees,
            cx = sprite.rotation.x,
            cy = sprite.rotation.y,
            dx = sprite.translation.x,
            dy = sprite.translation.y,
            path,
            i,
            p,
            xy,
            j,
            res = [];
        if (!alpha && !dx && !dy) {
            return this.pathToAbsolute(sprite.attr.path);
        }
        dx = dx || 0;
        dy = dy || 0;
        path = this.pathToAbsolute(sprite.attr.path);
        for (i = path.length; i--;) {
            p = res[i] = path[i].slice();
            if (p[0] == "A") {
                xy = this.rotatePoint(p[6], p[7], alpha, cx, cy);
                p[6] = xy.x + dx;
                p[7] = xy.y + dy;
            } else {
                j = 1;
                while (p[j + 1] != null) {
                    xy = this.rotatePoint(p[j], p[j + 1], alpha, cx, cy);
                    p[j] = xy.x + dx;
                    p[j + 1] = xy.y + dy;
                    j += 2;
                }
            }
        }
        return res;
    },

    
    rotatePoint: function (x, y, alpha, cx, cy) {
        if (!alpha) {
            return {
                x: x,
                y: y
            };
        }
        cx = cx || 0;
        cy = cy || 0;
        x = x - cx;
        y = y - cy;
        alpha = alpha * this.radian;
        var cos = Math.cos(alpha),
            sin = Math.sin(alpha);
        return {
            x: x * cos - y * sin + cx,
            y: x * sin + y * cos + cy
        };
    },

    pathDimensions: function (path) {
        if (!path || !(path + "")) {
            return {x: 0, y: 0, width: 0, height: 0};
        }
        path = this.path2curve(path);
        var x = 0, 
            y = 0,
            X = [],
            Y = [],
            i = 0,
            ln = path.length,
            p, xmin, ymin, xmax, ymax, dim;
        for (; i < ln; i++) {
            p = path[i];
            if (p[0] == "M") {
                x = p[1];
                y = p[2];
                X.push(x);
                Y.push(y);
            }
            else {
                dim = this.curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
                X = X.concat(dim.min.x, dim.max.x);
                Y = Y.concat(dim.min.y, dim.max.y);
                x = p[5];
                y = p[6];
            }
        }
        xmin = Math.min.apply(0, X);
        ymin = Math.min.apply(0, Y);
        xmax = Math.max.apply(0, X);
        ymax = Math.max.apply(0, Y);
        return {
            x: Math.round(xmin),
            y: Math.round(ymin),
            path: path,
            width: Math.round(xmax - xmin),
            height: Math.round(ymax - ymin)
        };
    },

    intersectInside: function(path, cp1, cp2) {
        return (cp2[0] - cp1[0]) * (path[1] - cp1[1]) > (cp2[1] - cp1[1]) * (path[0] - cp1[0]);
    },

    intersectIntersection: function(s, e, cp1, cp2) {
        var p = [],
            dcx = cp1[0] - cp2[0],
            dcy = cp1[1] - cp2[1],
            dpx = s[0] - e[0],
            dpy = s[1] - e[1],
            n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0],
            n2 = s[0] * e[1] - s[1] * e[0],
            n3 = 1 / (dcx * dpy - dcy * dpx);

        p[0] = (n1 * dpx - n2 * dcx) * n3;
        p[1] = (n1 * dpy - n2 * dcy) * n3;
        return p;
    },

    intersect: function(subjectPolygon, clipPolygon) {
        var me = this,
            i = 0,
            ln = clipPolygon.length,
            cp1 = clipPolygon[ln - 1],
            outputList = subjectPolygon,
            cp2, s, e, ln2, inputList, j;
        for (; i < ln; ++i) {
            cp2 = clipPolygon[i];
            inputList = outputList;
            outputList = [];
            s = inputList[inputList.length - 1];
            j = 0;
            ln2 = inputList.length;
            for (; j < ln2; j++) {
                e = inputList[j];
                if (me.intersectInside(e, cp1, cp2)) {
                    if (!me.intersectInside(s, cp1, cp2)) {
                        outputList.push(me.intersectIntersection(s, e, cp1, cp2));
                    }
                    outputList.push(e);
                }
                else if (me.intersectInside(s, cp1, cp2)) {
                    outputList.push(me.intersectIntersection(s, e, cp1, cp2));
                }
                s = e;
            }
            cp1 = cp2;
        }
        return outputList;
    },
    
    bezier : function (a, b, c, d, x) {
        if (x === 0) {
            return a;
        } 
        else if (x === 1) {
            return d;
        }
        var du = 1 - x,
            d3 = du * du * du,
            r = x / du;
        return d3 * (a + r * (3 * b + r * (3 * c + d * r)));
    },
    
    bezierDim : function (a, b, c, d) {
        var points = [], r,
            A, top, C, delta, bottom, s,
            min, max, i;
        
        if (a + 3 * c == d + 3 * b) {   
            r = a - b;
            r /= 2 * (a - b - b + c);
            if ( r < 1 && r > 0) {
                points.push(r);
            }
        } else {
            
            
            A = a - 3 * b + 3 * c - d;
            top = 2 * (a - b - b + c);
            C = a - b;
            delta = top * top - 4 * A * C;
            bottom = A + A;
            if (delta === 0) {
                r = top / bottom;
                if (r < 1 && r > 0) {
                    points.push(r);
                }
            } else if (delta > 0) {
                s = Math.sqrt(delta);
                r = (s + top) / bottom;
                
                if (r < 1 && r > 0) {
                    points.push(r);
                }
                
                r = (top - s) / bottom;
                
                if (r < 1 && r > 0) {
                    points.push(r);
                }
            }
        }
        min = Math.min(a, d);
        max = Math.max(a, d);
        for (i = 0; i < points.length; i++) {
            min = Math.min(min, this.bezier(a, b, c, d, points[i]));
            max = Math.max(max, this.bezier(a, b, c, d, points[i]));
        }
        return [min, max];
    },
    
    curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
        var x = this.bezierDim(p1x, c1x, c2x, p2x),
            y = this.bezierDim(p1y, c1y, c2y, p2y);
        return {
            min: {
                x: x[0],
                y: y[0]
            },
            max: {
                x: x[1],
                y: y[1]
            }
        };
    },

    
    getAnchors: function (prevX, prevY, curX, curY, nextX, nextY, value) {
        value = value || 4;
        var M = Math,
            PI = M.PI,
            halfPI = PI / 2,
            abs = M.abs,
            sin = M.sin,
            cos = M.cos,
            atan = M.atan,
            control1Length, control2Length, control1Angle, control2Angle,
            control1X, control1Y, control2X, control2Y, alpha;

        
        
        control1Length = (curX - prevX) / value;
        control2Length = (nextX - curX) / value;

        
        
        
        
        if ((curY >= prevY && curY >= nextY) || (curY <= prevY && curY <= nextY)) {
            control1Angle = control2Angle = halfPI;
        } else {
            control1Angle = atan((curX - prevX) / abs(curY - prevY));
            if (prevY < curY) {
                control1Angle = PI - control1Angle;
            }
            control2Angle = atan((nextX - curX) / abs(curY - nextY));
            if (nextY < curY) {
                control2Angle = PI - control2Angle;
            }
        }

        
        alpha = halfPI - ((control1Angle + control2Angle) % (PI * 2)) / 2;
        if (alpha > halfPI) {
            alpha -= PI;
        }
        control1Angle += alpha;
        control2Angle += alpha;

        
        control1X = curX - control1Length * sin(control1Angle);
        control1Y = curY + control1Length * cos(control1Angle);
        control2X = curX + control2Length * sin(control2Angle);
        control2Y = curY + control2Length * cos(control2Angle);

        
        
        
        
        if ((curY > prevY && control1Y < prevY) || (curY < prevY && control1Y > prevY)) {
            control1X += abs(prevY - control1Y) * (control1X - curX) / (control1Y - curY);
            control1Y = prevY;
        }
        if ((curY > nextY && control2Y < nextY) || (curY < nextY && control2Y > nextY)) {
            control2X -= abs(nextY - control2Y) * (control2X - curX) / (control2Y - curY);
            control2Y = nextY;
        }
        
        return {
            x1: control1X,
            y1: control1Y,
            x2: control2X,
            y2: control2Y
        };
    },

    
    smooth: function (originalPath, value) {
        var path = this.path2curve(originalPath),
            newp = [path[0]],
            x = path[0][1],
            y = path[0][2],
            j,
            points,
            i = 1,
            ii = path.length,
            beg = 1,
            mx = x,
            my = y,
            pathi,
            pathil,
            pathim,
            pathiml,
            pathip,
            pathipl,
            begl;
        
        for (; i < ii; i++) {
            pathi = path[i];
            pathil = pathi.length;
            pathim = path[i - 1];
            pathiml = pathim.length;
            pathip = path[i + 1];
            pathipl = pathip && pathip.length;
            if (pathi[0] == "M") {
                mx = pathi[1];
                my = pathi[2];
                j = i + 1;
                while (path[j][0] != "C") {
                    j++;
                }
                newp.push(["M", mx, my]);
                beg = newp.length;
                x = mx;
                y = my;
                continue;
            }
            if (pathi[pathil - 2] == mx && pathi[pathil - 1] == my && (!pathip || pathip[0] == "M")) {
                begl = newp[beg].length;
                points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], mx, my, newp[beg][begl - 2], newp[beg][begl - 1], value);
                newp[beg][1] = points.x2;
                newp[beg][2] = points.y2;
            }
            else if (!pathip || pathip[0] == "M") {
                points = {
                    x1: pathi[pathil - 2],
                    y1: pathi[pathil - 1]
                };
            } else {
                points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], pathi[pathil - 2], pathi[pathil - 1], pathip[pathipl - 2], pathip[pathipl - 1], value);
            }
            newp.push(["C", x, y, points.x1, points.y1, pathi[pathil - 2], pathi[pathil - 1]]);
            x = points.x2;
            y = points.y2;
        }
        return newp;
    },

    findDotAtSegment: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
        var t1 = 1 - t;
        return {
            x: Math.pow(t1, 3) * p1x + Math.pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + Math.pow(t, 3) * p2x,
            y: Math.pow(t1, 3) * p1y + Math.pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + Math.pow(t, 3) * p2y
        };
    },

    
    snapEnds: function (from, to, stepsMax, prettyNumbers) {
        if (Ext.isDate(from)) {
            return this.snapEndsByDate(from, to, stepsMax);
        }
        var step = (to - from) / stepsMax,
            level = Math.floor(Math.log(step) / Math.LN10) + 1,
            m = Math.pow(10, level),
            cur,
            floor,
            modulo = Math.round((step % m) * Math.pow(10, 2 - level)),
            interval = [[0, 15], [10, 1], [20, 4], [25, 2], [50, 9], [100, 15]],
            stepCount = 0,
            value,
            weight,
            i,
            topValue,
            topWeight = 1e9,
            ln = interval.length;

        floor = Math.floor(from / m) * m;
        if (from == floor && floor > 0) {
            floor = Math.floor((from - (m/10)) / m) * m;
        }
        
        if (prettyNumbers) {
            for (i = 0; i < ln; i++) {
                value = interval[i][0];
                weight = (value - modulo) < 0 ? 1e6 : (value - modulo) / interval[i][1];
                if (weight < topWeight) {
                    topValue = value;
                    topWeight = weight;
                }
            }
            step = Math.floor(step * Math.pow(10, -level)) * Math.pow(10, level) + topValue * Math.pow(10, level - 2);

            if (from < 0 && to >= 0) {
                cur = 0;
                while (cur > from) {
                    cur -= step;
                    stepCount++;
                }
                from = +cur.toFixed(10);

                cur = 0;
                while (cur < to) {
                    cur += step;
                    stepCount++;
                }
                to = +cur.toFixed(10);
            } else {
                cur = from = floor;
                while (cur < to) {
                    cur += step;
                    stepCount++;
                }
            }
            to = +cur.toFixed(10);
        } else {
            from = floor;
            stepCount = stepsMax;
        }
        
        return {
            from: from,
            to: to,
            power: level,
            step: step,
            steps: stepCount
        };
    },

    
    snapEndsByDate: function (from, to, stepsMax, lockEnds) {
        var selectedStep = false,
            scales       = [
                [Ext.Date.MILLI, [1, 2, 5, 10, 20, 50, 100, 200, 250, 500]],
                [Ext.Date.SECOND, [1, 2, 5, 10, 15, 30]],
                [Ext.Date.MINUTE, [1, 2, 5, 10, 15, 30]],
                [Ext.Date.HOUR, [1, 2, 3, 4, 6, 12]],
                [Ext.Date.DAY, [1, 2, 7, 14]],
                [Ext.Date.MONTH, [1, 2, 3, 6]]
            ],
            sLen         = scales.length,
            stop         = false,
            scale, j, yearDiff, s;

        
        for (s = 0; s < sLen; s++) {
            scale = scales[s];
            if (!stop) {
                for (j = 0; j < scale[1].length; j++) {
                    if (to < Ext.Date.add(from, scale[0], scale[1][j] * stepsMax)) {
                        selectedStep = [scale[0], scale[1][j]];
                        stop         = true;
                        break;
                    }
                }
            }
        }

        if (!selectedStep) {
            yearDiff = this.snapEnds(from.getFullYear(), to.getFullYear() + 1, stepsMax, lockEnds);
            selectedStep = [Date.YEAR, Math.round(yearDiff.step)];
        }
        return this.snapEndsByDateAndStep(from, to, selectedStep, lockEnds);
    },


    

    snapEndsByDateAndStep: function(from, to, step, lockEnds) {
        var fromStat = [from.getFullYear(), from.getMonth(), from.getDate(),
                from.getHours(), from.getMinutes(), from.getSeconds(), from.getMilliseconds()],
            steps, testFrom, testTo, date, year, month, day, fractionalMonth,
            stepUnit = step[0], stepValue = step[1];
        if (lockEnds) {
            testFrom = from;
        } else {
            switch (stepUnit) {
                case Ext.Date.MILLI:
                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
                            fromStat[4], fromStat[5], Math.floor(fromStat[6] / stepValue) * stepValue);
                    break;
                case Ext.Date.SECOND:
                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
                            fromStat[4], Math.floor(fromStat[5] / stepValue) * stepValue, 0);
                    break;
                case Ext.Date.MINUTE:
                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
                            Math.floor(fromStat[4] / stepValue) * stepValue, 0, 0);
                    break;
                case Ext.Date.HOUR:
                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2],
                            Math.floor(fromStat[3] / stepValue) * stepValue, 0, 0, 0);
                    break;
                case Ext.Date.DAY:
                    testFrom = new Date(fromStat[0], fromStat[1],
                            Math.floor((fromStat[2] - 1) / stepValue) * stepValue + 1, 0, 0, 0, 0);
                    break;
                case Ext.Date.MONTH:
                    testFrom = new Date(fromStat[0], Math.floor(fromStat[1] / stepValue) * stepValue, 1, 0, 0, 0, 0);
                    break;
                default: 
                    testFrom = new Date(Math.floor(fromStat[0] / stepValue) * stepValue, 0, 1, 0, 0, 0, 0);
                    break;
            }
        }

        fractionalMonth = ((stepUnit === Ext.Date.MONTH) && (stepValue == 1/2 || stepValue == 1/3 || stepValue == 1/4));
        steps = (fractionalMonth ? [] : 0);

        
        testTo = new Date(testFrom);
        while (testTo < to) {
            if (fractionalMonth) {
                date = new Date(testTo);
                year = date.getFullYear();
                month = date.getMonth();
                day = date.getDate();
                switch(stepValue) {
                    case 1/2:   
                        if (day >= 15) {
                            day = 1;
                            if (++month > 11) {
                                year++;
                            }
                        }
                        else {
                            day = 15;
                        }
                        break;

                    case 1/3:   
                        if (day >= 20) {
                            day = 1;
                            if (++month > 11) {
                                year++;
                            }
                        }
                        else {
                            if (day >= 10) {
                                day = 20
                            }
                            else {
                                day = 10;
                            }
                        }
                        break;

                    case 1/4:   
                        if (day >= 22) {
                            day = 1;
                            if (++month > 11) {
                                year++;
                            }
                        }
                        else {
                            if (day >= 15) {
                                day = 22
                            }
                            else {
                                if (day >= 8) {
                                    day = 15
                                }
                                else {
                                    day = 8;
                                }
                            }
                        }
                        break;
                }
                testTo.setYear(year);
                testTo.setMonth(month);
                testTo.setDate(day);
                steps.push(new Date(testTo));
            }
            else {
                testTo = Ext.Date.add(testTo, stepUnit, stepValue);                
                steps++;
            }
        }

        if (lockEnds) {
            testTo = to;
        }
        
        if (fractionalMonth) {
            return {
                from : +testFrom,
                to : +testTo,
                steps : steps   
            };            
        }
        else {
            return {
                from : +testFrom,
                to : +testTo,
                step : (testTo - testFrom) / steps,
                steps : steps   
            };            
        }
    },

    sorter: function (a, b) {
        return a.offset - b.offset;
    },

    rad: function(degrees) {
        return degrees % 360 * Math.PI / 180;
    },

    degrees: function(radian) {
        return radian * 180 / Math.PI % 360;
    },

    withinBox: function(x, y, bbox) {
        bbox = bbox || {};
        return (x >= bbox.x && x <= (bbox.x + bbox.width) && y >= bbox.y && y <= (bbox.y + bbox.height));
    },

    parseGradient: function(gradient) {
        var me = this,
            type = gradient.type || 'linear',
            angle = gradient.angle || 0,
            radian = me.radian,
            stops = gradient.stops,
            stopsArr = [],
            stop,
            vector,
            max,
            stopObj;

        if (type == 'linear') {
            vector = [0, 0, Math.cos(angle * radian), Math.sin(angle * radian)];
            max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
            vector[2] *= max;
            vector[3] *= max;
            if (vector[2] < 0) {
                vector[0] = -vector[2];
                vector[2] = 0;
            }
            if (vector[3] < 0) {
                vector[1] = -vector[3];
                vector[3] = 0;
            }
        }

        for (stop in stops) {
            if (stops.hasOwnProperty(stop) && me.stopsRE.test(stop)) {
                stopObj = {
                    offset: parseInt(stop, 10),
                    color: Ext.draw.Color.toHex(stops[stop].color) || '#ffffff',
                    opacity: stops[stop].opacity || 1
                };
                stopsArr.push(stopObj);
            }
        }
        
        Ext.Array.sort(stopsArr, me.sorter);
        if (type == 'linear') {
            return {
                id: gradient.id,
                type: type,
                vector: vector,
                stops: stopsArr
            };
        }
        else {
            return {
                id: gradient.id,
                type: type,
                centerX: gradient.centerX,
                centerY: gradient.centerY,
                focalX: gradient.focalX,
                focalY: gradient.focalY,
                radius: gradient.radius,
                vector: vector,
                stops: stopsArr
            };
        }
    }
});


Ext.define('Ext.fx.PropertyHandler', {

    

                                

    statics: {
        defaultHandler: {
            pixelDefaultsRE: /width|height|top$|bottom$|left$|right$/i,
            unitRE: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/,
            scrollRE: /^scroll/i,

            computeDelta: function(from, end, damper, initial, attr) {
                damper = (typeof damper == 'number') ? damper : 1;
                var unitRE = this.unitRE,
                    match = unitRE.exec(from),
                    start, units;
                if (match) {
                    from = match[1];
                    units = match[2];
                    if (!this.scrollRE.test(attr) && !units && this.pixelDefaultsRE.test(attr)) {
                        units = 'px';
                    }
                }
                from = +from || 0;

                match = unitRE.exec(end);
                if (match) {
                    end = match[1];
                    units = match[2] || units;
                }
                end = +end || 0;
                start = (initial != null) ? initial : from;
                return {
                    from: from,
                    delta: (end - start) * damper,
                    units: units
                };
            },

            get: function(from, end, damper, initialFrom, attr) {
                var ln = from.length,
                    out = [],
                    i, initial, res, j, len;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    if (Ext.isArray(from[i][1]) && Ext.isArray(end)) {
                        res = [];
                        j = 0;
                        len = from[i][1].length;
                        for (; j < len; j++) {
                            res.push(this.computeDelta(from[i][1][j], end[j], damper, initial, attr));
                        }
                        out.push([from[i][0], res]);
                    }
                    else {
                        out.push([from[i][0], this.computeDelta(from[i][1], end, damper, initial, attr)]);
                    }
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, res, len, j;
                for (i = 0; i < ln; i++) {
                    val  = values[i][1];
                    if (Ext.isArray(val)) {
                        res = [];
                        j = 0;
                        len = val.length;
                        for (; j < len; j++) {
                            res.push(val[j].from + val[j].delta * easing + (val[j].units || 0));
                        }
                        out.push([values[i][0], res]);
                    } else {
                        out.push([values[i][0], val.from + val.delta * easing + (val.units || 0)]);
                    }
                }
                return out;
            }
        },
        stringHandler: {
            computeDelta: function(from, end, damper, initial, attr) {
                return {
                    from: from,
                    delta: end
                };
            },

            get: function(from, end, damper, initialFrom, attr) {
                var ln = from.length,
                    out = [],
                    i, initial, res, j, len;
                for (i = 0; i < ln; i++) {
                    out.push([from[i][0], this.computeDelta(from[i][1], end, damper, initial, attr)]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, res, len, j;
                for (i = 0; i < ln; i++) {
                    val  = values[i][1];
                    out.push([values[i][0], val.delta]);
                }
                return out;
            }
        },
        color: {
            rgbRE: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
            hexRE: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
            hex3RE: /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i,

            parseColor : function(color, damper) {
                damper = (typeof damper == 'number') ? damper : 1;
                var out    = false,
                    reList = [this.hexRE, this.rgbRE, this.hex3RE],
                    length = reList.length,
                    match, base, re, i;

                for (i = 0; i < length; i++) {
                    re = reList[i];

                    base = (i % 2 === 0) ? 16 : 10;
                    match = re.exec(color);
                    if (match && match.length === 4) {
                        if (i === 2) {
                            match[1] += match[1];
                            match[2] += match[2];
                            match[3] += match[3];
                        }
                        out = {
                            red: parseInt(match[1], base),
                            green: parseInt(match[2], base),
                            blue: parseInt(match[3], base)
                        };
                        break;
                    }
                }

                return out || color;
            },

            computeDelta: function(from, end, damper, initial) {
                from = this.parseColor(from);
                end = this.parseColor(end, damper);
                var start = initial ? initial : from,
                    tfrom = typeof start,
                    tend = typeof end;
                
                if (tfrom == 'string' ||  tfrom == 'undefined'
                  || tend == 'string' || tend == 'undefined') {
                    return end || start;
                }
                return {
                    from:  from,
                    delta: {
                        red: Math.round((end.red - start.red) * damper),
                        green: Math.round((end.green - start.green) * damper),
                        blue: Math.round((end.blue - start.blue) * damper)
                    }
                };
            },

            get: function(start, end, damper, initialFrom) {
                var ln = start.length,
                    out = [],
                    i, initial;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, parsedString, from, delta;
                for (i = 0; i < ln; i++) {
                    val = values[i][1];
                    if (val) {
                        from = val.from;
                        delta = val.delta;
                        
                        val = (typeof val == 'object' && 'red' in val)? 
                                'rgb(' + val.red + ', ' + val.green + ', ' + val.blue + ')' : val;
                        val = (typeof val == 'object' && val.length)? val[0] : val;
                        if (typeof val == 'undefined') {
                            return [];
                        }
                        parsedString = typeof val == 'string'? val :
                            'rgb(' + [
                                  (from.red + Math.round(delta.red * easing)) % 256,
                                  (from.green + Math.round(delta.green * easing)) % 256,
                                  (from.blue + Math.round(delta.blue * easing)) % 256
                              ].join(',') + ')';
                        out.push([
                            values[i][0],
                            parsedString
                        ]);
                    }
                }
                return out;
            }
        },
        object: {
            interpolate: function(prop, damper) {
                damper = (typeof damper == 'number') ? damper : 1;
                var out = {},
                    p;
                for(p in prop) {
                    out[p] = parseFloat(prop[p]) * damper;
                }
                return out;
            },

            computeDelta: function(from, end, damper, initial) {
                from = this.interpolate(from);
                end = this.interpolate(end, damper);
                var start = initial ? initial : from,
                    delta = {},
                    p;

                for(p in end) {
                    delta[p] = end[p] - start[p];
                }
                return {
                    from:  from,
                    delta: delta
                };
            },

            get: function(start, end, damper, initialFrom) {
                var ln = start.length,
                    out = [],
                    i, initial;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    outObject = {},
                    i, from, delta, val, p;
                for (i = 0; i < ln; i++) {
                    val  = values[i][1];
                    from = val.from;
                    delta = val.delta;
                    for (p in from) {
                        outObject[p] = from[p] + delta[p] * easing;
                    }
                    out.push([
                        values[i][0],
                        outObject
                    ]);
                }
                return out;
            }
        },

        path: {
            computeDelta: function(from, end, damper, initial) {
                damper = (typeof damper == 'number') ? damper : 1;
                var start;
                from = +from || 0;
                end = +end || 0;
                start = (initial != null) ? initial : from;
                return {
                    from: from,
                    delta: (end - start) * damper
                };
            },

            forcePath: function(path) {
                if (!Ext.isArray(path) && !Ext.isArray(path[0])) {
                    path = Ext.draw.Draw.parsePathString(path);
                }
                return path;
            },

            get: function(start, end, damper, initialFrom) {
                var endPath = this.forcePath(end),
                    out = [],
                    startLn = start.length,
                    startPathLn, pointsLn, i, deltaPath, initial, j, k, path, startPath;
                for (i = 0; i < startLn; i++) {
                    startPath = this.forcePath(start[i][1]);

                    deltaPath = Ext.draw.Draw.interpolatePaths(startPath, endPath);
                    startPath = deltaPath[0];
                    endPath = deltaPath[1];

                    startPathLn = startPath.length;
                    path = [];
                    for (j = 0; j < startPathLn; j++) {
                        deltaPath = [startPath[j][0]];
                        pointsLn = startPath[j].length;
                        for (k = 1; k < pointsLn; k++) {
                            initial = initialFrom && initialFrom[0][1][j][k].from;
                            deltaPath.push(this.computeDelta(startPath[j][k], endPath[j][k], damper, initial));
                        }
                        path.push(deltaPath);
                    }
                    out.push([start[i][0], path]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, j, k, newPath, calcPath, deltaPath, deltaPathLn, pointsLn;
                for (i = 0; i < ln; i++) {
                    deltaPath = values[i][1];
                    newPath = [];
                    deltaPathLn = deltaPath.length;
                    for (j = 0; j < deltaPathLn; j++) {
                        calcPath = [deltaPath[j][0]];
                        pointsLn = deltaPath[j].length;
                        for (k = 1; k < pointsLn; k++) {
                            calcPath.push(deltaPath[j][k].from + deltaPath[j][k].delta * easing);
                        }
                        newPath.push(calcPath.join(','));
                    }
                    out.push([values[i][0], newPath.join(',')]);
                }
                return out;
            }
        }
        
    }
}, function() {
    
    var props  = [
            'outlineColor',
            'backgroundColor',
            'borderColor',
            'borderTopColor',
            'borderRightColor',
            'borderBottomColor',
            'borderLeftColor',
            'fill',
            'stroke'
        ],
        length = props.length,
        i      = 0,
        prop;

    for (; i<length; i++) {
        prop = props[i];
        this[prop] = this.color;
    }
    
    
    props  = ['cursor'];
    length = props.length;
    i      = 0;

    for (; i<length; i++) {
        prop = props[i];
        this[prop] = this.stringHandler;
    }
});


Ext.define('Ext.fx.Anim', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

                                                                                                                     

    

    
    isAnimation: true,

    

    

    
    duration: 250,

    
    delay: 0,

    
    delayStart: 0,

    
    dynamic: false,

    
    easing: 'ease',

    

    
    damper: 1,

    
    bezierRE: /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,

    
    reverse: false,

    
    running: false,

    
    paused: false,

    
    iterations: 1,

    
    alternate: false,

    
    currentIteration: 0,

    
    startTime: 0,

    

    

    

    
    
    
    frameCount: 0,

    
    constructor: function(config) {
        var me = this,
            curve;
            
        config = config || {};
        
        if (config.keyframes) {
            return new Ext.fx.Animator(config);
        }
        Ext.apply(me, config);
        if (me.from === undefined) {
            me.from = {};
        }
        me.propHandlers = {};
        me.config = config;
        me.target = Ext.fx.Manager.createTarget(me.target);
        me.easingFn = Ext.fx.Easing[me.easing];
        me.target.dynamic = me.dynamic;

        
        if (!me.easingFn) {
            me.easingFn = String(me.easing).match(me.bezierRE);
            if (me.easingFn && me.easingFn.length == 5) {
                curve = me.easingFn;
                me.easingFn = Ext.fx.CubicBezier.cubicBezier(+curve[1], +curve[2], +curve[3], +curve[4]);
            }
        }
        me.id = Ext.id(null, 'ext-anim-');
        me.addEvents(
            
            'beforeanimate',
             
            'afteranimate',
             
            'lastframe'
        );
        me.mixins.observable.constructor.call(me);
        Ext.fx.Manager.addAnim(me);
    },

    
    setAttr: function(attr, value) {
        return Ext.fx.Manager.items.get(this.id).setAttr(this.target, attr, value);
    },

    
    initAttrs: function() {
        var me = this,
            from = me.from,
            to = me.to,
            initialFrom = me.initialFrom || {},
            out = {},
            start, end, propHandler, attr;

        for (attr in to) {
            if (to.hasOwnProperty(attr)) {
                start = me.target.getAttr(attr, from[attr]);
                end = to[attr];
                
                if (!Ext.fx.PropertyHandler[attr]) {
                    if (Ext.isObject(end)) {
                        propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.object;
                    } else {
                        propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.defaultHandler;
                    }
                }
                
                else {
                    propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler[attr];
                }
                out[attr] = propHandler.get(start, end, me.damper, initialFrom[attr], attr);
            }
        }
        me.currentAttrs = out;
    },

    
    start: function(startTime) {
        var me = this,
            delay = me.delay,
            delayStart = me.delayStart,
            delayDelta;
        
        if (delay) {
            if (!delayStart) {
                me.delayStart = startTime;
                return;
            }
            else {
                delayDelta = startTime - delayStart;
                if (delayDelta < delay) {
                    return;
                }
                else {
                    
                    startTime = new Date(delayStart.getTime() + delay);
                }
            }
        }
        if (me.fireEvent('beforeanimate', me) !== false) {
            me.startTime = startTime;
            if (!me.paused && !me.currentAttrs) {
                me.initAttrs();
            }
            me.running = true;
            me.frameCount = 0;
        }
    },

    
    runAnim: function(elapsedTime) {
        var me = this,
            attrs = me.currentAttrs,
            duration = me.duration,
            easingFn = me.easingFn,
            propHandlers = me.propHandlers,
            ret = {},
            easing, values, attr, lastFrame;

        if (elapsedTime >= duration) {
            elapsedTime = duration;
            lastFrame = true;
        }
        if (me.reverse) {
            elapsedTime = duration - elapsedTime;
        }

        for (attr in attrs) {
            if (attrs.hasOwnProperty(attr)) {
                values = attrs[attr];
                easing = lastFrame ? 1 : easingFn(elapsedTime / duration);
                ret[attr] = propHandlers[attr].set(values, easing);
            }
        }
        me.frameCount++;
            
        return ret;
    },

    
    lastFrame: function() {
        var me = this,
            iter = me.iterations,
            iterCount = me.currentIteration;

        iterCount++;
        if (iterCount < iter) {
            if (me.alternate) {
                me.reverse = !me.reverse;
            }
            me.startTime = new Date();
            me.currentIteration = iterCount;
            
            me.paused = false;
        }
        else {
            me.currentIteration = 0;
            me.end();
            me.fireEvent('lastframe', me, me.startTime);
        }
    },

    endWasCalled: 0,

    
    end: function() {
        if (this.endWasCalled++) {
            return;
        }
        var me = this;
        me.startTime = 0;
        me.paused = false;
        me.running = false;
        Ext.fx.Manager.removeAnim(me);
        me.fireEvent('afteranimate', me, me.startTime);
        Ext.callback(me.callback, me.scope, [me, me.startTime]);
    },
    
    isReady: function() {
        return this.paused === false && this.running === false && this.iterations > 0;
    },
    
    isRunning: function() {
        return this.paused === false && this.running === true && this.isAnimator !== true;
    }
});

Ext.enableFx = true;


Ext.define('Ext.util.Animate', {
               
                       
                                   
                          
                     
      
    
    isAnimate: true,

    
    animate: function(animObj) {
        var me = this;
        if (Ext.fx.Manager.hasFxBlock(me.id)) {
            return me;
        }
        Ext.fx.Manager.queueFx(new Ext.fx.Anim(me.anim(animObj)));
        return this;
    },

    
    anim: function(config) {
        if (!Ext.isObject(config)) {
            return (config) ? {} : false;
        }

        var me = this;

        if (config.stopAnimation) {
            me.stopAnimation();
        }

        Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));

        return Ext.apply({
            target: me,
            paused: true
        }, config);
    },

    
    stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'),

    
    stopAnimation: function() {
        Ext.fx.Manager.stopAnimation(this.id);
        return this;
    },

    
    syncFx: function() {
        Ext.fx.Manager.setFxDefaults(this.id, {
            concurrent: true
        });
        return this;
    },

    
    sequenceFx: function() {
        Ext.fx.Manager.setFxDefaults(this.id, {
            concurrent: false
        });
        return this;
    },

    
    hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'),

    
    getActiveAnimation: function() {
        return Ext.fx.Manager.getActiveAnimation(this.id);
    }
}, function(){
    
    Ext.applyIf(Ext.Element.prototype, this.prototype);
    
    Ext.CompositeElementLite.importElementMethods();
});


Ext.define('Ext.util.ElementContainer', {

    childEls: [
        
        
        
        
    ],

    constructor: function () {
        var me = this,
            childEls;

        
        
        if (me.hasOwnProperty('childEls')) {
            childEls = me.childEls;
            delete me.childEls;

            me.addChildEls.apply(me, childEls);
        }
    },

    destroy: function () {
        var me = this,
            childEls = me.getChildEls(),
            child, childName, i, k;

        for (i = childEls.length; i--; ) {
            childName = childEls[i];
            if (typeof childName != 'string') {
                childName = childName.name;
            }

            child = me[childName];
            if (child) {
                me[childName] = null; 
                child.remove();
            }
        }
    },

    
    addChildEls: function () {
        var me = this,
            args = arguments;

        if (me.hasOwnProperty('childEls')) {
            me.childEls.push.apply(me.childEls, args);
        } else {
            me.childEls = me.getChildEls().concat(Array.prototype.slice.call(args));
        }
        
        me.prune(me.childEls, false);
    },

    
    applyChildEls: function(el, id) {
        var me = this,
            childEls = me.getChildEls(),
            baseId, childName, i, selector, value;

        baseId = (id || me.id) + '-';
        for (i = childEls.length; i--; ) {
            childName = childEls[i];

            if (typeof childName == 'string') {
                
                
                value = el.getById(baseId + childName);
            } else {
                if ((selector = childName.select)) {
                    value = Ext.select(selector, true, el.dom); 
                } else if ((selector = childName.selectNode)) {
                    value = Ext.get(Ext.DomQuery.selectNode(selector, el.dom));
                } else {
                    
                    value = el.getById(childName.id || (baseId + childName.itemId));
                }

                childName = childName.name;
            }

            me[childName] = value;
        }
    },

    getChildEls: function () {
        var me = this,
            self;

        
        if (me.hasOwnProperty('childEls')) {
            return me.childEls;
        }

        
        
        self = me.self;
        return self.$childEls || me.getClassChildEls(self);
    },

    getClassChildEls: function (cls) {
        var me = this,
            result = cls.$childEls,
            childEls, i, length, forked, mixin, mixins, name, parts, proto, supr, superMixins;

        if (!result) {
            
            
            

            supr = cls.superclass;
            if (supr) {
                supr = supr.self;
                parts = [supr.$childEls || me.getClassChildEls(supr)]; 
                superMixins = supr.prototype.mixins || {};
            } else {
                parts = [];
                superMixins = {};
            }

            proto = cls.prototype;
            mixins = proto.mixins; 
            for (name in mixins) {
                if (mixins.hasOwnProperty(name) && !superMixins.hasOwnProperty(name)) {
                    mixin = mixins[name].self;
                    parts.push(mixin.$childEls || me.getClassChildEls(mixin));
                }
            }

            parts.push(proto.hasOwnProperty('childEls') && proto.childEls);

            for (i = 0, length = parts.length; i < length; ++i) {
                childEls = parts[i];
                if (childEls && childEls.length) {
                    if (!result) {
                        result = childEls;
                    } else {
                        if (!forked) {
                            forked = true;
                            result = result.slice(0);
                        }
                        result.push.apply(result, childEls);
                    }
                }
            }

            cls.$childEls = result = (result ? me.prune(result, !forked) : []);
        }

        return result;
    },

    prune: function (childEls, shared) {
        var index = childEls.length,
            map = {},
            name;

        while (index--) {
            name = childEls[index];
            if (typeof name != 'string') {
                name = name.name;
            }

            if (!map[name]) {
                map[name] = 1;
            } else {
                if (shared) {
                    shared = false;
                    childEls = childEls.slice(0);
                }
                Ext.Array.erase(childEls, index, 1);
            }
        }

        return childEls;
    },

    
    removeChildEls: function (testFn) {
        var me = this,
            old = me.getChildEls(),
            keepers = (me.childEls = []),
            n, i, cel;

        for (i = 0, n = old.length; i < n; ++i) {
            cel = old[i];
            if (!testFn(cel)) {
                keepers.push(cel);
            }
        }
    }
});


Ext.define('Ext.util.Renderable', {
               
                         
      

    frameCls: Ext.baseCSSPrefix + 'frame',

    frameIdRegex: /[\-]frame\d+[TMB][LCR]$/,
    
    frameElNames: ['TL','TC','TR','ML','MC','MR','BL','BC','BR'],

    frameTpl: [
        '{%this.renderDockedItems(out,values,0);%}',
        '<tpl if="top">',
            '<tpl if="left"><div id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>{frameElCls}" role="presentation"></tpl>',
                '<tpl if="right"><div id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>{frameElCls}" role="presentation"></tpl>',
                    '<div id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>{frameElCls}" role="presentation"></div>',
                '<tpl if="right"></div></tpl>',
            '<tpl if="left"></div></tpl>',
        '</tpl>',
        '<tpl if="left"><div id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>{frameElCls}" role="presentation"></tpl>',
            '<tpl if="right"><div id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>{frameElCls}" role="presentation"></tpl>',
                '<div id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>{frameElCls}" role="presentation">',
                    '{%this.applyRenderTpl(out, values)%}',
                '</div>',
            '<tpl if="right"></div></tpl>',
        '<tpl if="left"></div></tpl>',
        '<tpl if="bottom">',
            '<tpl if="left"><div id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>{frameElCls}" role="presentation"></tpl>',
                '<tpl if="right"><div id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>{frameElCls}" role="presentation"></tpl>',
                    '<div id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>{frameElCls}" role="presentation"></div>',
                '<tpl if="right"></div></tpl>',
            '<tpl if="left"></div></tpl>',
        '</tpl>',
        '{%this.renderDockedItems(out,values,1);%}'
    ],

    frameTableTpl: [
        '{%this.renderDockedItems(out,values,0);%}',
        '<table class="', Ext.plainTableCls, '" cellpadding="0"><tbody>',
            '<tpl if="top">',
                '<tr>',
                    '<tpl if="left"><td id="{fgid}TL" class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl>{frameElCls}" role="presentation"></td></tpl>',
                    '<td id="{fgid}TC" class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl>{frameElCls}" role="presentation"></td>',
                    '<tpl if="right"><td id="{fgid}TR" class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl>{frameElCls}" role="presentation"></td></tpl>',
                '</tr>',
            '</tpl>',
            '<tr>',
                '<tpl if="left"><td id="{fgid}ML" class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl>{frameElCls}" role="presentation"></td></tpl>',
                '<td id="{fgid}MC" class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl>{frameElCls}" role="presentation">',
                    '{%this.applyRenderTpl(out, values)%}',
                '</td>',
                '<tpl if="right"><td id="{fgid}MR" class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl>{frameElCls}" role="presentation"></td></tpl>',
            '</tr>',
            '<tpl if="bottom">',
                '<tr>',
                    '<tpl if="left"><td id="{fgid}BL" class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl>{frameElCls}" role="presentation"></td></tpl>',
                    '<td id="{fgid}BC" class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl>{frameElCls}" role="presentation"></td>',
                    '<tpl if="right"><td id="{fgid}BR" class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl>{frameElCls}" role="presentation"></td></tpl>',
                '</tr>',
            '</tpl>',
        '</tbody></table>',
        '{%this.renderDockedItems(out,values,1);%}'
    ],

    
    afterRender : function() {
        var me = this,
            data = {},
            protoEl = me.protoEl,
            target = me.el,
            item, pre, hide, contentEl;

        me.finishRenderChildren();
        
        
        
        if (me.contentEl) {
            pre = Ext.baseCSSPrefix;
            hide = pre + 'hide-';
            contentEl = Ext.get(me.contentEl);
            contentEl.removeCls([pre+'hidden', hide+'display', hide+'offsets', hide+'nosize']);
            me.getContentTarget().appendChild(contentEl.dom);
        }

        protoEl.writeTo(data);
        
        
        
        
        item = data.removed;
        if (item) {
            target.removeCls(item);
        }
        
        item = data.cls;
        if (item.length) {
            target.addCls(item);
        }
        
        item = data.style;
        if (data.style) {
            target.setStyle(item);
        }
        
        me.protoEl = null;

        
        if (!me.ownerCt) {
            me.updateLayout();
        }
    },

    afterFirstLayout : function(width, height) {
        var me = this,
            x = me.x,
            y = me.y,
            hasX,
            hasY,
            pos, xy;

        
        
        if (!me.ownerLayout) {
            hasX = Ext.isDefined(x);
            hasY = Ext.isDefined(y);
        }

        
        
        if (me.floating && (!hasX || !hasY)) {
            if (me.floatParent) {
                pos = me.floatParent.getTargetEl().getViewRegion();
                xy = me.el.getAlignToXY(me.floatParent.getTargetEl(), 'c-c');
                pos.x = xy[0] - pos.x;
                pos.y = xy[1] - pos.y;
            } else {
                xy = me.el.getAlignToXY(me.container, 'c-c');
                pos = me.container.translateXY(xy[0], xy[1]);
            }
            x = hasX ? x : pos.x;
            y = hasY ? y : pos.y;
            hasX = hasY = true;
        }

        if (hasX || hasY) {
            me.setPosition(x, y);
        }
        me.onBoxReady(width, height);
    },

    
    applyRenderSelectors: function() {
        var me = this,
            selectors = me.renderSelectors,
            el = me.el,
            dom = el.dom,
            selector;

        me.applyChildEls(el);

        
        
        
        if (selectors) {
            for (selector in selectors) {
                if (selectors.hasOwnProperty(selector) && selectors[selector]) {
                    me[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], dom));
                }
            }
        }
    },

    beforeRender: function () {
        var me = this,
            target = me.getTargetEl(),
            overflowEl = me.getOverflowEl(),
            layout = me.getComponentLayout(),
            
            overflowStyle = me.getOverflowStyle();

        
        me.frame = me.frame || me.alwaysFramed;

        if (!layout.initialized) {
            layout.initLayout();
        }

        
        
        if (overflowEl) {
            overflowEl.setStyle(overflowStyle);
            me.overflowStyleSet = true;
        }

        me.setUI(me.ui);

        if (me.disabled) {
            
            me.disable(true);
        }
    },

    
    doApplyRenderTpl: function(out, values) {
        
        

        var me = values.$comp,
            tpl;

        
        if (!me.rendered) {
            tpl = me.initRenderTpl();
            tpl.applyOut(values.renderData, out);
        }
    },

    
    doAutoRender: function() {
        var me = this;
        if (!me.rendered) {
            if (me.floating) {
                me.render(document.body);
            } else {
                me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender);
            }
        }
    },

    doRenderContent: function (out, renderData) {
        
        

        var me = renderData.$comp;

        if (me.html) {
            Ext.DomHelper.generateMarkup(me.html, out);
            delete me.html;
        }

        if (me.tpl) {
            
            if (!me.tpl.isTemplate) {
                me.tpl = new Ext.XTemplate(me.tpl);
            }

            if (me.data) {
                
                me.tpl.applyOut(me.data, out);
                delete me.data;
            }
        }
    },

    doRenderFramingDockedItems: function (out, renderData, after) {
        
        

        var me = renderData.$comp;

        
        
        if (!me.rendered && me.doRenderDockedItems) {
            
            
            renderData.renderData.$skipDockedItems = true;

            
            
            me.doRenderDockedItems.call(this, out, renderData, after);
        }
    },

    
    finishRender: function(containerIdx) {
        var me = this,
            tpl, data, el;

        
        
        
        
        
        
        

        if (!me.el || me.$pid) {
            if (me.container) {
                el = me.container.getById(me.id, true);
            } else {
                el = Ext.getDom(me.id);
            }

            if (!me.el) {
                
                me.wrapPrimaryEl(el);
            } else {
                
                
                delete me.$pid;

                if (!me.el.dom) {
                    
                    me.wrapPrimaryEl(me.el);
                }
                el.parentNode.insertBefore(me.el.dom, el);
                Ext.removeNode(el); 
                
            }
        } else if (!me.rendering) {
            
            
            
            tpl = me.initRenderTpl();
            if (tpl) {
                data = me.initRenderData();
                tpl.insertFirst(me.getTargetEl(), data);
            }
        }
        

        if (!me.container) {
            
            me.container = Ext.get(me.el.dom.parentNode);
        }

        if (me.ctCls) {
            me.container.addCls(me.ctCls);
        }

        
        me.onRender(me.container, containerIdx);

        
        if (!me.overflowStyleSet) {
            me.getOverflowEl().setStyle(me.getOverflowStyle());
        }

        
        
        me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]);

        if (me.overCls) {
            me.el.hover(me.addOverCls, me.removeOverCls, me);
        }

        if (me.hasListeners.render) {
            me.fireEvent('render', me);
        }

        me.afterRender(); 
        if (me.hasListeners.afterrender) {
            me.fireEvent('afterrender', me);
        }
        me.initEvents();

        if (me.hidden) {
            
            
            
            me.el.hide();
        }
    },

    finishRenderChildren: function () {
        var layout = this.getComponentLayout();

        layout.finishRender();
    },

    getElConfig : function() {
        var me = this,
            autoEl = me.autoEl,
            frameInfo = me.getFrameInfo(),
            config = {
                tag: 'div',
                tpl: frameInfo ? me.initFramingTpl(frameInfo.table) : me.initRenderTpl()
            },
            protoEl = me.protoEl,
            i, frameElNames, len, suffix, frameGenId, frameData;

        me.initStyles(protoEl);
        protoEl.writeTo(config);
        protoEl.flush();

        if (Ext.isString(autoEl)) {
            config.tag = autoEl;
        } else {
            Ext.apply(config, autoEl); 
        }

        
        config.id = me.id;

        if (config.tpl) {
            
            if (frameInfo) {
                frameElNames = me.frameElNames;
                len = frameElNames.length;

                config.tplData = frameData = me.getFrameRenderData();
                frameData.renderData = me.initRenderData();
                frameGenId = frameData.fgid;

                
                for (i = 0; i < len; i++) {
                    suffix = frameElNames[i];
                    me.addChildEls({ name: 'frame' + suffix, id: frameGenId + suffix });
                }

                
                me.addChildEls({
                    name: 'frameBody',
                    id: frameGenId + 'MC'
                });
            } else {
                config.tplData = me.initRenderData();
            }
        }

        return config;
    },

    
    
    initFramingTpl: function(table) {
        var tpl = this.getFrameTpl(table);

        if (tpl && !tpl.applyRenderTpl) {
            this.setupFramingTpl(tpl);
        }

        return tpl;
    },

    
    setupFramingTpl: function(frameTpl) {
        frameTpl.applyRenderTpl = this.doApplyRenderTpl;
        frameTpl.renderDockedItems = this.doRenderFramingDockedItems;
    },

    
    getInsertPosition: function(position) {
        
        if (position !== undefined) {
            if (Ext.isNumber(position)) {
                position = this.container.dom.childNodes[position];
            }
            else {
                position = Ext.getDom(position);
            }
        }

        return position;
    },

    getRenderTree: function() {
        var me = this;

        if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
            me.beforeRender();

            
            
            me.rendering = true;

            if (me.el) {
                
                
                
                return {
                    tag: 'div',
                    id: (me.$pid = Ext.id())
                };
            }

            return me.getElConfig();
        }

        return null;
    },

    initContainer: function(container) {
        var me = this;

        
        
        
        if (!container && me.el) {
            container = me.el.dom.parentNode;
            me.allowDomMove = false;
        }
        me.container = container.dom ? container : Ext.get(container);

        return me.container;
    },

    
    initRenderData: function() {
        var me = this;

        return Ext.apply({
            $comp: me,
            id: me.id,
            ui: me.ui,
            uiCls: me.uiCls,
            baseCls: me.baseCls,
            componentCls: me.componentCls,
            frame: me.frame,
            childElCls: '' 
        }, me.renderData);
    },

    
    initRenderTpl: function() {
        var tpl = this.getTpl('renderTpl');

        if (tpl && !tpl.renderContent) {
            this.setupRenderTpl(tpl);
        }

        return tpl;
    },

    
    onRender: function(parentNode, containerIdx) {
        var me = this,
            x = me.x,
            y = me.y,
            lastBox = null,
            width, height,
            el = me.el;

        me.applyRenderSelectors();

        
        
        me.rendering = null;

        me.rendered = true;

        
        if (x != null) {
            lastBox = {x:x};
        }
        if (y != null) {
            (lastBox = lastBox || {}).y = y;
        }
        
        
        
        if (!me.getFrameInfo() && Ext.isBorderBox) {
            width = me.width;
            height = me.height;

            if (typeof width === 'number') {
                lastBox = lastBox || {};
                lastBox.width = width;
            }
            if (typeof height === 'number') {
                lastBox = lastBox || {};
                lastBox.height = height;
            }
        }

        me.lastBox = el.lastBox = lastBox;
    },

    
    render: function(container, position) {
        var me = this,
            el = me.el && (me.el = Ext.get(me.el)), 
            vetoed,
            tree,
            nextSibling;

        Ext.suspendLayouts();

        container = me.initContainer(container);

        nextSibling = me.getInsertPosition(position);

        if (!el) {
            tree = me.getRenderTree();
            if (me.ownerLayout && me.ownerLayout.transformItemRenderTree) {
                tree = me.ownerLayout.transformItemRenderTree(tree);
            }

            
            if (tree) {
                if (nextSibling) {
                    el = Ext.DomHelper.insertBefore(nextSibling, tree);
                } else {
                    el = Ext.DomHelper.append(container, tree);
                }

                me.wrapPrimaryEl(el);
            }
        } else {
            if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
                me.beforeRender();
                
                me.initStyles(el);
                if (me.allowDomMove !== false) {
                    if (nextSibling) {
                        container.dom.insertBefore(el.dom, nextSibling);
                    } else {
                        container.dom.appendChild(el.dom);
                    }
                }
            } else {
                vetoed = true;
            }
        }

        if (el && !vetoed) {
            me.finishRender(position);
        }

        Ext.resumeLayouts(!me.hidden && !container.isDetachedBody);
    },

    
    ensureAttachedToBody: function (runLayout) {
        var comp = this,
            body;

        while (comp.ownerCt) {
            comp = comp.ownerCt;
        }

        if (comp.container.isDetachedBody) {
            comp.container = body = Ext.getBody();
            body.appendChild(comp.el.dom);
            if (runLayout) {
                comp.updateLayout();
            }
            if (typeof comp.x == 'number' || typeof comp.y == 'number') {
                comp.setPosition(comp.x, comp.y);
            }
        }
    },

    setupRenderTpl: function (renderTpl) {
        renderTpl.renderBody = renderTpl.renderContent = this.doRenderContent;
    },

    wrapPrimaryEl: function (dom) {
        this.el = Ext.get(dom, true);
    },

    
    initFrame : function() {
        if (Ext.supports.CSS3BorderRadius || !this.frame) {
            return;
        }

        var me = this,
            frameInfo = me.getFrameInfo(),
            frameTpl, frameGenId,
            frameElNames = me.frameElNames,
            len = frameElNames.length,
            i, frameData, suffix;

        if (frameInfo) {
            frameTpl = me.getFrameTpl(frameInfo.table);
            frameData = me.getFrameRenderData();
            frameGenId = frameData.fgid;

            
            
            frameTpl.insertFirst(me.el, frameData);

            
            
            me.frameBody = me.el.down('.' + me.frameCls + '-mc');

            
            me.removeChildEls(function (c) {
                return c.id && me.frameIdRegex.test(c.id);
            });

            
            for (i = 0; i < len; i++) {
                suffix = frameElNames[i];
                me['frame' + suffix] = me.el.getById(frameGenId + suffix);
            }
        }
    },

    getFrameRenderData: function () {
        var me = this,
            
            frameInfo = me.frameSize,
            frameGenId = (me.frameGenId || 0) + 1;

        
        
        me.frameGenId = frameGenId;

        return {
            $comp:      me,
            fgid:       me.id + '-frame' + frameGenId,
            ui:         me.ui,
            uiCls:      me.uiCls,
            frameCls:   me.frameCls,
            baseCls:    me.baseCls,
            top:        !!frameInfo.top,
            left:       !!frameInfo.left,
            right:      !!frameInfo.right,
            bottom:     !!frameInfo.bottom,
            
            
            frameElCls: ''
        };
    },

    updateFrame: function() {
        if (Ext.supports.CSS3BorderRadius || !this.frame) {
            return;
        }

        var me = this,
            wasTable = me.frameSize && me.frameSize.table,
            oldFrameTL = me.frameTL,
            oldFrameBL = me.frameBL,
            oldFrameML = me.frameML,
            oldFrameMC = me.frameMC,
            newMCClassName;

        me.initFrame();

        if (oldFrameMC) {
            if (me.frame) {

                
                newMCClassName = me.frameMC.dom.className;

                
                
                oldFrameMC.insertAfter(me.frameMC);
                me.frameMC.remove();

                
                me.frameBody = me.frameMC = oldFrameMC;

                
                oldFrameMC.dom.className = newMCClassName;

                
                if (wasTable) {
                    me.el.query('> table')[1].remove();
                }
                else {
                    if (oldFrameTL) {
                        oldFrameTL.remove();
                    }
                    if (oldFrameBL) {
                        oldFrameBL.remove();
                    }
                    if (oldFrameML) {
                        oldFrameML.remove();
                    }
                }
            }
        }
        else if (me.frame) {
            me.applyRenderSelectors();
        }
    },

    
    getFrameInfo: function() {
        
        if (Ext.supports.CSS3BorderRadius || !this.frame) {
            return false;
        }

        var me = this,
            frameInfoCache = me.frameInfoCache,
            cls = me.getFramingInfoCls() + '-frameInfo',
            frameInfo = frameInfoCache[cls],
            max = Math.max,
            styleEl, match, info, frameTop, frameRight, frameBottom, frameLeft,
            borderWidthT, borderWidthR, borderWidthB, borderWidthL,
            paddingT, paddingR, paddingB, paddingL,
            borderRadiusTL, borderRadiusTR, borderRadiusBR, borderRadiusBL;

        if (frameInfo == null) {
            
            styleEl = Ext.fly(me.getStyleProxy(cls), 'frame-style-el');
            info = styleEl.getStyle('font-family');

            if (info) {
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                info = info.split('-');
                
                borderRadiusTL = parseInt(info[1], 10);
                borderRadiusTR = parseInt(info[2], 10);
                borderRadiusBR = parseInt(info[3], 10);
                borderRadiusBL = parseInt(info[4], 10);
                borderWidthT   = parseInt(info[5], 10);
                borderWidthR   = parseInt(info[6], 10);
                borderWidthB   = parseInt(info[7], 10);
                borderWidthL   = parseInt(info[8], 10);
                paddingT       = parseInt(info[9], 10);
                paddingR       = parseInt(info[10], 10);
                paddingB       = parseInt(info[11], 10);
                paddingL       = parseInt(info[12], 10);

                
                
                frameTop    = max(borderWidthT, max(borderRadiusTL, borderRadiusTR));
                frameRight  = max(borderWidthR, max(borderRadiusTR, borderRadiusBR));
                frameBottom = max(borderWidthB, max(borderRadiusBL, borderRadiusBR));
                frameLeft   = max(borderWidthL, max(borderRadiusTL, borderRadiusBL));

                frameInfo = {
                    table: info[0].charAt(0) === 't',
                    vertical: info[0].charAt(1) === 'v',

                    top: frameTop,
                    right: frameRight,
                    bottom: frameBottom,
                    left: frameLeft,

                    width: frameLeft + frameRight,
                    height: frameTop + frameBottom,

                    maxWidth: max(frameTop, frameRight, frameBottom, frameLeft),

                    border: {
                        top:    borderWidthT,
                        right:  borderWidthR,
                        bottom: borderWidthB,
                        left:   borderWidthL,
                        width:  borderWidthL + borderWidthR,
                        height: borderWidthT + borderWidthB
                    },
                    padding: {
                        top:    paddingT,
                        right:  paddingR,
                        bottom: paddingB,
                        left:   paddingL,
                        width:  paddingL + paddingR,
                        height: paddingT + paddingB
                    },
                    radius: {
                        tl: borderRadiusTL,
                        tr: borderRadiusTR,
                        br: borderRadiusBR,
                        bl: borderRadiusBL
                    }
                };
            } else {
                frameInfo = false;
            }


            frameInfoCache[cls] = frameInfo;
        }

        me.frame = !!frameInfo;
        me.frameSize = frameInfo;

        return frameInfo;
    },
    
    getFramingInfoCls: function(){
        return this.baseCls + '-' + this.ui;
    },

    
    getStyleProxy: function(cls) {
        var result = this.styleProxyEl || (Ext.AbstractComponent.prototype.styleProxyEl = Ext.getBody().createChild({
                style: {
                    position: 'absolute',
                    top: '-10000px'
                }
            }, null, true));

        result.className = cls;
        return result;
    },

    
    getFrameTpl : function(table) {
        return this.getTpl(table ? 'frameTableTpl' : 'frameTpl');
    },

    
    frameInfoCache: {}
});


Ext.define('Ext.state.Provider', {
    mixins: {
        observable:  Ext.util.Observable 
    },
    
    
    prefix: 'ext-',
    
    constructor : function(config){
        config = config || {};
        var me = this;
        Ext.apply(me, config);
        
        me.addEvents("statechange");
        me.state = {};
        me.mixins.observable.constructor.call(me);
    },
    
    
    get : function(name, defaultValue){
        return typeof this.state[name] == "undefined" ?
            defaultValue : this.state[name];
    },

    
    clear : function(name){
        var me = this;
        delete me.state[name];
        me.fireEvent("statechange", me, name, null);
    },

    
    set : function(name, value){
        var me = this;
        me.state[name] = value;
        me.fireEvent("statechange", me, name, value);
    },

    
    decodeValue : function(value){

        
        
        
        
        
        
        

        var me = this,
            re = /^(a|n|d|b|s|o|e)\:(.*)$/,
            matches = re.exec(unescape(value)),
            all,
            type,
            keyValue,
            values,
            vLen,
            v;
            
        if(!matches || !matches[1]){
            return; 
        }
        
        type = matches[1];
        value = matches[2];
        switch (type) {
            case 'e':
                return null;
            case 'n':
                return parseFloat(value);
            case 'd':
                return new Date(Date.parse(value));
            case 'b':
                return (value == '1');
            case 'a':
                all = [];
                if(value != ''){
                    values = value.split('^');
                    vLen   = values.length;

                    for (v = 0; v < vLen; v++) {
                        value = values[v];
                        all.push(me.decodeValue(value));
                    }
                }
                return all;
           case 'o':
                all = {};
                if(value != ''){
                    values = value.split('^');
                    vLen   = values.length;

                    for (v = 0; v < vLen; v++) {
                        value = values[v];
                        keyValue         = value.split('=');
                        all[keyValue[0]] = me.decodeValue(keyValue[1]);
                    }
                }
                return all;
           default:
                return value;
        }
    },

    
    encodeValue : function(value){
        var flat = '',
            i = 0,
            enc,
            len,
            key;
            
        if (value == null) {
            return 'e:1';    
        } else if(typeof value == 'number') {
            enc = 'n:' + value;
        } else if(typeof value == 'boolean') {
            enc = 'b:' + (value ? '1' : '0');
        } else if(Ext.isDate(value)) {
            enc = 'd:' + value.toGMTString();
        } else if(Ext.isArray(value)) {
            for (len = value.length; i < len; i++) {
                flat += this.encodeValue(value[i]);
                if (i != len - 1) {
                    flat += '^';
                }
            }
            enc = 'a:' + flat;
        } else if (typeof value == 'object') {
            for (key in value) {
                if (typeof value[key] != 'function' && value[key] !== undefined) {
                    flat += key + '=' + this.encodeValue(value[key]) + '^';
                }
            }
            enc = 'o:' + flat.substring(0, flat.length-1);
        } else {
            enc = 's:' + value;
        }
        return escape(enc);
    }
});


Ext.define('Ext.state.Manager', {
    singleton: true,
                                     
    constructor: function() {
        this.provider = new Ext.state.Provider();
    },
    
    
    
    setProvider : function(stateProvider){
        this.provider = stateProvider;
    },

    
    get : function(key, defaultValue){
        return this.provider.get(key, defaultValue);
    },

    
     set : function(key, value){
        this.provider.set(key, value);
    },

    
    clear : function(key){
        this.provider.clear(key);
    },

    
    getProvider : function(){
        return this.provider;
    }
});


Ext.define('Ext.state.Stateful', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

                                    

    

    
    stateful: false,

    

    

    
    saveDelay: 100,

    constructor: function(config) {
        var me = this;

        config = config || {};
        if (config.stateful !== undefined) {
            me.stateful = config.stateful;
        }
        if (config.saveDelay !== undefined) {
            me.saveDelay = config.saveDelay;
        }
        me.stateId = me.stateId || config.stateId;

        if (!me.stateEvents) {
            me.stateEvents = [];
        }
        if (config.stateEvents) {
            me.stateEvents.concat(config.stateEvents);
        }
        this.addEvents(
            
            'beforestaterestore',

            
            'staterestore',

            
            'beforestatesave',

            
            'statesave'
        );
        me.mixins.observable.constructor.call(me);

        if (me.stateful !== false) {
            me.addStateEvents(me.stateEvents);
            me.initState();
        }
    },

    
    addStateEvents: function (events) {
        var me = this,
            i, event, stateEventsByName;

        if (me.stateful && me.getStateId()) {
            if (typeof events == 'string') {
                events = Array.prototype.slice.call(arguments, 0);
            }

            stateEventsByName = me.stateEventsByName || (me.stateEventsByName = {});

            for (i = events.length; i--; ) {
                event = events[i];

                if (!stateEventsByName[event]) {
                    stateEventsByName[event] = 1;
                    me.on(event, me.onStateChange, me);
                }
            }
        }
    },

    
    onStateChange: function(){
        var me = this,
            delay = me.saveDelay,
            statics, runner;

        if (!me.stateful) {
            return;
        }

        if (delay) {
            if (!me.stateTask) {
                statics = Ext.state.Stateful;
                runner = statics.runner || (statics.runner = new Ext.util.TaskRunner());

                me.stateTask = runner.newTask({
                    run: me.saveState,
                    scope: me,
                    interval: delay,
                    repeat: 1
                });
            }

            me.stateTask.start();
        } else {
            me.saveState();
        }
    },

    
    saveState: function() {
        var me = this,
            id = me.stateful && me.getStateId(),
            hasListeners = me.hasListeners,
            state;

        if (id) {
            state = me.getState() || {};    
            if (!hasListeners.beforestatesave || me.fireEvent('beforestatesave', me, state) !== false) {
                Ext.state.Manager.set(id, state);
                if (hasListeners.statesave) {
                    me.fireEvent('statesave', me, state);
                }
            }
        }
    },

    
    getState: function(){
        return null;
    },

    
    applyState: function(state) {
        if (state) {
            Ext.apply(this, state);
        }
    },

    
    getStateId: function() {
        var me = this;
        return me.stateId || (me.autoGenId ? null : me.id);
    },

    
    initState: function(){
        var me = this,
            id = me.stateful && me.getStateId(),
            hasListeners = me.hasListeners,
            state;

        if (id) {
            state = Ext.state.Manager.get(id);
            if (state) {
                state = Ext.apply({}, state);
                if (!hasListeners.beforestaterestore || me.fireEvent('beforestaterestore', me, state) !== false) {
                    me.applyState(state);
                    if (hasListeners.staterestore) {
                        me.fireEvent('staterestore', me, state);
                    }
                }
            }
        }
    },

    
    savePropToState: function (propName, state, stateName) {
        var me = this,
            value = me[propName],
            config = me.initialConfig;

        if (me.hasOwnProperty(propName)) {
            if (!config || config[propName] !== value) {
                if (state) {
                    state[stateName || propName] = value;
                }
                return true;
            }
        }
        return false;
    },

    
    savePropsToState: function (propNames, state) {
        var me = this,
            i, n;

        if (typeof propNames == 'string') {
            me.savePropToState(propNames, state);
        } else {
            for (i = 0, n = propNames.length; i < n; ++i) {
                me.savePropToState(propNames[i], state);
            }
        }

        return state;
    },

    
    destroy: function(){
        var me = this,
            task = me.stateTask;

        if (task) {
            task.destroy();
            me.stateTask = null;
        }

        me.clearListeners();
    }
});



Ext.define('Ext.AbstractComponent', {

    
               
                             
                               
                                
                                   
                           
      

    mixins: {
        positionable:  Ext.util.Positionable ,
        observable:  Ext.util.Observable ,
        animate:  Ext.util.Animate ,
        elementCt:  Ext.util.ElementContainer ,
        renderable:  Ext.util.Renderable ,
        state:  Ext.state.Stateful 
    },

    
    
           
                            
                      
                        
                        
                              
                           
                             
                            
                                    
                       
                           
      

    statics: {
        AUTO_ID: 1000,

        pendingLayouts: null,

        layoutSuspendCount: 0,

        
        cancelLayout: function(comp, isDestroying) {
            var context = this.runningLayoutContext || this.pendingLayouts;

            if (context) {
                context.cancelComponent(comp, false, isDestroying);
            }
        },

        
        flushLayouts: function () {
            var me = this,
                context = me.pendingLayouts;

            if (context && context.invalidQueue.length) {
                me.pendingLayouts = null;
                me.runningLayoutContext = context;

                Ext.override(context, {
                    runComplete: function () {
                        
                        
                        
                        me.runningLayoutContext = null;
                         
                        var result = this.callParent(); 
                        if (Ext.globalEvents.hasListeners.afterlayout) {                            
                            Ext.globalEvents.fireEvent('afterlayout');
                        }
                        return result;
                    }
                });

                context.run();
            }
        },

        
        resumeLayouts: function (flush) {
            if (this.layoutSuspendCount && ! --this.layoutSuspendCount) {
                if (flush) {
                    this.flushLayouts();
                }
                if (Ext.globalEvents.hasListeners.resumelayouts) {
                    Ext.globalEvents.fireEvent('resumelayouts');
                }
            }
        },

        
        suspendLayouts: function () {
            ++this.layoutSuspendCount;
        },

        
        updateLayout: function (comp, defer) {
            var me = this,
                running = me.runningLayoutContext,
                pending;

            if (running) {
                running.queueInvalidate(comp);
            } else {
                pending = me.pendingLayouts || (me.pendingLayouts = new Ext.layout.Context());
                pending.queueInvalidate(comp);

                if (!defer && !me.layoutSuspendCount && !comp.isLayoutSuspended()) {
                    me.flushLayouts();
                }
            }
        }
    },

    

    
    isComponent: true,

    
    getAutoId: function() {
        this.autoGenId = true;
        return ++Ext.AbstractComponent.AUTO_ID;
    },

    deferLayouts: false,

    

     
    autoGenId: false,

    

    

    

    
    renderTpl: '{%this.renderContent(out,values)%}',

    

    

    

    

    

    
    frameSize: null,

    

    

    

    

    
    tplWriteMode: 'overwrite',

    
    baseCls: Ext.baseCSSPrefix + 'component',

    

    

    

    
    disabledCls: Ext.baseCSSPrefix + 'item-disabled',

    
    ui: 'default',

    
    uiCls: [],

    

    

    

    

    

    

    
    hidden: false,

    
    disabled: false,

    

    
    draggable: false,

    
    floating: false,

    
    hideMode: 'display',

    

    

    
    
    
    

    

    

    
    autoShow: false,

    
    autoRender: false,

    
    allowDomMove: true,

    

    
    rendered: false,

    
    componentLayoutCounter: 0,

    
    shrinkWrap: 2,

    weight: 0,

    
    maskOnDisable: true,

    
    _isLayoutRoot: false,

     
    contentPaddingProperty: 'padding',
    
    horizontalPosProp: 'left',

    
    borderBoxCls: Ext.baseCSSPrefix + 'border-box',

    
    constructor : function(config) {
        var me = this,
            i, len, xhooks;

        if (config) {
            Ext.apply(me, config);

            xhooks = me.xhooks;
            if (xhooks) {
                delete me.xhooks;
                Ext.override(me, xhooks);
            }
        } else {
            config = {};
        }

        me.initialConfig = config;

        me.mixins.elementCt.constructor.call(me);

        me.addEvents(
            
            'beforeactivate',
            
            'activate',
            
            'beforedeactivate',
            
            'deactivate',
            
            'added',
            
            'disable',
            
            'enable',
            
            'beforeshow',
            
            'show',
            
            'beforehide',
            
            'hide',
            
            'removed',
            
            'beforerender',
            
            'render',
            
            'afterrender',
            
            'boxready',
            
            'beforedestroy',
            
            'destroy',
            
            'resize',
            
             'move',
            
            'focus',
            
            'blur'
        );

        me.getId();

        me.setupProtoEl();

        
        
        if (me.cls) {
            me.initialCls = me.cls;
            me.protoEl.addCls(me.cls);
        }
        if (me.style) {
            me.initialStyle = me.style;
            me.protoEl.setStyle(me.style);
        }

        me.renderData = me.renderData || {};
        me.renderSelectors = me.renderSelectors || {};

        if (me.plugins) {
            me.plugins = me.constructPlugins();
        }

        
        if (!me.hasListeners) {
            me.hasListeners = new me.HasListeners();
        }

        me.initComponent();

        
        Ext.ComponentManager.register(me);

        
        me.mixins.observable.constructor.call(me);
        me.mixins.state.constructor.call(me, config);

        
        this.addStateEvents('resize');

        
        if (me.plugins) {
            for (i = 0, len = me.plugins.length; i < len; i++) {
                me.plugins[i] = me.initPlugin(me.plugins[i]);
            }
        }

        me.loader = me.getLoader();

        if (me.renderTo) {
            me.render(me.renderTo);
            
            
            
        }

        
        
        if (me.autoShow && !me.isContained) {
            me.show();
        }

    },

    initComponent: function () {
        
        
        this.plugins = this.constructPlugins();

        
        
        this.setSize(this.width, this.height);
    },

    
    getState: function() {
        var me = this,
            state = null,
            sizeModel = me.getSizeModel();

        if (sizeModel.width.configured) {
            state = me.addPropertyToState(state, 'width');
        }
        if (sizeModel.height.configured) {
            state = me.addPropertyToState(state, 'height');
        }

        return state;
    },

    
    addPropertyToState: function (state, propName, value) {
        var me = this,
            len = arguments.length;

        
        
        if (len == 3 || me.hasOwnProperty(propName)) {
            if (len < 3) {
                value = me[propName];
            }

            
            
            if (value !== me.initialConfig[propName]) {
                (state || (state = {}))[propName] = value;
            }
        }

        return state;
    },

    show: Ext.emptyFn,

    animate: function(animObj) {
        var me = this,
            hasToWidth,
            hasToHeight,
            toHeight,
            toWidth,
            to,
            clearWidth,
            clearHeight,
            curWidth, w, curHeight, h, isExpanding,
            wasConstrained,
            wasConstrainedHeader,
            passedCallback,
            oldOverflow;

        animObj = animObj || {};
        to = animObj.to || {};

        if (Ext.fx.Manager.hasFxBlock(me.id)) {
            return me;
        }

        hasToWidth = Ext.isDefined(to.width);
        if (hasToWidth) {
            toWidth = Ext.Number.constrain(to.width, me.minWidth, me.maxWidth);
        }

        hasToHeight = Ext.isDefined(to.height);
        if (hasToHeight) {
            toHeight = Ext.Number.constrain(to.height, me.minHeight, me.maxHeight);
        }

        
        if (!animObj.dynamic && (hasToWidth || hasToHeight)) {
            curWidth = (animObj.from ? animObj.from.width : undefined) || me.getWidth();
            w = curWidth;
            curHeight = (animObj.from ? animObj.from.height : undefined) || me.getHeight();
            h = curHeight;
            isExpanding = false;

            if (hasToHeight && toHeight > curHeight) {
                h = toHeight;
                isExpanding = true;
            }
            if (hasToWidth && toWidth > curWidth) {
                w = toWidth;
                isExpanding = true;
            }

            
            if (hasToHeight || hasToWidth) {
                oldOverflow = me.el.getStyle('overtflow');
                if (oldOverflow !== 'hidden') {
                    me.el.setStyle('overflow', 'hidden');
                }
            }

            
            
            
            if (isExpanding) {
                clearWidth = !Ext.isNumber(me.width);
                clearHeight = !Ext.isNumber(me.height);

                
                
                
                me.setSize(w, h);
                me.el.setSize(curWidth, curHeight);

                if (clearWidth) {
                    delete me.width;
                }
                if (clearHeight) {
                    delete me.height;
                }
            }
            if (hasToWidth) {
                to.width = toWidth;
            }

            if (hasToHeight) {
                to.height = toHeight;
            }
        }

        
        
        wasConstrained = me.constrain;
        wasConstrainedHeader = me.constrainHeader;
        if (wasConstrained || wasConstrainedHeader) {
            me.constrain = me.constrainHeader = false;
            passedCallback = animObj.callback;
            animObj.callback = function() {
                me.constrain = wasConstrained;
                me.constrainHeader = wasConstrainedHeader;
                
                if (passedCallback) {
                    passedCallback.call(animObj.scope||me, arguments);
                }
                if (oldOverflow !== 'hidden') {
                    me.el.setStyle('overflow', oldOverflow);
                }
            };
        }
        return me.mixins.animate.animate.apply(me, arguments);
    },
    
    setHiddenState: function(hidden){
        var hierarchyState = this.getHierarchyState();
        
        this.hidden = hidden;
        if (hidden) {
            hierarchyState.hidden = true;
        } else {
            delete hierarchyState.hidden;
        }
    },

    onHide: function() {
        
        if (this.ownerLayout) {
            this.updateLayout({ isRoot: false });
        }
    },

    onShow : function() {
        this.updateLayout({ isRoot: false });
    },

    
    constructPlugin: function(plugin) {
        var me = this;
        
        
        if (typeof plugin == 'string') {
            plugin = Ext.PluginManager.create({}, plugin, me);
        }
        
        else {
            plugin = Ext.PluginManager.create(plugin, null, me);
        }
        return plugin;
    },

    
    constructPlugins: function() {
        var me = this,
            plugins = me.plugins,
            result, i, len;

        if (plugins) {
            result = [];
            if (!Ext.isArray(plugins)) {
                plugins = [ plugins ];
            }
            for (i = 0, len = plugins.length; i < len; i++) {
                
                result[i] = me.constructPlugin(plugins[i]);
            }
        }

        me.pluginsInitialized = true;
        return result;
    },

    
    initPlugin : function(plugin) {
        plugin.init(this);

        return plugin;
    },

    
    
    addPlugin: function(plugin) {
        var me = this;

        plugin = me.constructPlugin(plugin);
        if (me.plugins) {
            me.plugins.push(plugin);
        } else {
            me.plugins = [ plugin ];
        }
        if (me.pluginsInitialized) {
            me.initPlugin(plugin);
        }
        return plugin;
    },

    removePlugin: function(plugin) {
        Ext.Array.remove(this.plugins, plugin);
        plugin.destroy();
    },

    
    findPlugin: function(ptype) {
        var i,
            plugins = this.plugins,
            ln = plugins && plugins.length;
        for (i = 0; i < ln; i++) {
            if (plugins[i].ptype === ptype) {
                return plugins[i];
            }
        }
    },

    
    getPlugin: function(pluginId) {
        var i,
            plugins = this.plugins,
            ln = plugins && plugins.length;
        for (i = 0; i < ln; i++) {
            if (plugins[i].pluginId === pluginId) {
                return plugins[i];
            }
        }
    },

    
    beforeLayout: Ext.emptyFn,

    
    updateAria: Ext.emptyFn,

    
    registerFloatingItem: function(cmp) {
        var me = this;
        if (!me.floatingDescendants) {
            me.floatingDescendants = new Ext.ZIndexManager(me);
        }
        me.floatingDescendants.register(cmp);
    },

    unregisterFloatingItem: function(cmp) {
        var me = this;
        if (me.floatingDescendants) {
            me.floatingDescendants.unregister(cmp);
        }
    },

    layoutSuspendCount: 0,

    suspendLayouts: function () {
        var me = this;
        if (!me.rendered) {
            return;
        }
        if (++me.layoutSuspendCount == 1) {
            me.suspendLayout = true;
        }
    },

    resumeLayouts: function (flushOptions) {
        var me = this;
        if (!me.rendered) {
            return;
        }
        if (! --me.layoutSuspendCount) {
            me.suspendLayout = false;
            if (flushOptions && !me.isLayoutSuspended()) {
                me.updateLayout(flushOptions);
            }
        }
    },

    setupProtoEl: function() {
        var cls = this.initCls();

        this.protoEl = new Ext.util.ProtoElement({
            cls: cls.join(' ') 
        });
    },

    initCls: function() {
        var me = this,
            cls = [ me.baseCls, me.getComponentLayout().targetCls ];

        if (Ext.isDefined(me.cmpCls)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.Component: cmpCls has been deprecated. Please use componentCls.');
            }
            me.componentCls = me.cmpCls;
            delete me.cmpCls;
        }

        if (me.componentCls) {
            cls.push(me.componentCls);
        } else {
            me.componentCls = me.baseCls;
        }

        return cls;
    },

    
    setUI: function(ui) {
        var me = this,
            uiCls = me.uiCls,
            activeUI = me.activeUI,
            classes;

        if (ui === activeUI) {
            
            return;
        }

        
        if (activeUI) {
            classes = me.removeClsWithUI(uiCls, true);

            if (classes.length) {
                me.removeCls(classes);
            }

            
            me.removeUIFromElement();
        }
        else {
            
            me.uiCls = [];
        }

        
        me.ui = ui;

        
        
        me.activeUI = ui;

        
        me.addUIToElement();

        classes = me.addClsWithUI(uiCls, true);

        if (classes.length) {
            me.addCls(classes);
        }

        
        
        
        if (me.rendered) {
            me.updateLayout();
        }
    },

    
    addClsWithUI: function(classes, skip) {
        var me = this,
            clsArray = [],
            i = 0,
            uiCls = me.uiCls = Ext.Array.clone(me.uiCls),
            activeUI = me.activeUI,
            length,
            cls;

        if (typeof classes === "string") {
            classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes);
        }

        length = classes.length;

        for (; i < length; i++) {
            cls = classes[i];

            if (cls && !me.hasUICls(cls)) {
                uiCls.push(cls);

                
                if (activeUI) {
                    clsArray = clsArray.concat(me.addUIClsToElement(cls));
                }
            }
        }

        if (skip !== true && activeUI) {
            me.addCls(clsArray);
        }

        return clsArray;
    },

    
    removeClsWithUI: function(classes, skip) {
        var me = this,
            clsArray = [],
            i = 0,
            extArray = Ext.Array,
            remove = extArray.remove,
            uiCls = me.uiCls = extArray.clone(me.uiCls),
            activeUI = me.activeUI,
            length, cls;

        if (typeof classes === "string") {
            classes = (classes.indexOf(' ') < 0) ? [classes] : Ext.String.splitWords(classes);
        }

        length = classes.length;

        for (i = 0; i < length; i++) {
            cls = classes[i];

            if (cls && me.hasUICls(cls)) {
                remove(uiCls, cls);

                
                if (activeUI) {
                    clsArray = clsArray.concat(me.removeUIClsFromElement(cls));
                }
            }
        }

        if (skip !== true && activeUI) {
            me.removeCls(clsArray);
        }

        return clsArray;
    },

    
    hasUICls: function(cls) {
        var me = this,
            uiCls = me.uiCls || [];

        return Ext.Array.contains(uiCls, cls);
    },

    frameElementsArray: ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],

    
    addUIClsToElement: function(cls) {
        var me = this,
            baseClsUi = me.baseCls + '-' + me.ui + '-' + cls,
            result = [Ext.baseCSSPrefix + cls, me.baseCls + '-' + cls, baseClsUi],
            frameElementsArray, frameElementsLength, i, el, frameElement;

        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            frameElementsArray = me.frameElementsArray;
            frameElementsLength = frameElementsArray.length;

            
            for (i = 0; i < frameElementsLength; i++) {
                frameElement = frameElementsArray[i];
                el = me['frame' + frameElement.toUpperCase()];

                if (el) {
                    el.addCls(baseClsUi + '-' + frameElement);
                }
            }
        }

        return result;
    },

    
    removeUIClsFromElement: function(cls) {
        var me = this,
            baseClsUi = me.baseCls + '-' + me.ui + '-' + cls,
            result = [Ext.baseCSSPrefix + cls, me.baseCls + '-' + cls, baseClsUi],
            frameElementsArray, frameElementsLength, i, el, frameElement;

        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            frameElementsArray = me.frameElementsArray;
            frameElementsLength = frameElementsArray.length;

            
            for (i = 0; i < frameElementsLength; i++) {
                frameElement = frameElementsArray[i];
                el = me['frame' + frameElement.toUpperCase()];

                if (el) {
                    el.removeCls(baseClsUi + '-' + frameElement);
                }
            }
        }

        return result;
    },

    
    addUIToElement: function() {
        var me = this,
            baseClsUI = me.baseCls + '-' + me.ui,
            frameElementsArray, frameElementsLength, i, el, frameElement;

        me.addCls(baseClsUI);

        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            frameElementsArray = me.frameElementsArray;
            frameElementsLength = frameElementsArray.length;

            
            for (i = 0; i < frameElementsLength; i++) {
                frameElement = frameElementsArray[i];
                el = me['frame' + frameElement.toUpperCase()];

                if (el) {
                    el.addCls(baseClsUI + '-' + frameElement);
                }
            }
        }
    },

    
    removeUIFromElement: function() {
        var me = this,
            baseClsUI = me.baseCls + '-' + me.ui,
            frameElementsArray, frameElementsLength, i, el, frameElement;

        me.removeCls(baseClsUI);

        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            frameElementsArray = me.frameElementsArray;
            frameElementsLength = frameElementsArray.length;

            for (i = 0; i < frameElementsLength; i++) {
                frameElement = frameElementsArray[i];
                el = me['frame' + frameElement.toUpperCase()];

                if (el) {
                    el.removeCls(baseClsUI + '-' + frameElement);
                }
            }
        }
    },

    
    getTpl: function(name) {
        return Ext.XTemplate.getTpl(this, name);
    },

    
    initStyles: function(targetEl) {
        var me = this,
            Element = Ext.Element,
            margin = me.margin,
            border = me.border,
            cls = me.cls,
            style = me.style,
            x = me.x,
            y = me.y,
            width, height;

        me.initPadding(targetEl);

        if (margin != null) {
            targetEl.setStyle('margin', this.unitizeBox((margin === true) ? 5 : margin));
        }

        if (border != null) {
            me.setBorder(border, targetEl);
        }

        
        
        if (cls && cls != me.initialCls) {
            targetEl.addCls(cls);
            me.cls = me.initialCls = null;
        }
        if (style && style != me.initialStyle) {
            targetEl.setStyle(style);
            me.style = me.initialStyle = null;
        }

        if (x != null) {
            targetEl.setStyle(me.horizontalPosProp, (typeof x == 'number') ? (x + 'px') : x);
        }
        if (y != null) {
            targetEl.setStyle('top', (typeof y == 'number') ? (y + 'px') : y);
        }

        if (Ext.isBorderBox && (!me.ownerCt || me.floating)) {
            targetEl.addCls(me.borderBoxCls);
        }

        
        
        if (!me.getFrameInfo()) {
            width = me.width;
            height = me.height;

            
            if (width != null) {
                if (typeof width === 'number') {
                    if (Ext.isBorderBox) {
                        targetEl.setStyle('width', width + 'px');
                    }
                } else {
                    targetEl.setStyle('width', width);
                }
            }
            if (height != null) {
                if (typeof height === 'number') {
                    if (Ext.isBorderBox) {
                        targetEl.setStyle('height', height + 'px');
                    }
                } else {
                    targetEl.setStyle('height', height);
                }
            }
        }
    },

    
    initPadding: function(targetEl) {
        var me = this,
            padding = me.padding;

        if (padding != null) {
            if (me.layout && me.layout.managePadding && me.contentPaddingProperty === 'padding') {
                
                
                
                
                
                targetEl.setStyle('padding', 0);
            } else {
                
                
                targetEl.setStyle('padding', this.unitizeBox((padding === true) ? 5 : padding));
            }
        }
    },
    
    parseBox: function(box) {
        return Ext.dom.Element.parseBox(box);    
    },
    
    unitizeBox: function(box) {
        return Ext.dom.Element.unitizeBox(box);    
    },
    
    
    setMargin: function(margin,  preventLayout) {
        var me = this;
        
        if (me.rendered) {
            if (!margin && margin !== 0) {
                margin = '';
            } else {
                if (margin === true) {
                    margin = 5;
                }
                margin = this.unitizeBox(margin);
            }
            me.getTargetEl().setStyle('margin', margin);
            if (!preventLayout) {
                me.updateLayout();
            }
        } else {
            me.margin = margin;
        }
    },

    
    initEvents : function() {
        var me = this,
            afterRenderEvents = me.afterRenderEvents,
            afterRenderEvent, el, property, index, len;

        if (afterRenderEvents) {
            for (property in afterRenderEvents) {
                el = me[property];

                if (el && el.on) {
                    afterRenderEvent = afterRenderEvents[property];

                    for (index = 0, len = afterRenderEvent.length ; index < len ; ++index) {
                        me.mon(el, afterRenderEvent[index]);
                     }
                 }
            }
        }

        
        
        
        me.addFocusListener();
    },

    
    addFocusListener: function() {
        var me = this,
            focusEl = me.getFocusEl(),
            needsTabIndex;

        
        
        
        
        
        

        
        if (focusEl) {
            
            
            if (focusEl.isComponent) {
                return focusEl.addFocusListener();
            }

            
            
            
            needsTabIndex = focusEl.needsTabIndex();
            if (!me.focusListenerAdded && (!needsTabIndex || Ext.FocusManager.enabled)) {
                if (needsTabIndex) {
                    focusEl.dom.tabIndex = -1;
                }
                focusEl.on({
                    focus: me.onFocus,
                    blur: me.onBlur,
                    scope: me
                });
                me.focusListenerAdded = true;
            }
        }
    },

    
    getFocusEl: Ext.emptyFn,

    isFocusable: function() {
        var me = this,
            focusEl;
        if ((me.focusable !== false) && (focusEl = me.getFocusEl()) && me.rendered && !me.destroying && !me.isDestroyed && !me.disabled && me.isVisible(true)) {

            
            
            
            return focusEl.isFocusable(true);
        }
    },

    
    beforeFocus: Ext.emptyFn,

    
    onFocus: function(e) {
        var me = this,
            focusCls = me.focusCls,
            focusEl = me.getFocusEl();

        if (!me.disabled) {
            me.beforeFocus(e);
            if (focusCls && focusEl) {
                focusEl.addCls(me.addClsWithUI(focusCls, true));
            }
            if (!me.hasFocus) {
                me.hasFocus = true;
                me.fireEvent('focus', me, e);
            }
        }
    },

    
    beforeBlur : Ext.emptyFn,

    
    onBlur : function(e) {
        var me = this,
            focusCls = me.focusCls,
            focusEl = me.getFocusEl();

        if (me.destroying) {
            return;
        }

        me.beforeBlur(e);
        if (focusCls && focusEl) {
            focusEl.removeCls(me.removeClsWithUI(focusCls, true));
        }
        if (me.validateOnBlur) {
            me.validate();
        }
        me.hasFocus = false;
        me.fireEvent('blur', me, e);
        me.postBlur(e);
    },

    
    postBlur : Ext.emptyFn,

    
    is: function(selector) {
        return Ext.ComponentQuery.is(this, selector);
    },

    
    up: function (selector, limit) {
        var result = this.getRefOwner(),
            limitSelector = typeof limit === 'string',
            limitCount = typeof limit === 'number',
            limitComponent = limit && limit.isComponent,
            steps = 0;

        if (selector) {
            for (; result; result = result.getRefOwner()) {
                steps++;
                if (selector.isComponent) {
                    if (result === selector) {
                        return result;
                    }
                } else {
                    if (Ext.ComponentQuery.is(result, selector)) {
                        return result;
                    }
                }

                
                if (limitSelector && result.is(limit)) {
                    return;
                }
                if (limitCount && steps === limit) {
                    return;
                }
                if (limitComponent && result === limit) {
                    return;
                }
            }
        }
        return result;
    },

    
    nextSibling: function(selector) {
        var o = this.ownerCt, it, last, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this) + 1;
            if (idx) {
                if (selector) {
                    for (last = it.getCount(); idx < last; idx++) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx < it.getCount()) {
                        return it.getAt(idx);
                    }
                }
            }
        }
        return null;
    },

    
    previousSibling: function(selector) {
        var o = this.ownerCt, it, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this);
            if (idx != -1) {
                if (selector) {
                    for (--idx; idx >= 0; idx--) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx) {
                        return it.getAt(--idx);
                    }
                }
            }
        }
        return null;
    },

    
    previousNode: function(selector,  includeSelf) {
        var node = this,
            ownerCt = node.ownerCt,
            result,
            it, i, sib;

        
        if (includeSelf && node.is(selector)) {
            return node;
        }

        if (ownerCt) {
            for (it = ownerCt.items.items, i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) {
                sib = it[i];
                if (sib.query) {
                    result = sib.query(selector);
                    result = result[result.length - 1];
                    if (result) {
                        return result;
                    }
                }
                if (sib.is(selector)) {
                    return sib;
                }
            }
            return ownerCt.previousNode(selector, true);
        }
        return null;
    },

    
    nextNode: function(selector,  includeSelf) {
        var node = this,
            ownerCt = node.ownerCt,
            result,
            it, len, i, sib;

        
        if (includeSelf && node.is(selector)) {
            return node;
        }

        if (ownerCt) {
            for (it = ownerCt.items.items, i = Ext.Array.indexOf(it, node) + 1, len = it.length; i < len; i++) {
                sib = it[i];
                if (sib.is(selector)) {
                    return sib;
                }
                if (sib.down) {
                    result = sib.down(selector);
                    if (result) {
                        return result;
                    }
                }
            }
            return ownerCt.nextNode(selector);
        }
        return null;
    },

    
    getId : function() {
        return this.id || (this.id = 'ext-comp-' + (this.getAutoId()));
    },

    
    getItemId : function() {
        return this.itemId || this.id;
    },

    
    getEl : function() {
        return this.el;
    },

    
    getTargetEl: function() {
        return this.frameBody || this.el;
    },
    
    
    getOverflowEl: function(){
        return this.getTargetEl();
    },

    
    getOverflowStyle: function() {
        var me = this,
            result = null,
            ox, oy,
            overflowStyle;

        
        
        
        if (typeof me.autoScroll === 'boolean') {
            result = {
                overflow: overflowStyle = me.autoScroll ? 'auto' : ''
            };
            me.scrollFlags = {
                overflowX: overflowStyle,
                overflowY: overflowStyle,
                x: true,
                y: true,
                both: true
            };
        } else {
            ox = me.overflowX;
            oy = me.overflowY;
            if (ox !== undefined || oy !== undefined) {
                result = {
                    'overflowX':  ox = ox || '',
                    'overflowY':  oy = oy || ''
                };

                
                me.scrollFlags = {
                    overflowX: ox,
                    overflowY: oy,
                    x: ox = (ox === 'auto' || ox === 'scroll'),
                    y: oy = (oy === 'auto' || oy === 'scroll'),
                    both: ox && oy
                };
            } else {
                me.scrollFlags = {
                    overflowX: '',
                    overflowY: '',
                    x: false,
                    y: false,
                    both: false
                };
            }
        }

        
        
        if (result && Ext.isIE7m) {
            result.position = 'relative';
        }

        return result;
    },

    
    isXType: function(xtype, shallow) {
        if (shallow) {
            return this.xtype === xtype;
        }
        else {
            return this.xtypesMap[xtype];
        }
    },

    
    getXTypes: function() {
        var self = this.self,
            xtypes, parentPrototype, parentXtypes;

        if (!self.xtypes) {
            xtypes = [];
            parentPrototype = this;

            while (parentPrototype) {
                parentXtypes = parentPrototype.xtypes;

                if (parentXtypes !== undefined) {
                    xtypes.unshift.apply(xtypes, parentXtypes);
                }

                parentPrototype = parentPrototype.superclass;
            }

            self.xtypeChain = xtypes;
            self.xtypes = xtypes.join('/');
        }

        return self.xtypes;
    },

    
    update : function(htmlOrData, loadScripts, cb) {
        var me = this,
            isData = (me.tpl && !Ext.isString(htmlOrData)),
            el;

        if (isData) {
            me.data = htmlOrData;
        } else {
            me.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
        }

        if (me.rendered) {
            el = me.isContainer ? me.layout.getRenderTarget() : me.getTargetEl();
            if (isData) {
                me.tpl[me.tplWriteMode](el, htmlOrData || {});
            } else {
                el.update(me.html, loadScripts, cb);
            }
            me.updateLayout();
        }

    },

    
    setVisible : function(visible) {
        return this[visible ? 'show': 'hide']();
    },

    
    isVisible: function(deep) {
        var me = this,
            hidden;

        if (me.hidden || !me.rendered || me.isDestroyed) {
            hidden = true;
        } else if (deep) {
            hidden = me.isHierarchicallyHidden();
        }

        return !hidden;
    },

    isHierarchicallyHidden: function() {
        var child = this,
            hidden = false,
            parent, parentHierarchyState;

        
        
        
        
        for (; (parent = child.ownerCt || child.floatParent); child = parent) {
            parentHierarchyState = parent.getHierarchyState();
            if (parentHierarchyState.hidden) {
                hidden = true;
                break;
            }
            if (child.getHierarchyState().collapseImmune) {
                
                if (parent.collapsed && !child.collapseImmune) {
                    
                    
                    
                    hidden = true;
                    break;
                }
            } else {
                
                
                
                hidden = !!parentHierarchyState.collapsed;
                break;
            }
        }

        return hidden;
    },

    onBoxReady: function(width, height) {
        var me = this;

        if (me.disableOnBoxReady) {
            me.onDisable();
        } else if (me.enableOnBoxReady) {
            me.onEnable();
        }
        if (me.resizable) {
            me.initResizable(me.resizable);
        }

        
        
        if (me.draggable) {
            me.initDraggable();
        }
        
        if (me.hasListeners.boxready) {
            me.fireEvent('boxready', me, width, height);
        }
    },

    
    enable: function(silent) {
        var me = this;

        delete me.disableOnBoxReady;
        me.removeCls(me.disabledCls);
        if (me.rendered) {
            me.onEnable();
        } else {
            me.enableOnBoxReady = true;
        }

        me.disabled = false;
        delete me.resetDisable;

        if (silent !== true) {
            me.fireEvent('enable', me);
        }

        return me;
    },

    
    disable: function(silent) {
        var me = this;

        delete me.enableOnBoxReady;
        me.addCls(me.disabledCls);
        if (me.rendered) {
            me.onDisable();
        } else {
            me.disableOnBoxReady = true;
        }

        me.disabled = true;

        if (silent !== true) {
            delete me.resetDisable;
            me.fireEvent('disable', me);
        }

        return me;
    },

    
    onEnable: function() {
        if (this.maskOnDisable) {
            this.el.dom.disabled = false;
            this.unmask();
        }
    },

    
    onDisable : function() {
        var me = this,
            focusCls = me.focusCls,
            focusEl = me.getFocusEl();
            
        if (focusCls && focusEl) {
            focusEl.removeCls(me.removeClsWithUI(focusCls, true));
        }
        
        if (me.maskOnDisable) {
            me.el.dom.disabled = true;
            me.mask();
        }
    },

    mask: function() {
        var box = this.lastBox,
            target = this.getMaskTarget(),
            args = [];

        
        if (box) {
            args[2] = box.height;
        }
        target.mask.apply(target, args);
    },

    unmask: function() {
        this.getMaskTarget().unmask();
    },

    getMaskTarget: function(){
        return this.el;
    },

    
    isDisabled : function() {
        return this.disabled;
    },

    
    setDisabled : function(disabled) {
        return this[disabled ? 'disable': 'enable']();
    },

    
    isHidden : function() {
        return this.hidden;
    },

    
    addCls : function(cls) {
        var me = this,
            el = me.rendered ? me.el : me.protoEl;

        el.addCls.apply(el, arguments);
        return me;
    },

    
    addClass : function() {
        return this.addCls.apply(this, arguments);
    },

    
    hasCls: function (cls) {
        var me = this,
            el = me.rendered ? me.el : me.protoEl;

        return el.hasCls.apply(el, arguments);
    },

    
    removeCls : function(cls) {
        var me = this,
            el = me.rendered ? me.el : me.protoEl;

        el.removeCls.apply(el, arguments);
        return me;
    },


    addOverCls: function() {
        var me = this;
        if (!me.disabled) {
            me.el.addCls(me.overCls);
        }
    },

    removeOverCls: function() {
        this.el.removeCls(this.overCls);
    },

    addListener : function(element, listeners, scope, options) {
        var me = this,
            fn,
            option;

        if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) {
            if (options.element) {
                fn = listeners;

                listeners = {};
                listeners[element] = fn;
                element = options.element;
                if (scope) {
                    listeners.scope = scope;
                }

                for (option in options) {
                    if (options.hasOwnProperty(option)) {
                        if (me.eventOptionsRe.test(option)) {
                            listeners[option] = options[option];
                        }
                    }
                }
            }

            
            
            if (me[element] && me[element].on) {
                me.mon(me[element], listeners);
            } else {
                me.afterRenderEvents = me.afterRenderEvents || {};
                if (!me.afterRenderEvents[element]) {
                    me.afterRenderEvents[element] = [];
                }
                me.afterRenderEvents[element].push(listeners);
            }
            return;
        }

        return me.mixins.observable.addListener.apply(me, arguments);
    },

    
    removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
        var me = this,
            element = managedListener.options ? managedListener.options.element : null;

        if (element) {
            element = me[element];
            if (element && element.un) {
                if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
                    element.un(managedListener.ename, managedListener.fn, managedListener.scope);
                    if (!isClear) {
                        Ext.Array.remove(me.managedListeners, managedListener);
                    }
                }
            }
        } else {
            return me.mixins.observable.removeManagedListenerItem.apply(me, arguments);
        }
    },

    
    getBubbleTarget : function() {
        return this.ownerCt;
    },

    
    isFloating : function() {
        return this.floating;
    },

    
    isDraggable : function() {
        return !!this.draggable;
    },

    
    isDroppable : function() {
        return !!this.droppable;
    },

    
    onAdded : function(container, pos) {
        var me = this;

        me.ownerCt = container;

        if (me.hierarchyState) {
            
            
            
            me.hierarchyState.invalid = true;
            
            
            
            
            
            delete me.hierarchyState;
        }

        if (me.hasListeners.added) {
            me.fireEvent('added', me, container, pos);
        }
    },

    
    onRemoved : function(destroying) {
        var me = this;
        if (me.hasListeners.removed) {
            me.fireEvent('removed', me, me.ownerCt);
        }
        delete me.ownerCt;
        delete me.ownerLayout;
    },

    
    beforeDestroy : Ext.emptyFn,

    
    onResize: function(width, height, oldWidth, oldHeight) {
        var me = this;

        
        if (me.floating && me.constrain) {
            me.doConstrain();
        }
        if (me.hasListeners.resize) {
            me.fireEvent('resize', me, width, height, oldWidth, oldHeight);
        }
    },

    
    setSize : function(width, height) {
        var me = this;

        
        if (width && typeof width == 'object') {
            height = width.height;
            width  = width.width;
        }

        
        if (typeof width == 'number') {
            me.width = Ext.Number.constrain(width, me.minWidth, me.maxWidth);
        } else if (width === null) {
            delete me.width;
        }
        
        if (typeof height == 'number') {
            me.height = Ext.Number.constrain(height, me.minHeight, me.maxHeight);
        } else if (height === null) {
            delete me.height;
        }

        
        
        if (me.rendered && me.isVisible()) {

            
            me.updateLayout({
                isRoot: false
            });
        }

        return me;
    },

    
    isLayoutRoot: function() {
        var me = this,
            ownerLayout = me.ownerLayout;

        
        
        
        
        if (!ownerLayout || me._isLayoutRoot || me.floating) {
            return true;
        }

        return ownerLayout.isItemLayoutRoot(me);
    },

    
    isLayoutSuspended: function () {
        var comp = this,
            ownerLayout;

        while (comp) {
            if (comp.layoutSuspendCount || comp.suspendLayout) {
                return true;
            }

            ownerLayout = comp.ownerLayout;
            if (!ownerLayout) {
                break;
            }

            

            
            
            
            comp = ownerLayout.owner;
        }

        return false;
    },

    
    updateLayout: function (options) {
        var me = this,
            defer,
            lastBox = me.lastBox,
            isRoot = options && options.isRoot;

        if (lastBox) {
            
            
            lastBox.invalid = true;
        }

        if (!me.rendered || me.layoutSuspendCount || me.suspendLayout) {
            return;
        }

        if (me.hidden) {
            Ext.AbstractComponent.cancelLayout(me);
        } else if (typeof isRoot != 'boolean') {
            isRoot = me.isLayoutRoot();
        }

        
        if (isRoot || !me.ownerLayout || !me.ownerLayout.onContentChange(me)) {
            
            if (!me.isLayoutSuspended()) {
                
                defer = (options && options.hasOwnProperty('defer')) ? options.defer : me.deferLayouts;
                Ext.AbstractComponent.updateLayout(me, defer);
            }
        }
    },

    
    getSizeModel: function (ownerCtSizeModel) {
        var me = this,
            models = Ext.layout.SizeModel,
            ownerContext = me.componentLayout.ownerContext,
            width = me.width,
            height = me.height,
            typeofWidth, typeofHeight,
            hasPixelWidth, hasPixelHeight,
            heightModel, ownerLayout, policy, shrinkWrap, topLevel, widthModel;

        if (ownerContext) {
            
            
            
            
            widthModel = ownerContext.widthModel;
            heightModel = ownerContext.heightModel;
        }

        if (!widthModel || !heightModel) {
            hasPixelWidth = ((typeofWidth = typeof width) == 'number');
            hasPixelHeight = ((typeofHeight = typeof height) == 'number');
            topLevel = me.floating || !(ownerLayout = me.ownerLayout);

            
            if (topLevel) {
                policy = Ext.layout.Layout.prototype.autoSizePolicy;
                shrinkWrap = me.floating ? 3 : me.shrinkWrap;

                if (hasPixelWidth) {
                    widthModel = models.configured;
                }

                if (hasPixelHeight) {
                    heightModel = models.configured;
                }
            } else {
                policy = ownerLayout.getItemSizePolicy(me, ownerCtSizeModel);
                shrinkWrap = ownerLayout.isItemShrinkWrap(me);
            }

            if (ownerContext) {
                ownerContext.ownerSizePolicy = policy;
            }

            shrinkWrap = (shrinkWrap === true) ? 3 : (shrinkWrap || 0); 

            
            
            
            
            if (topLevel && shrinkWrap) {
                if (width && typeofWidth == 'string') {
                    shrinkWrap &= 2; 
                }
                if (height && typeofHeight == 'string') {
                    shrinkWrap &= 1; 
                }
            }

            if (shrinkWrap !== 3) {
                if (!ownerCtSizeModel) {
                    ownerCtSizeModel = me.ownerCt && me.ownerCt.getSizeModel();
                }

                if (ownerCtSizeModel) {
                    shrinkWrap |= (ownerCtSizeModel.width.shrinkWrap ? 1 : 0) | (ownerCtSizeModel.height.shrinkWrap ? 2 : 0);
                }
            }

            if (!widthModel) {
                if (!policy.setsWidth) {
                    if (hasPixelWidth) {
                        widthModel = models.configured;
                    } else {
                        widthModel = (shrinkWrap & 1) ? models.shrinkWrap : models.natural;
                    }
                } else if (policy.readsWidth) {
                    if (hasPixelWidth) {
                        widthModel = models.calculatedFromConfigured;
                    } else {
                        widthModel = (shrinkWrap & 1) ? models.calculatedFromShrinkWrap :
                                    models.calculatedFromNatural;
                    }
                } else {
                    widthModel = models.calculated;
                }
            }

            if (!heightModel) {
                if (!policy.setsHeight) {
                    if (hasPixelHeight) {
                        heightModel = models.configured;
                    } else {
                        heightModel = (shrinkWrap & 2) ? models.shrinkWrap : models.natural;
                    }
                } else if (policy.readsHeight) {
                    if (hasPixelHeight) {
                        heightModel = models.calculatedFromConfigured;
                    } else {
                        heightModel = (shrinkWrap & 2) ? models.calculatedFromShrinkWrap :
                                    models.calculatedFromNatural;
                    }
                } else {
                    heightModel = models.calculated;
                }
            }
        }

        
        
        return widthModel.pairsByHeightOrdinal[heightModel.ordinal];
    },

    isDescendant: function(ancestor) {
        if (ancestor.isContainer) {
            for (var c = this.ownerCt; c; c = c.ownerCt) {
                if (c === ancestor) {
                    return true;
                }
            }
        }
        return false;
    },

    
    doComponentLayout : function() {
        this.updateLayout();
        return this;
    },

    
    forceComponentLayout: function () {
        this.updateLayout();
    },

    
    setComponentLayout : function(layout) {
        var currentLayout = this.componentLayout;
        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
            currentLayout.setOwner(null);
        }
        this.componentLayout = layout;
        layout.setOwner(this);
    },

    getComponentLayout : function() {
        var me = this;

        if (!me.componentLayout || !me.componentLayout.isLayout) {
            me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent'));
        }
        return me.componentLayout;
    },

    
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var me = this;

        if (++me.componentLayoutCounter === 1) {
            me.afterFirstLayout(width, height);
        }

        if (width !== oldWidth || height !== oldHeight) {
            me.onResize(width, height, oldWidth, oldHeight);
        }
    },

    
    beforeComponentLayout: function(width, height) {
        return true;
    },

    
    setPosition: function(x, y, animate) {
        var me = this,
            pos = me.beforeSetPosition.apply(me, arguments);

        if (pos && me.rendered) {
            x = pos.x;
            y = pos.y;

            if (animate) {
                
                
                
                if (x !== me.getLocalX() || y !== me.getLocalY()) {
                    me.stopAnimation();
                    me.animate(Ext.apply({
                        duration: 1000,
                        listeners: {
                            afteranimate: Ext.Function.bind(me.afterSetPosition, me, [x, y])
                        },
                        to: {
                            x: x,
                            y: y
                        }
                    }, animate));
                }
            } else {
                me.setLocalXY(x, y);
                me.afterSetPosition(x, y);
            }
        }
        return me;
    },

    
    beforeSetPosition: function (x, y, animate) {
        var pos, x0;

        
        
        if (x) {
            
            if (Ext.isNumber(x0 = x[0])) {
                animate = y;
                y = x[1];
                x = x0;
            }
            
            else if ((x0 = x.x) !== undefined) {
                animate = y;
                y = x.y;
                x = x0;
            }
        }

        if (this.constrain || this.constrainHeader) {
            pos = this.calculateConstrainedPosition(null, [x, y], true);
            if (pos) {
                x = pos[0];
                y = pos[1];
            }
        }

        
        pos = {
            x : this.x = x,
            y : this.y = y,
            anim: animate,
            hasX: x !== undefined,
            hasY: y !== undefined
        };

        return (pos.hasX || pos.hasY) ? pos : null;
    },

    
    afterSetPosition: function(x, y) {
        var me = this;
        me.onPosition(x, y);
        if (me.hasListeners.move) {
            me.fireEvent('move', me, x, y);
        }
    },

    
    onPosition: Ext.emptyFn,

    
    setWidth : function(width) {
        return this.setSize(width);
    },

    
    setHeight : function(height) {
        return this.setSize(undefined, height);
    },

    
    getSize : function() {
        return this.el.getSize();
    },

    
    getWidth : function() {
        return this.el.getWidth();
    },

    
    getHeight : function() {
        return this.el.getHeight();
    },

    
    getLoader: function(){
        var me = this,
            autoLoad = me.autoLoad ? (Ext.isObject(me.autoLoad) ? me.autoLoad : {url: me.autoLoad}) : null,
            loader = me.loader || autoLoad;

        if (loader) {
            if (!loader.isLoader) {
                me.loader = new Ext.ComponentLoader(Ext.apply({
                    target: me,
                    autoLoad: autoLoad
                }, loader));
            } else {
                loader.setTarget(me);
            }
            return me.loader;

        }
        return null;
    },

    
    setDocked : function(dock, layoutParent) {
        var me = this;

        me.dock = dock;
        if (layoutParent && me.ownerCt && me.rendered) {
            me.ownerCt.updateLayout();
        }
        return me;
    },

    
    setBorder: function(border,  targetEl) {
        var me = this,
            initial = !!targetEl;

        if (me.rendered || initial) {
            if (!initial) {
                targetEl = me.el;
            }

            if (!border) {
                border = 0;
            } else if (border === true) {
                border = '1px';
            } else {
                border = this.unitizeBox(border);
            }
            targetEl.setStyle('border-width', border);
            if (!initial) {
                me.updateLayout();
            }
        }
        me.border = border;
    },

    onDestroy : function() {
        var me = this;

        if (me.monitorResize && Ext.EventManager.resizeEvent) {
            Ext.EventManager.resizeEvent.removeListener(me.setSize, me);
        }

        
        Ext.destroy(
            me.componentLayout,
            me.loadMask,
            me.floatingDescendants
        );
    },

    
    destroy : function() {
        var me = this,
            selectors = me.renderSelectors,
            selector,
            el;

        if (!me.isDestroyed) {
            if (!me.hasListeners.beforedestroy || me.fireEvent('beforedestroy', me) !== false) {
                me.destroying = true;
                me.beforeDestroy();

                if (me.floating) {
                    delete me.floatParent;
                    
                    
                    if (me.zIndexManager) {
                        me.zIndexManager.unregister(me);
                    }
                } else if (me.ownerCt && me.ownerCt.remove) {
                    me.ownerCt.remove(me, false);
                }

                me.stopAnimation();
                me.onDestroy();

                
                Ext.destroy(me.plugins);

                if (me.hasListeners.destroy) {
                    me.fireEvent('destroy', me);
                }
                Ext.ComponentManager.unregister(me);

                me.mixins.state.destroy.call(me);

                me.clearListeners();
                
                if (me.rendered) {
                    if (!me.preserveElOnDestroy) {
                        me.el.remove();
                    }
                    me.mixins.elementCt.destroy.call(me); 
                    if (selectors) {
                        for (selector in selectors) {
                            if (selectors.hasOwnProperty(selector)) {
                                el = me[selector];
                                if (el) { 
                                    delete me[selector];
                                    el.remove();
                                }
                            }
                        }
                    }

                    delete me.el;
                    delete me.frameBody;
                    delete me.rendered;
                }

                me.destroying = false;
                me.isDestroyed = true;
            }
        }
    },

    
    isDescendantOf: function(container) {
        return !!this.findParentBy(function(p){
            return p === container;
        });
    },

    
    getHierarchyState: function (inner) {
        var me = this,
            hierarchyState = (inner && me.hierarchyStateInner) || me.hierarchyState,
            ownerCt = me.ownerCt,
            parent, layout, hierarchyStateInner, getInner;
 
        if (!hierarchyState || hierarchyState.invalid) {
            
            
            
            
            parent = me.getRefOwner();
            
            if (ownerCt) {
                
                
                getInner = me.ownerLayout === ownerCt.layout;
            }

            me.hierarchyState = hierarchyState =
                
                
                
                
                
                Ext.Object.chain(parent ? parent.getHierarchyState(getInner)
                                        : Ext.rootHierarchyState);

            me.initHierarchyState(hierarchyState);
            if ((layout = me.componentLayout).initHierarchyState) {
                layout.initHierarchyState(hierarchyState);
            }

            if (me.isContainer) {
                me.hierarchyStateInner = hierarchyStateInner = Ext.Object.chain(hierarchyState);

                layout = me.layout;
                if (layout && layout.initHierarchyState) {
                    layout.initHierarchyState(hierarchyStateInner, hierarchyState);
                }
                if (inner) {
                    hierarchyState = hierarchyStateInner;
                }
            }
        }

        return hierarchyState;
    },

    
    initHierarchyState: function(hierarchyState) {
        var me = this;

        if (me.collapsed) {
            hierarchyState.collapsed = true;
        }
        if (me.hidden) {
            hierarchyState.hidden = true;
        }
        if (me.collapseImmune) {
            hierarchyState.collapseImmune = true;
        }
    },

    
    
    

    getAnchorToXY: function(el, anchor, local, mySize) {
        return el.getAnchorXY(anchor, local, mySize);
    },

    getBorderPadding: function() {
        return this.el.getBorderPadding();
    },

    getLocalX: function() {
        return this.el.getLocalX();
    },

    getLocalXY: function() {
        return this.el.getLocalXY();
    },

    getLocalY: function() {
        return this.el.getLocalY();
    },

    getX: function() {
        return this.el.getX();
    },

    getXY: function() {
        return this.el.getXY();
    },

    getY: function() {
        return this.el.getY();
    },

    setLocalX: function(x) {
        this.el.setLocalX(x);
    },

    setLocalXY: function(x, y) {
        this.el.setLocalXY(x, y);
    },

    setLocalY: function(y) {
        this.el.setLocalY(y);
    },

    setX: function(x, animate) {
        this.el.setX(x, animate);
    },

    setXY: function(xy, animate) {
        this.el.setXY(xy, animate);
    },

    setY: function(y, animate) {
        this.el.setY(y, animate);
    }

    
    
    
}, function() {
    var AbstractComponent = this;

    AbstractComponent.createAlias({
        on: 'addListener',
        prev: 'previousSibling',
        next: 'nextSibling'
    });

    
    Ext.resumeLayouts = function (flush) {
        AbstractComponent.resumeLayouts(flush);
    };

    
    Ext.suspendLayouts = function () {
        AbstractComponent.suspendLayouts();
    };

    
    Ext.batchLayouts = function(fn, scope) {
        AbstractComponent.suspendLayouts();
        
        fn.call(scope);
        AbstractComponent.resumeLayouts(true);
    };
});


Ext.define('Ext.AbstractPlugin', {
    disabled: false,

    
    isPlugin: true,

    
    constructor: function(config) {
        this.pluginConfig = config;
        Ext.apply(this, config);
    },

    
    clonePlugin: function(overrideCfg) {
        return new this.self(Ext.apply({}, overrideCfg, this.pluginConfig));
    },

    
    setCmp: function(cmp) {
        this.cmp = cmp;
    },

    
    getCmp: function() {
        return this.cmp;
    },

    

    
    init: Ext.emptyFn,

    
    destroy: Ext.emptyFn,

    
    enable: function() {
        this.disabled = false;
    },

    
    disable: function() {
        this.disabled = true;
    },

    
    
    onClassExtended: function(cls, data, hooks) {
        var alias = data.alias;

        
        if (alias && !data.ptype) {
            if (Ext.isArray(alias)) {
                alias = alias[0];
            }
            cls.prototype.ptype = alias.split('plugin.')[1];
        }
    }
});


Ext.define('Ext.Action', {

    

    

    
    
    
    
    
    
    

    
    constructor : function(config){
        this.initialConfig = config;
        this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
        this.items = [];
    },

    
    isAction : true,

    
    setText : function(text){
        this.initialConfig.text = text;
        this.callEach('setText', [text]);
    },

    
    getText : function(){
        return this.initialConfig.text;
    },

    
    setIconCls : function(cls){
        this.initialConfig.iconCls = cls;
        this.callEach('setIconCls', [cls]);
    },

    
    getIconCls : function(){
        return this.initialConfig.iconCls;
    },

    
    setDisabled : function(v){
        this.initialConfig.disabled = v;
        this.callEach('setDisabled', [v]);
    },

    
    enable : function(){
        this.setDisabled(false);
    },

    
    disable : function(){
        this.setDisabled(true);
    },

    
    isDisabled : function(){
        return this.initialConfig.disabled;
    },

    
    setHidden : function(v){
        this.initialConfig.hidden = v;
        this.callEach('setVisible', [!v]);
    },

    
    show : function(){
        this.setHidden(false);
    },

    
    hide : function(){
        this.setHidden(true);
    },

    
    isHidden : function(){
        return this.initialConfig.hidden;
    },

    
    setHandler : function(fn, scope){
        this.initialConfig.handler = fn;
        this.initialConfig.scope = scope;
        this.callEach('setHandler', [fn, scope]);
    },

    
    each : function(fn, scope){
        Ext.each(this.items, fn, scope);
    },

    
    callEach : function(fnName, args){
        var items = this.items,
            i = 0,
            len = items.length,
            item;

        Ext.suspendLayouts();
        for(; i < len; i++){
            item = items[i];
            item[fnName].apply(item, args);
        }
        Ext.resumeLayouts(true);
    },

    
    addComponent : function(comp){
        this.items.push(comp);
        comp.on('destroy', this.removeComponent, this);
    },

    
    removeComponent : function(comp){
        Ext.Array.remove(this.items, comp);
    },

    
    execute : function(){
        this.initialConfig.handler.apply(this.initialConfig.scope || Ext.global, arguments);
    }
});


Ext.define('Ext.data.flash.BinaryXhr', {
    
    statics: {
        
        flashPluginActivated: function() {
            Ext.data.flash.BinaryXhr.flashPluginActive = true;
            Ext.data.flash.BinaryXhr.flashPlugin = document.getElementById("ext-flash-polyfill");
            Ext.globalEvents.fireEvent("flashready"); 
        },
        
        
        flashPluginActive: false,
        
        
        flashPluginInjected: false,
        
        
        
        connectionIndex: 1,
        
        
        liveConnections: {},
        
        
        flashPlugin: null,
        
        
        onFlashStateChange: function(javascriptId, state, data) {
            var connection;
            
            connection = this.liveConnections[Number(javascriptId)]; 
            if (connection) {
                connection.onFlashStateChange(state, data);
            } 
        },
        
        
        registerConnection: function(conn) {
            var i = this.connectionIndex;
            this.conectionIndex = this.connectionIndex + 1;
            this.liveConnections[i] = conn;
            return i;
        },
        
        
        injectFlashPlugin: function() {
            var divTag, pTag, aTag, iTag,
                me=this,
                flashLoaderPath, flashObjectPath;
                
               
               
                
                
            
            iTag=document.createElement("img");
            iTag.setAttribute("src",  window.location.protocol + '//www.adobe.com/images/shared/download_buttons/get_flash_player.gif');
            iTag.setAttribute("alt", "Get Adobe Flash player");
            
            aTag=document.createElement("a");
            aTag.setAttribute("href", "http://www.adobe.com/go/getflashplayer");
            aTag.appendChild(iTag);
            
            pTag=document.createElement("p");
            pTag.innerHTML="To view this page ensure that Adobe Flash Player version 11.1.0 or greater is installed.";
            
            divTag=document.createElement("div");
            divTag.setAttribute("id", "ext-flash-polyfill");
            divTag.appendChild(pTag);
            divTag.appendChild(iTag);
            
            Ext.getBody().dom.appendChild(divTag);
            
            
            
            
            
            flashLoaderPath = [Ext.Loader.getPath('Ext.data.Connection'), '../../../plugins/flash/swfobject.js'].join('/');
            flashObjectPath = "/plugins/flash/FlashPlugin.swf";
            if (Ext.flashPluginPath) {
                flashObjectPath = Ext.flashPluginPath;
            }
            
            Ext.Loader.loadScript({
                url:flashLoaderPath,
                onLoad: function() {
                    
                    var swfVersionStr = "11.4.0";
                    
                    var xiSwfUrlStr = "playerProductInstall.swf";
                    var flashvars = {};
                    var params = {};
                    params.quality = "high";
                    params.bgcolor = "#ffffff";
                    params.allowscriptaccess = "sameDomain";
                    params.allowfullscreen = "true";
                    var attributes = {};
                    attributes.id = "ext-flash-polyfill";
                    attributes.name = "polyfill";
                    attributes.align = "middle";
                    swfobject.embedSWF(
                        flashObjectPath, "ext-flash-polyfill", 
                        "0", "0", 
                        swfVersionStr, xiSwfUrlStr, 
                        flashvars, params, attributes);
                },
                onError: function() {
                },
                scope: me
            });
            Ext.globalEvents.addEvents("flashready"); 
            Ext.data.flash.BinaryXhr.flashPluginInjected = true;
        }

    
    },
    
    
    readyState: 0,
    
    
    status: 0,
    
    
    
    statusText: "",
    
    
    responseBytes: null,
    
    
    javascriptId: null,
    
    
    
    constructor: function (config) {
        
        if (!Ext.data.flash.BinaryXhr.flashPluginInjected) {
            Ext.data.flash.BinaryXhr.injectFlashPlugin();
        }
        var me = this;

        Ext.apply(me, config);
        me.requestHeaders = {};
    },

    
    abort: function () {
        var me = this;
        
        if (me.readyState == 4) {
            return;
        }
        
        me.aborted = true;
        
        if (!Ext.data.flash.BinaryXhr.flashPluginActive) {
            Ext.globalEvents.removeListener("flashready", me.onFlashReady, me);
            return;
        }
        
        Ext.data.flash.BinaryXhr.flashPlugin.abortRequest(me.javascriptId);
        
        delete Ext.data.flash.BinaryXhr.liveConnections[me.javascriptId];
    },

    
    getAllResponseHeaders: function () {
        var headers = [];
        Ext.Object.each(this.responseHeaders, function (name, value) {
            headers.push(name + ': ' + value);
        });
        return headers.join('\x0d\x0a');
    },

    
    getResponseHeader: function (header) {
        var headers = this.responseHeaders;
        return (headers && headers[header]) || null;
    },

    
    open: function (method, url, async, user, password) {
        var me = this;
        me.method = method;
        me.url = url;
        me.async = async !== false;
        me.user = user;
        me.password = password;
        
    },

    
    overrideMimeType: function (mimeType) {
        this.mimeType = mimeType;
    },

    
    send: function (body) {
        var me = this;
        me.body = body;
        if (!Ext.data.flash.BinaryXhr.flashPluginActive) {
            Ext.globalEvents.addListener("flashready", me.onFlashReady, me);
        } else {
            this.onFlashReady();
        }
    },
    
    
    onFlashReady: function() {
        var me = this, req, status;
        me.javascriptId = Ext.data.flash.BinaryXhr.registerConnection(me);
        
        
        req = {
            method: me.method, 
            url: me.url,
            user: me.user,
            password: me.password,
            mimeType: me.mimeType,
            requestHeaders: me.requestHeaders,
            body: me.body,
            javascriptId: me.javascriptId
        };
        status = Ext.data.flash.BinaryXhr.flashPlugin.postBinary(req);
    },

    
    setReadyState: function (state) {
        var me = this;
        if (me.readyState != state) {
            me.readyState = state;
            me.onreadystatechange();
        }
    },

    
    setRequestHeader: function (header, value) {
        this.requestHeaders[header] = value;
    },

    
    onreadystatechange: Ext.emptyFn,

    
    parseData: function (data) {
        var me = this;
        
        this.status = data.status || 0; 
        
        me.responseHeaders = {};
        if (me.mimeType) {
            me.responseHeaders["content-type"] = me.mimeType;
        }
        if (data.reason == "complete") {
            
            this.responseBytes = data.data;
            me.responseHeaders["content-length"] = data.data.length;
        } else if (data.reason == "error" || data.reason == "securityError") {
            this.statusText = data.text;
            me.responseHeaders["content-length"] = 0; 
        }
    },

    
    onFlashStateChange: function(state, data) {
        var me = this;
        if (state == 4) {
            
            me.parseData(data);
            
            delete Ext.data.flash.BinaryXhr.liveConnections[me.javascriptId];
        }
        me.setReadyState(state); 
    }
    
});


Ext.define('Ext.data.Connection', {
    mixins: {
        observable:  Ext.util.Observable 
    },
    
               
                                  
      

    statics: {
        requestId: 0
    },

    url: null,
    async: true,
    method: null,
    username: '',
    password: '',

    
    disableCaching: true,

    
    withCredentials: false,

    
    binary: false,

    
    cors: false,

    isXdr: false,

    defaultXdrContentType: 'text/plain',

    
    disableCachingParam: '_dc',

    
    timeout : 30000,

    

    

    

    

    useDefaultHeader : true,
    defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
    useDefaultXhrHeader : true,
    defaultXhrHeader : 'XMLHttpRequest',

    constructor : function(config) {
        config = config || {};
        Ext.apply(this, config);

        
        
        
        this.requests = {};
        this.mixins.observable.constructor.call(this);
    },

    
    request : function(options) {
        options = options || {};
        var me = this,
            scope = options.scope || window,
            username = options.username || me.username,
            password = options.password || me.password || '',
            async,
            requestOptions,
            request,
            headers,
            xhr;
        if (me.fireEvent('beforerequest', me, options) !== false) {

            requestOptions = me.setOptions(options, scope);

            if (me.isFormUpload(options)) {
                me.upload(options.form, requestOptions.url, requestOptions.data, options);
                return null;
            }

            
            if (options.autoAbort || me.autoAbort) {
                me.abort();
            }

            
            async = options.async !== false ? (options.async || me.async) : false;
            xhr = me.openRequest(options, requestOptions, async, username, password);

            
            if (!me.isXdr) {
                headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);
            }

            
            request = {
                id: ++Ext.data.Connection.requestId,
                xhr: xhr,
                headers: headers,
                options: options,
                async: async,
                binary: options.binary || me.binary,
                timeout: setTimeout(function() {
                    request.timedout = true;
                    me.abort(request);
                }, options.timeout || me.timeout)
            };

            me.requests[request.id] = request;
            me.latestId = request.id;
            
            if (async) {
                if (!me.isXdr) {
                    xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
                }
            }

            if (me.isXdr) {
                me.processXdrRequest(request, xhr);
            }

            
            xhr.send(requestOptions.data);
            if (!async) {
                return me.onComplete(request);
            }
            return request;
        } else {
            Ext.callback(options.callback, options.scope, [options, undefined, undefined]);
            return null;
        }
    },

    processXdrRequest: function(request, xhr) {
        var me = this;

        
        delete request.headers;

        request.contentType = request.options.contentType || me.defaultXdrContentType;

        xhr.onload = Ext.Function.bind(me.onStateChange, me, [request, true]);
        xhr.onerror = xhr.ontimeout = Ext.Function.bind(me.onStateChange, me, [request, false]);
    },

    processXdrResponse: function(response, xhr) {
        
        response.getAllResponseHeaders = function () {
            return [];
        };
        response.getResponseHeader = function () {
            return '';
        };
        response.contentType = xhr.contentType || this.defaultXdrContentType;
    },

    
    upload: function(form, url, params, options) {
        form = Ext.getDom(form);
        options = options || {};

        var id = Ext.id(),
            frame = document.createElement('iframe'),
            hiddens = [],
            encoding = 'multipart/form-data',
            buf = {
                target: form.target,
                method: form.method,
                encoding: form.encoding,
                enctype: form.enctype,
                action: form.action
            },
            addField = function(name, value) {
                hiddenItem = document.createElement('input');
                Ext.fly(hiddenItem).set({
                    type: 'hidden',
                    value: value,
                    name: name
                });
                form.appendChild(hiddenItem);
                hiddens.push(hiddenItem);
            },
            hiddenItem, obj, value, name, vLen, v, hLen, h;

        
        Ext.fly(frame).set({
            id: id,
            name: id,
            cls: Ext.baseCSSPrefix + 'hide-display',
            src: Ext.SSL_SECURE_URL
        });

        document.body.appendChild(frame);

        
        if (document.frames) {
            document.frames[id].name = id;
        }

        Ext.fly(form).set({
            target: id,
            method: 'POST',
            enctype: encoding,
            encoding: encoding,
            action: url || buf.action
        });

        
        if (params) {
            obj = Ext.Object.fromQueryString(params) || {};

            for (name in obj) {
                if (obj.hasOwnProperty(name)) {
                    value = obj[name];  
                    if (Ext.isArray(value)) {
                        vLen = value.length;
                        for (v = 0; v < vLen; v++) {
                            addField(name, value[v]);
                        }
                    } else {
                        addField(name, value);
                    }
                }
            }
        }

        Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: !Ext.isOpera});
        form.submit();

        Ext.fly(form).set(buf);

        hLen = hiddens.length;

        for (h = 0; h < hLen; h++) {
            Ext.removeNode(hiddens[h]);
        }
    },

    
    onUploadComplete: function(frame, options) {
        var me = this,
            
            response = {
                responseText: '',
                responseXML: null
            }, callback, success, doc, contentNode;

        try {
            doc = frame.contentWindow.document || frame.contentDocument || window.frames[frame.id].document;
            
            
            if (doc) {
                 if (Ext.isOpera && doc.location == 'about:blank') {
                     return;
                 }
                if (doc.body) {

                    
                    
                    if ((contentNode = doc.body.firstChild) && /pre/i.test(contentNode.tagName)) {
                        response.responseText = contentNode.textContent;
                    }

                    
                    
                    else if ((contentNode = doc.getElementsByTagName('textarea')[0])) {
                        response.responseText = contentNode.value;
                    }
                    
                    else {
                        response.responseText = doc.body.textContent || doc.body.innerText;
                    }
                }
                
                response.responseXML = doc.XMLDocument || doc;
                callback = options.success;
                success = true;
            }
        } catch (e) {
            
            response.responseText = '{success:false,message:"' + Ext.String.trim(e.message || e.description) + '"}';
            callback = options.failure;
            success = false;
        }

        me.fireEvent('requestcomplete', me, response, options);

        Ext.callback(callback, options.scope, [response, options]);
        Ext.callback(options.callback, options.scope, [options, success, response]);

        setTimeout(function() {
            Ext.removeNode(frame);
        }, 100);
    },

    
    isFormUpload: function(options) {
        var form = this.getForm(options);
        if (form) {
            return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
        }
        return false;
    },

    
    getForm: function(options) {
        return Ext.getDom(options.form) || null;
    },

    
    setOptions: function(options, scope) {
        var me = this,
            params = options.params || {},
            extraParams = me.extraParams,
            urlParams = options.urlParams,
            url = options.url || me.url,
            jsonData = options.jsonData,
            method,
            disableCache,
            data;


        
        if (Ext.isFunction(params)) {
            params = params.call(scope, options);
        }

        
        if (Ext.isFunction(url)) {
            url = url.call(scope, options);
        }

        url = this.setupUrl(options, url);


        
        data = options.rawData || options.binaryData || options.xmlData || jsonData || null;
        if (jsonData && !Ext.isPrimitive(jsonData)) {
            data = Ext.encode(data);
        }
        
        if (options.binaryData) {
            if (me.nativeBinaryPostSupport()) {
                data = (new Uint8Array(options.binaryData));
                if ((Ext.isChrome && Ext.chromeVersion < 22) || Ext.isSafari || Ext.isGecko) {
                    data = data.buffer; 
                }
            }
        }
        
        
        if (Ext.isObject(params)) {
            params = Ext.Object.toQueryString(params);
        }

        if (Ext.isObject(extraParams)) {
            extraParams = Ext.Object.toQueryString(extraParams);
        }

        params = params + ((extraParams) ? ((params) ? '&' : '') + extraParams : '');

        urlParams = Ext.isObject(urlParams) ? Ext.Object.toQueryString(urlParams) : urlParams;

        params = this.setupParams(options, params);

        
        method = (options.method || me.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();
        this.setupMethod(options, method);


        disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false;
        
        if (method === 'GET' && disableCache) {
            url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime()));
        }

        
        if ((method == 'GET' || data) && params) {
            url = Ext.urlAppend(url, params);
            params = null;
        }

        
        if (urlParams) {
            url = Ext.urlAppend(url, urlParams);
        }

        return {
            url: url,
            method: method,
            data: data || params || null
        };
    },

    
    setupUrl: function(options, url) {
        var form = this.getForm(options);
        if (form) {
            url = url || form.action;
        }
        return url;
    },


    
    setupParams: function(options, params) {
        var form = this.getForm(options),
            serializedForm;
        if (form && !this.isFormUpload(options)) {
            serializedForm = Ext.Element.serializeForm(form);
            params = params ? (params + '&' + serializedForm) : serializedForm;
        }
        return params;
    },

    
    setupMethod: function(options, method) {
        if (this.isFormUpload(options)) {
            return 'POST';
        }
        return method;
    },

    
    setupHeaders: function(xhr, options, data, params) {
        var me = this,
            headers = Ext.apply({}, options.headers || {}, me.defaultHeaders || {}),
            contentType = me.defaultPostHeader,
            jsonData = options.jsonData,
            xmlData = options.xmlData,
            key,
            header;

        if (!headers['Content-Type'] && (data || params)) {
            if (data) {
                if (options.rawData) {
                    contentType = 'text/plain';
                } else {
                    if (xmlData && Ext.isDefined(xmlData)) {
                        contentType = 'text/xml';
                    } else if (jsonData && Ext.isDefined(jsonData)) {
                        contentType = 'application/json';
                    }
                }
            }
            headers['Content-Type'] = contentType;
        }

        if (me.useDefaultXhrHeader && !headers['X-Requested-With']) {
            headers['X-Requested-With'] = me.defaultXhrHeader;
        }
        
        try {
            for (key in headers) {
                if (headers.hasOwnProperty(key)) {
                    header = headers[key];
                    xhr.setRequestHeader(key, header);
                }
            }
        } catch(e) {
            me.fireEvent('exception', key, header);
        }
        return headers;
    },

    
    newRequest: function (options) {
        var me = this,
            xhr;

        if (options.binaryData) {
            
            if (me.nativeBinaryPostSupport()) { 
                xhr = this.getXhrInstance(); 
            } else {
                
                xhr = new Ext.data.flash.BinaryXhr();
            }
        } else  if ((options.cors || me.cors) && Ext.isIE && Ext.ieVersion <= 9) {
            xhr = me.getXdrInstance();
            me.isXdr = true;
        } else {
            xhr = me.getXhrInstance();
        }

        return xhr;
    },

    
    openRequest: function (options, requestOptions, async, username, password) {
        var me = this,
            xhr = me.newRequest(options);

        if (username) {
            xhr.open(requestOptions.method, requestOptions.url, async, username, password);
        } else {
            if (me.isXdr) {
                xhr.open(requestOptions.method, requestOptions.url);
            } else {
                xhr.open(requestOptions.method, requestOptions.url, async);
            }
        }

        if (options.binary || me.binary) {
            if (window.Uint8Array) {
                xhr.responseType = 'arraybuffer';
            } else if (xhr.overrideMimeType) {
                
                
                
                
                xhr.overrideMimeType('text\/plain; charset=x-user-defined');  
            }
        }

        if (options.withCredentials || me.withCredentials) {
            xhr.withCredentials = true;
        }

        return xhr;
    },

    
    getXdrInstance: function() {
        var xdr;

        if (Ext.ieVersion >= 8) {
            xdr = new XDomainRequest();
        } else {
            Ext.Error.raise({
                msg: 'Your browser does not support CORS'
            });
        }

        return xdr;
    },

    
    getXhrInstance: (function() {
        var options = [function() {
            return new XMLHttpRequest();
        }, function() {
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
        }, function() {
            return new ActiveXObject('MSXML2.XMLHTTP');
        }, function() {
            return new ActiveXObject('Microsoft.XMLHTTP');
        }], i = 0,
            len = options.length,
            xhr;

        for (; i < len; ++i) {
            try {
                xhr = options[i];
                xhr();
                break;
            } catch(e) {
            }
        }
        return xhr;
    }()),

    
    isLoading : function(request) {
        if (!request) {
            request = this.getLatest();
        }
        if (!(request && request.xhr)) {
            return false;
        }
        
        var state = request.xhr.readyState;
        return ((request.xhr instanceof Ext.data.flash.BinaryXhr) && state != 4) || !(state === 0 || state == 4);
    },

    
    abort : function(request) {
        var me = this,
            xhr;
        
        if (!request) {
            request = me.getLatest();
        }

        if (request && me.isLoading(request)) {
            
            xhr = request.xhr;
            try {
                xhr.onreadystatechange = null;
            } catch (e) {
                
                
                xhr.onreadystatechange = Ext.emptyFn;
            }
            xhr.abort();
            me.clearTimeout(request);
            if (!request.timedout) {
                request.aborted = true;
            }
            me.onComplete(request);
            me.cleanup(request);
        }
    },
    
    
    abortAll: function(){
        var requests = this.requests,
            id;
        
        for (id in requests) {
            if (requests.hasOwnProperty(id)) {
                this.abort(requests[id]);
            }
        }
    },
    
    
    getLatest: function(){
        var id = this.latestId,
            request;
            
        if (id) {
            request = this.requests[id];
        }
        return request || null;
    },

    
    onStateChange : function(request, xdrResult) {
        var me = this;

        
        if ((request.xhr && request.xhr.readyState == 4) || me.isXdr) {
            me.clearTimeout(request);
            me.onComplete(request, xdrResult);
            me.cleanup(request);
            Ext.EventManager.idleEvent.fire();
        }
    },

    
    clearTimeout: function(request) {
        clearTimeout(request.timeout);
        delete request.timeout;
    },

    
    cleanup: function(request) {
        request.xhr = null;
        delete request.xhr;
    },

    
    onComplete : function(request, xdrResult) {
        var me = this,
            options = request.options,
            result,
            success,
            response;

        try {
            result = me.parseStatus(request.xhr.status);
        } catch (e) {
            
            result = {
                success : false,
                isException : false
            };

        }
        success = me.isXdr ? xdrResult : result.success;

        if (success) {
            response = me.createResponse(request);
            me.fireEvent('requestcomplete', me, response, options);
            Ext.callback(options.success, options.scope, [response, options]);
        } else {
            if (result.isException || request.aborted || request.timedout) {
                response = me.createException(request);
            } else {
                response = me.createResponse(request);
            }
            me.fireEvent('requestexception', me, response, options);
            Ext.callback(options.failure, options.scope, [response, options]);
        }
        Ext.callback(options.callback, options.scope, [options, success, response]);
        delete me.requests[request.id];
        return response;
    },

    
    parseStatus: function(status) {
        
        status = status == 1223 ? 204 : status;

        var success = (status >= 200 && status < 300) || status == 304,
            isException = false;

        if (!success) {
            switch (status) {
                case 12002:
                case 12029:
                case 12030:
                case 12031:
                case 12152:
                case 13030:
                    isException = true;
                    break;
            }
        }
        return {
            success: success,
            isException: isException
        };
    },

    
    createResponse : function(request) {
        var me = this,
            xhr = request.xhr,
            isXdr = me.isXdr,
            headers = {},
            lines = isXdr ? [] : xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'),
            count = lines.length,
            line, index, key, response, byteArray;

        while (count--) {
            line = lines[count];
            index = line.indexOf(':');
            if (index >= 0) {
                key = line.substr(0, index).toLowerCase();
                if (line.charAt(index + 1) == ' ') {
                    ++index;
                }
                headers[key] = line.substr(index + 1);
            }
        }

        request.xhr = null;
        delete request.xhr;

        response = {
            request: request,
            requestId: request.id,
            status: xhr.status,
            statusText: xhr.statusText,
            getResponseHeader: function(header) {
                return headers[header.toLowerCase()];
            },
            getAllResponseHeaders: function() {
                return headers;
            }
        };

        if (isXdr) {
            me.processXdrResponse(response, xhr);
        }

        if (request.binary) {
            response.responseBytes = me.getByteArray(xhr);
        } else {
            
            
            
            
            response.responseText = xhr.responseText;
            response.responseXML = xhr.responseXML;
        }

        
        
        xhr = null;
        return response;
    },

    
    createException : function(request) {
        return {
            request : request,
            requestId : request.id,
            status : request.aborted ? -1 : 0,
            statusText : request.aborted ? 'transaction aborted' : 'communication failure',
            aborted: request.aborted,
            timedout: request.timedout
        };
    },

    
    getByteArray: function(xhr) {
        var response = xhr.response,
            responseBody = xhr.responseBody,
            byteArray, responseText, len, i;

        if (xhr instanceof Ext.data.flash.BinaryXhr) {
            
            byteArray = xhr.responseBytes;
        } else if (window.Uint8Array) {
            
            
            
            byteArray = response ? new Uint8Array(response) : [];
        } else if (Ext.isIE9p) {
            
            
            
            
            try {
                byteArray = new VBArray(responseBody).toArray();
            } catch(e) {
                
                
                
                
                byteArray = [];
            }
        } else if (Ext.isIE) {
            
            
            
            
            
            if (!this.self.vbScriptInjected) {
                this.injectVBScript();
            }
            getIEByteArray(xhr.responseBody, byteArray = []);
        } else {
            
            
            byteArray = [];
            responseText = xhr.responseText;
            len = responseText.length;
            for (i = 0; i < len; i++) {
                
                
                
                byteArray.push(responseText.charCodeAt(i) & 0xFF);
            }
        }

        return byteArray;
    },

    
    injectVBScript: function() {
        var scriptTag = document.createElement('script');
        scriptTag.type = 'text/vbscript';
        scriptTag.text = [
            'Function getIEByteArray(byteArray, out)',
                'Dim len, i',
                'len = LenB(byteArray)',
                'For i = 1 to len',
                    'out.push(AscB(MidB(byteArray, i, 1)))',
                'Next',
            'End Function'
        ].join('\n');
        Ext.getHead().dom.appendChild(scriptTag);
        this.self.vbScriptInjected = true;
    },
    
    
    nativeBinaryPostSupport: function() {
        return Ext.isChrome ||
            (Ext.isSafari && Ext.isDefined(window.Uint8Array)) ||
            (Ext.isGecko && Ext.isDefined(window.Uint8Array));
    }
    
    
});


Ext.define('Ext.Ajax', {
    extend:  Ext.data.Connection ,
    singleton: true,

    
    
    
    
    
    

    
    
    
    
    
    

    
    autoAbort : false
});


Ext.define('Ext.util.Floating', {

                                             

    
    focusOnToFront: true,

    
    shadow: 'sides',

    
    constrain: false,

    

    

    constructor: function (dom) {
        var me = this;

        
        me.fixed = me.fixed && !(Ext.isIE6 || Ext.isIEQuirks);

        me.el = new Ext.dom.Layer(Ext.apply({
            preventSync  : true,
            hideMode     : me.hideMode,
            hidden       : me.hidden,
            shadow       : (typeof me.shadow != 'undefined') ? me.shadow : 'sides',
            shadowOffset : me.shadowOffset,
            constrain    : false,
            fixed        : me.fixed,
            shim         : (me.shim === false) ? false : undefined
        }, me.floating), dom);

        
        
        if (me.modal && !(Ext.FocusManager && Ext.FocusManager.enabled)) {
            me.mon(me.el, {
                keydown: me.onKeyDown,
                scope: me
            });
        }

        
        me.mon(me.el, {
            mousedown: me.onMouseDown,
            scope: me
        });

        
        me.floating = true;

        
        
        
        me.registerWithOwnerCt();

        me.initHierarchyEvents();
    },

    initHierarchyEvents: function() {
        var me = this,
            syncHidden = this.syncHidden;

        if (!me.hasHierarchyEventListeners) {
            me.mon(me.hierarchyEventSource, {
                hide: syncHidden,
                collapse: syncHidden,
                show: syncHidden,
                expand: syncHidden,
                added: syncHidden,
                scope: me
            });
            me.hasHierarchyEventListeners = true;
        }
    },

    registerWithOwnerCt: function() {
        var me = this,
            ownerCt = me.ownerCt,
            zip = me.zIndexParent;

        if (zip) {
            zip.unregisterFloatingItem(me);
        }

        
        
        zip = me.zIndexParent = me.up('[floating]');

        
        
        
        me.setFloatParent(ownerCt || zip);
        delete me.ownerCt;

        if (zip) {
            zip.registerFloatingItem(me);
        } else {
            Ext.WindowManager.register(me);
        }
    },

    
    onKeyDown: function(e) {
        var me = this,
            shift,
            focusables,
            first,
            last;

        
        
        
        
        
        
        
        
        
        
        
        if (e.getKey() == Ext.EventObject.TAB) {
            shift = e.shiftKey;
            focusables = me.el.query(':focusable');
            first = focusables[0];
            last = focusables[focusables.length - 1];
            if (first && last && e.target === (shift ? first : last)) {
                e.stopEvent();
                (shift ? last : first).focus(false, true);
            }
        }
    },

    
    
    onMouseDown: function (e) {
        var focusTask = this.focusTask;
        
        if (this.floating &&
            
            
            
            (!focusTask || !focusTask.id)) {
            
            this.toFront(!!e.getTarget(':focusable'));
        }
    },

    setFloatParent: function(floatParent) {
        var me = this;

        me.floatParent = floatParent;

        
        
        if ((me.constrain || me.constrainHeader) && !me.constrainTo) {
            me.constrainTo = floatParent ? floatParent.getTargetEl() : me.container;
        }
    },
    
    
    syncShadow : function() {
        if (this.floating) {
            this.el.sync(true);
        }
    },
    
    onBeforeFloatLayout: function(){
        this.el.preventSync = true;
    },
    
    onAfterFloatLayout: function(){
        delete this.el.preventSync;
        this.syncShadow();   
    },

    
    syncHidden: function() {
        var me = this,
            hidden = me.hidden || !me.rendered,
            hierarchicallyHidden = me.hierarchicallyHidden = me.isHierarchicallyHidden(),
            pendingShow = me.pendingShow;

        if (hidden !== hierarchicallyHidden) {
            if (hierarchicallyHidden) {
                me.hide();
                me.pendingShow = true;
            } else if (pendingShow) {
                delete me.pendingShow;
                if (pendingShow.length) {
                    me.show.apply(me, pendingShow);
                } else {
                    me.show();
                }
            }
        }
    },

    
    
    
    
    
    setZIndex: function(index) {
        var me = this;

        me.el.setZIndex(index);

        
        index += 10;

        
        
        if (me.floatingDescendants) {
            index = Math.floor(me.floatingDescendants.setBase(index) / 100) * 100 + 10000;
        }
        return index;
    },

    
    doConstrain: function(constrainTo) {
        var me = this,
            
            
            
            xy = me.calculateConstrainedPosition(constrainTo, null, true);

        
        if (xy) {
            me.setPosition(xy);
        }
    },

    
    toFront: function(preventFocus) {
        var me = this,
            zip = me.zIndexParent,
            preventFocusSetting = me.preventFocusOnActivate;

        
        
        if (zip && me.bringParentToFront !== false) {
            zip.toFront(true);
        }

        if (!Ext.isDefined(preventFocus)) {
            preventFocus = !me.focusOnToFront;
        }

        if (preventFocus) {
            me.preventFocusOnActivate = true;
        }
        if (me.zIndexManager.bringToFront(me, preventFocus)) {    
            if (!preventFocus) {
                
                
                
                me.focus(false, true);
            }
        }
        
        
        me.preventFocusOnActivate = preventFocusSetting;
        return me;
    },

    
    setActive: function(active, newActive) {
        var me = this;
        
        if (active) {
            if (me.el.shadow && !me.maximized) {
                me.el.enableShadow(true);
            }
            if (!me.preventFocusOnActivate) {
                me.focus(false, true);
            }
            me.fireEvent('activate', me);
        } else {
            
            
            if (me.isWindow && (newActive && newActive.isWindow) && me.hideShadowOnDeactivate) {
                me.el.disableShadow();
            }
            me.fireEvent('deactivate', me);
        }
    },

    
    toBack: function() {
        this.zIndexManager.sendToBack(this);
        return this;
    },

    
    center: function() {
        var me = this,
            xy;
            
        if (me.isVisible()) {
            xy = me.getAlignToXY(me.container, 'c-c');
            me.setPagePosition(xy);
        } else {
            me.needsCenter = true;
        }
        return me;
    },
    
    onFloatShow: function() {
        if (this.needsCenter) {
            this.center();    
        }
        delete this.needsCenter;
    },

    
    fitContainer: function(animate) {
        var me = this,
            parent = me.floatParent,
            container = parent ? parent.getTargetEl() : me.container,
            newBox = container.getViewSize(false),
            newPosition = parent || (container.dom !== document.body) ?
                
                [0, 0] :
                
                container.getXY();

        newBox.x = newPosition[0];
        newBox.y = newPosition[1];
        me.setBox(newBox, animate);
    }
});


Ext.define('Ext.Component', {

    

    alias: ['widget.component', 'widget.box'],

    extend:  Ext.AbstractComponent ,

           
                               
                    
                              
                                   
      

    mixins: {
        floating:  Ext.util.Floating 
    },

    statics: {
        
        DIRECTION_TOP: 'top',
        DIRECTION_RIGHT: 'right',
        DIRECTION_BOTTOM: 'bottom',
        DIRECTION_LEFT: 'left',

        VERTICAL_DIRECTION_Re: /^(?:top|bottom)$/,

        
        
        INVALID_ID_CHARS_Re: /[\.,\s]/g
    },

    

    

    
    resizeHandles: 'all',

    

    

    

    
    floating: false,
    
    
    defaultAlign: 'tl-bl?',

    
    toFrontOnShow: true,
    
    

    

    

    

    

    
    
    

    

    

    hideMode: 'display',
    
    offsetsCls: Ext.baseCSSPrefix + 'hide-offsets',

    bubbleEvents: [],

    defaultComponentLayoutType: 'autocomponent',

    
    
    
    
    
    

    
    constructor: function(config) {
        var me = this;

        config = config || {};
        if (config.initialConfig) {

            
            if (config.isAction) {
                me.baseAction = config;
            }
            config = config.initialConfig;
            
        }
        else if (config.tagName || config.dom || Ext.isString(config)) {
            
            config = {
                applyTo: config,
                id: config.id || config
            };
        }

        me.callParent([config]);

        
        
        if (me.baseAction){
            me.baseAction.addComponent(me);
        }
    },

    
    initComponent: function() {
        var me = this;

        me.callParent();

        if (me.listeners) {
            me.on(me.listeners);
            me.listeners = null; 
        }
        me.enableBubble(me.bubbleEvents);
    },

    afterRender: function() {
        var me = this;

        me.callParent();

        if (!(me.x && me.y) && (me.pageX || me.pageY)) {
            me.setPagePosition(me.pageX, me.pageY);
        }
    },

    
    setAutoScroll : function(scroll) {
        var me = this;

        me.autoScroll = !!scroll;

        
        
        
        if (me.rendered) {
            me.getOverflowEl().setStyle(me.getOverflowStyle());
        }
        me.updateLayout();
        return me;
    },

    
    setOverflowXY: function(overflowX, overflowY) {
        var me = this,
            argCount = arguments.length;

        if (argCount) {
            me.overflowX = overflowX || '';
            if (argCount > 1) {
                me.overflowY = overflowY || '';
            }
        }

        
        
        
        if (me.rendered) {
            me.getOverflowEl().setStyle(me.getOverflowStyle());
        }
        me.updateLayout();
        return me;
    },

    beforeRender: function () {
        var me = this,
            floating = me.floating,
            cls;

        if (floating) {
            me.addCls(Ext.baseCSSPrefix + 'layer');

            cls = floating.cls;
            if (cls) {
                me.addCls(cls);
            }
        }

        return me.callParent();
    },
    
    beforeLayout: function(){
        this.callParent(arguments);
        if (this.floating) {
            this.onBeforeFloatLayout();
        }    
    },
    
    afterComponentLayout: function(){
        this.callParent(arguments);
        if (this.floating) {
            this.onAfterFloatLayout();
        }
    },

    
    makeFloating : function (dom) {
        this.mixins.floating.constructor.call(this, dom);
    },

    wrapPrimaryEl: function (dom) {
        if (this.floating) {
            this.makeFloating(dom);
        } else {
            this.callParent(arguments);
        }
    },

    initResizable: function(resizable) {
        var me = this;

        resizable = Ext.apply({
            target: me,
            dynamic: false,
            constrainTo: me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : null),
            handles: me.resizeHandles
        }, resizable);
        resizable.target = me;
        me.resizer = new Ext.resizer.Resizer(resizable);
    },

    getDragEl: function() {
        return this.el;
    },

    initDraggable: function() {
        var me = this,

            
            
            
            dragTarget = (me.resizer && me.resizer.el !== me.el) ? me.resizerComponent = new Ext.Component({
                el: me.resizer.el,
                rendered: true,
                container: me.container
            }) : me,
            ddConfig = Ext.applyIf({
                el: dragTarget.getDragEl(),
                constrainTo: (me.constrain||me.draggable.constrain) ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.container)) : undefined
            }, me.draggable);

        
        if (me.constrain || me.constrainDelegate) {
            ddConfig.constrain = me.constrain;
            ddConfig.constrainDelegate = me.constrainDelegate;
        }

        me.dd = new Ext.util.ComponentDragger(dragTarget, ddConfig);
    },

    
    scrollBy: function(deltaX, deltaY, animate) {
        var el;

        if ((el = this.getTargetEl()) && el.dom) {
            el.scrollBy.apply(el, arguments);
        }
    },

    
    setLoading : function(load, targetEl) {
        var me = this,
            config = {
                target: me
            };

        if (me.rendered) {
            Ext.destroy(me.loadMask);
            me.loadMask = null;

            if (load !== false && !me.collapsed) {
                if (Ext.isObject(load)) {
                    Ext.apply(config, load);
                } else if (Ext.isString(load)) {
                    config.msg = load;
                }
                
                if (targetEl) {
                    Ext.applyIf(config, {
                        useTargetEl: true
                    });
                }
                me.loadMask = new Ext.LoadMask(config);
                me.loadMask.show();
            }
        }
        return me.loadMask;
    },

    beforeSetPosition: function () {
        var me = this,
            pos = me.callParent(arguments), 
            adj;

        if (pos) {
            adj = me.adjustPosition(pos.x, pos.y);
            pos.x = adj.x;
            pos.y = adj.y;
        }
        return pos || null;
    },

    afterSetPosition: function(ax, ay) {
        this.onPosition(ax, ay);
        this.fireEvent('move', this, ax, ay);
    },

    
    showAt: function(x, y, animate) {
        var me = this;

        
        
        if (!me.rendered && (me.autoRender || me.floating)) {
            me.x = x;
            me.y = y;
            return me.show();
        }
        if (me.floating) {
            me.setPosition(x, y, animate);
        } else {
            me.setPagePosition(x, y, animate);
        }
        me.show();
    },
    
    
    showBy: function(cmp, pos, off) {
        var me = this;
        

        if (me.floating && cmp) {
            me.show();

            
            if (me.rendered && !me.hidden) {
                
                
                
                me.alignTo(cmp, pos || me.defaultAlign, off);
            }
        }
        return me;
    },

    
    setPagePosition: function(x, y, animate) {
        var me = this,
            p,
            floatParentBox;

        if (Ext.isArray(x)) {
            y = x[1];
            x = x[0];
        }
        me.pageX = x;
        me.pageY = y;

        if (me.floating) {

            
            if (me.isContainedFloater()) {
                floatParentBox = me.floatParent.getTargetEl().getViewRegion();
                if (Ext.isNumber(x) && Ext.isNumber(floatParentBox.left)) {
                    x -= floatParentBox.left;
                }
                if (Ext.isNumber(y) && Ext.isNumber(floatParentBox.top)) {
                    y -= floatParentBox.top;
                }
            } else {
                p = me.el.translateXY(x, y);
                x = p.x;
                y = p.y;
            }

            me.setPosition(x, y, animate);
        } else {
            p = me.el.translateXY(x, y);
            me.setPosition(p.x, p.y, animate);
        }

        return me;
    },

    
    
    isContainedFloater: function() {
        return (this.floating && this.floatParent);
    },

    
    updateBox : function(box){
        this.setSize(box.width, box.height);
        this.setPagePosition(box.x, box.y);
        return this;
    },

    
    getOuterSize: function() {
        var el = this.el;
        return {
            width: el.getWidth() + el.getMargin('lr'),
            height: el.getHeight() + el.getMargin('tb')
        };
    },

    
    adjustPosition: function(x, y) {
        var me = this,
            floatParentBox;

        
        if (me.isContainedFloater()) {
            floatParentBox = me.floatParent.getTargetEl().getViewRegion();
            x += floatParentBox.left;
            y += floatParentBox.top;
        }

        return {
            x: x,
            y: y
        };
    },

    
    getPosition: function(local) {
        var me = this,
            xy,
            isContainedFloater = me.isContainedFloater(),
            floatParentBox;

        
        if ((local === true) && !isContainedFloater) {
            return [me.getLocalX(), me.getLocalY()];
        }

        xy = me.getXY();

        
        if ((local === true) && isContainedFloater) {
            floatParentBox = me.floatParent.getTargetEl().getViewRegion();
            xy[0] -= floatParentBox.left;
            xy[1] -= floatParentBox.top;
        }
        return xy;
    },

    getId: function() {
        var me = this,
            xtype;

        if (!me.id) {
            xtype = me.getXType();
            if (xtype) {
                xtype = xtype.replace(Ext.Component.INVALID_ID_CHARS_Re, '-');
            } else {
                xtype = Ext.name.toLowerCase() + '-comp';
            }
            me.id = xtype + '-' + me.getAutoId();
        }
        return me.id;
    },

    
    show: function(animateTarget, cb, scope) {
        var me = this,
            rendered = me.rendered;

        if (me.hierarchicallyHidden || (me.floating && !rendered && me.isHierarchicallyHidden())) {
            
            
            
            if (!rendered) {
                
                
                
                
                
                
                
                
                
                me.initHierarchyEvents();
            }
            
            if (arguments.length > 1) {
                arguments[0] = null;
                me.pendingShow = arguments;
            } else {
                me.pendingShow = true;
            }
        } else if (rendered && me.isVisible()) {
            if (me.toFrontOnShow && me.floating) {
                me.toFront();
            }
        } else {
            if (me.fireEvent('beforeshow', me) !== false) {
                me.hidden = false;
                delete this.getHierarchyState().hidden;
                
                
                
                
                
                
                
                
                
                Ext.suspendLayouts();
                if (!rendered && (me.autoRender || me.floating)) {
                    me.doAutoRender();
                    rendered = me.rendered;
                }
            
                if (rendered) {
                    me.beforeShow();
                    Ext.resumeLayouts();
                    me.onShow.apply(me, arguments);
                    me.afterShow.apply(me, arguments);
                } else {
                    Ext.resumeLayouts(true);
                }
            } else {
                me.onShowVeto();
            }
        }
        return me;
    },
    
    onShowVeto: Ext.emptyFn,

    
    beforeShow: Ext.emptyFn,

    
    onShow: function() {
        var me = this;

        me.el.show();
        me.callParent(arguments);

        
        if (me.floating) {
            if (me.maximized) {
                me.fitContainer();
            }
            else if (me.constrain) {
                me.doConstrain();
            }
        }
    },
    
    getAnimateTarget: function(target){
        target = target || this.animateTarget;
        if (target) {
            target = target.isComponent ? target.getEl() : Ext.get(target);
        }
        return target || null;
    },

    
    afterShow: function(animateTarget, cb, scope) {
        var me = this,
            myEl = me.el,
            fromBox,
            toBox,
            ghostPanel;

        
        animateTarget = me.getAnimateTarget(animateTarget);

        
        if (!me.ghost) {
            animateTarget = null;
        }
        
        if (animateTarget) {
            toBox = {
                x: myEl.getX(),
                y: myEl.getY(),
                width: myEl.dom.offsetWidth,
                height: myEl.dom.offsetHeight
            };
            fromBox = {
                x: animateTarget.getX(),
                y: animateTarget.getY(),
                width: animateTarget.dom.offsetWidth,
                height: animateTarget.dom.offsetHeight
            };
            myEl.addCls(me.offsetsCls);
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();

            
            ghostPanel.setX(-10000);

            me.ghostBox = toBox;
            ghostPanel.el.animate({
                from: fromBox,
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        me.unghost();
                        delete me.ghostBox;
                        myEl.removeCls(me.offsetsCls);
                        me.onShowComplete(cb, scope);
                    }
                }
            });
        }
        else {
            me.onShowComplete(cb, scope);
        }
        me.fireHierarchyEvent('show');
    },

    
    onShowComplete: function(cb, scope) {
        var me = this;
        if (me.floating) {
            me.toFront();
            me.onFloatShow();
        }
        Ext.callback(cb, scope || me);
        me.fireEvent('show', me);
        delete me.hiddenByLayout;
    },

    
    hide: function(animateTarget, cb, scope) {
        var me = this,
            continueHide;

        if (me.pendingShow) {
            
            
            delete me.pendingShow;
        } if (!(me.rendered && !me.isVisible())) {
            continueHide = (me.fireEvent('beforehide', me) !== false);
            if (me.hierarchicallyHidden || continueHide) {
                me.hidden = true;
                me.getHierarchyState().hidden = true;
                if (me.rendered) {
                    me.onHide.apply(me, arguments);
                }
            }
        }
        return me;
    },

    
    onHide: function(animateTarget, cb, scope) {
        var me = this,
            ghostPanel,
            fromSize,
            toBox;

        
        animateTarget = me.getAnimateTarget(animateTarget);

        
        if (!me.ghost) {
            animateTarget = null;
        }
        
        if (animateTarget) {
            toBox = {
                x: animateTarget.getX(),
                y: animateTarget.getY(),
                width: animateTarget.dom.offsetWidth,
                height: animateTarget.dom.offsetHeight
            };
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();
            fromSize = me.getSize();
            ghostPanel.el.animate({
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        ghostPanel.el.hide();
                        ghostPanel.el.setSize(fromSize);
                        me.afterHide(cb, scope);
                    }
                }
            });
        }
        me.el.hide();
        if (!animateTarget) {
            me.afterHide(cb, scope);
        }
    },

    
    afterHide: function(cb, scope) {
        var me = this,
            activeEl = Ext.Element.getActiveElement();

        me.hiddenByLayout = null;

        
        
        Ext.AbstractComponent.prototype.onHide.call(me);

        
        if (activeEl === me.el || me.el.contains(activeEl)) {
            Ext.fly(activeEl).blur();
        }

        Ext.callback(cb, scope || me);
        me.fireEvent('hide', me);
        me.fireHierarchyEvent('hide');
    },

    
    onDestroy: function() {
        var me = this;

        
        if (me.rendered) {
            Ext.destroy(
                me.dd,
                me.resizer,
                me.proxy,
                me.proxyWrap,
                me.resizerComponent
            );
        }
        delete me.focusTask;
        me.callParent();
    },

    deleteMembers: function() {
        var args = arguments,
            len = args.length,
            i = 0;
        for (; i < len; ++i) {
            delete this[args[i]];
        }
    },

    
    focus: function(selectText, delay, callback, scope) {
        var me = this,
            focusEl,
            focusElDom,
            containerScrollTop;

        
        if (delay) {
            if (!me.focusTask) {
                
                
                Ext.Component.prototype.focusTask = new Ext.util.DelayedTask(me.focus);
            }
            me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [selectText, false, callback, scope]);
            return me;
        }

        
        if (me.focusTask) {
            me.focusTask.cancel();
        }

        if (me.rendered && !me.isDestroyed && me.isVisible(true) && (focusEl = me.getFocusEl())) {

            
            
            if (focusEl.isComponent) {
                return focusEl.focus(selectText, delay);
            }

            
            if ((focusElDom = focusEl.dom)) {

                
                if (focusEl.needsTabIndex()) {
                    focusElDom.tabIndex = -1;
                }

                if (me.floating) {
                    containerScrollTop = me.container.dom.scrollTop;
                }

                
                
                
                focusEl.focus();
                if (selectText === true) {
                    focusElDom.select();
                }

                
                Ext.callback(callback, scope);
            }

            
            
            if (me.floating) {
                me.toFront(true);
                if (containerScrollTop !== undefined) {
                    me.container.dom.scrollTop = containerScrollTop;
                }
            }
        }
        return me;
    },

    
    cancelFocus: function() {
        var task = this.focusTask;
        if (task) {
            task.cancel();
        }
    },

    
    blur: function() {
        var focusEl;
        if (this.rendered && (focusEl = this.getFocusEl())) {
            focusEl.blur();
        }
        return this;
    },

    getEl: function() {
        return this.el;
    },

    
    getResizeEl: function() {
        return this.el;
    },

    
    getPositionEl: function() {
        return this.el;
    },

    
    getActionEl: function() {
        return this.el;
    },

    
    getVisibilityEl: function() {
        return this.el;
    },

    
    getRefOwner: function() {
        return this.ownerCt || this.floatParent;
    },

    
    getBubbleTarget: function() {
        return this.getRefOwner();
    },

    
    getContentTarget: function() {
        return this.el;
    },

    
    cloneConfig: function(overrides) {
        overrides = overrides || {};
        var id = overrides.id || Ext.id(),
            cfg = Ext.applyIf(overrides, this.initialConfig),
            self;

        cfg.id = id;

        self = Ext.getClass(this);

        
        return new self(cfg);
    },

    
    getXType: function() {
        return this.self.xtype;
    },

    
    findParentBy: function(fn) {
        var p;

        
        for (p = this.getBubbleTarget(); p && !fn(p, this); p = p.getBubbleTarget()) {
            
        }
        return p || null;
    },

    
    findParentByType: function(xtype) {
        return Ext.isFunction(xtype) ?
            this.findParentBy(function(p) {
                return p.constructor === xtype;
            })
        :
            this.up(xtype);
    },

    
    bubble: function(fn, scope, args) {
        var p = this;
        while (p) {
            if (fn.apply(scope || p, args || [p]) === false) {
                break;
            }
            p = p.getBubbleTarget();
        }
        return this;
    },

    getProxy: function() {
        var me = this,
            target;

        if (!me.proxy) {
            target = Ext.getBody();
            me.proxy = me.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', target, true);
        }
        return me.proxy;
    },

    
    fireHierarchyEvent: function (ename) {
        this.hierarchyEventSource.fireEvent(ename, this);
    },

    onAdded: function() {
        this.callParent(arguments);
        if (this.hierarchyEventSource.hasListeners.added) {
            this.fireHierarchyEvent('added');
        }
    }
}, function () {
    
    this.hierarchyEventSource = this.prototype.hierarchyEventSource = new Ext.util.Observable({ events: {
        hide: true,
        show: true,
        collapse: true,
        expand: true,
        added: true
    }});
});


Ext.define('Ext.layout.container.border.Region', {
    override: 'Ext.Component',

    
    initBorderRegion: function () {
        var me = this;

        if (!me._borderRegionInited) {
            me._borderRegionInited = true;

            
            
            me.addStateEvents(['changeregion', 'changeweight']);

            
            
            
            
            Ext.override(me, {
                getState: function () {
                    var state = me.callParent();

                    
                    
                    state = me.addPropertyToState(state, 'region');
                    state = me.addPropertyToState(state, 'weight');

                    return state;
                }
            });
        }
    },

    
    getOwningBorderContainer: function () {
        var layout = this.getOwningBorderLayout();
        return layout && layout.owner;
    },

    
    getOwningBorderLayout: function () {
        
        var layout = this.ownerLayout;
        return (layout && layout.isBorderLayout) ? layout : null;
    },

    
    setBorderRegion: function (region) {
        var me = this,
            borderLayout,
            old = me.region;

        if (region !== old) {
            borderLayout = me.getOwningBorderLayout();
            if (borderLayout) {
                var regionFlags = borderLayout.regionFlags[region],
                    placeholder = me.placeholder,
                    splitter = me.splitter,
                    owner = borderLayout.owner,
                    regionMeta = borderLayout.regionMeta,
                    collapsed = me.collapsed || me.floated,
                    delta, items, index;

                if (me.fireEventArgs('beforechangeregion', [me, region]) === false) {
                    return old;
                }
                Ext.suspendLayouts();

                me.region = region;
                Ext.apply(me, regionFlags);

                if (me.updateCollapseTool) {
                    me.updateCollapseTool();
                }

                if (splitter) {
                    
                    Ext.apply(splitter, regionFlags);
                    splitter.updateOrientation();

                    items = owner.items;
                    index = items.indexOf(me);
                    if (index >= 0) {
                        delta = regionMeta[region].splitterDelta;
                        if (items.getAt(index + delta) !== splitter) {
                            
                            items.remove(splitter);
                            index = items.indexOf(me);  
                            if (delta > 0) {
                                ++index;
                            }
                            
                            items.insert(index, splitter);

                            
                            
                            
                        }
                    }
                }
                if (placeholder) {
                    
                    
                    
                    
                    if (collapsed) {
                        me.expand(false);
                    }

                    owner.remove(placeholder);
                    me.placeholder = null; 

                    if (collapsed) {
                        me.collapse(null, false);
                    }
                }

                owner.updateLayout();
                Ext.resumeLayouts(true);

                me.fireEventArgs('changeregion', [me, old]);
            } else {
                me.region = region; 
            }
        }

        return old;
    },

    
    setRegionWeight: function (weight) {
        var me = this,
            ownerCt = me.getOwningBorderContainer(),
            placeholder = me.placeholder,
            old = me.weight;

        if (weight !== old) {
            if (me.fireEventArgs('beforechangeweight', [me, weight]) !== false) {
                me.weight = weight;
                if (placeholder) {
                    placeholder.weight = weight;
                }
                if (ownerCt) {
                    ownerCt.updateLayout();
                }
                me.fireEventArgs('changeweight', [me, old]);
            }
        }

        return old;
    }
});


Ext.define('Ext.ElementLoader', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

           
                              
                  
      

    statics: {
        Renderer: {
            Html: function(loader, response, active){
                loader.getTarget().update(response.responseText, active.scripts === true);
                return true;
            }
        }
    },

    

    
    url: null,

    
    params: null,

    
    baseParams: null,

    
    autoLoad: false,

    
    target: null,

    
    loadMask: false,

    
    ajaxOptions: null,

    
    scripts: false,

    

    

    

    

    

    
    isLoader: true,

    constructor: function(config) {
        var me = this,
            autoLoad;

        config = config || {};
        Ext.apply(me, config);
        me.setTarget(me.target);
        me.addEvents(
            
            'beforeload',

            
            'exception',

            
            'load'
        );

        
        me.mixins.observable.constructor.call(me);

        if (me.autoLoad) {
            autoLoad = me.autoLoad;
            if (autoLoad === true) {
                autoLoad = {};
            }
            me.load(autoLoad);
        }
    },

    
    setTarget: function(target){
        var me = this;
        target = Ext.get(target);
        if (me.target && me.target != target) {
            me.abort();
        }
        me.target = target;
    },

    
    getTarget: function(){
        return this.target || null;
    },

    
    abort: function(){
        var active = this.active;
        if (active !== undefined) {
            Ext.Ajax.abort(active.request);
            if (active.mask) {
                this.removeMask();
            }
            delete this.active;
        }
    },

    
    removeMask: function(){
        this.target.unmask();
    },

    
    addMask: function(mask){
        this.target.mask(mask === true ? null : mask);
    },

    
    load: function(options) {

        options = Ext.apply({}, options);

        var me = this,
            mask = Ext.isDefined(options.loadMask) ? options.loadMask : me.loadMask,
            params = Ext.apply({}, options.params),
            ajaxOptions = Ext.apply({}, options.ajaxOptions),
            callback = options.callback || me.callback,
            scope = options.scope || me.scope || me;

        Ext.applyIf(ajaxOptions, me.ajaxOptions);
        Ext.applyIf(options, ajaxOptions);

        Ext.applyIf(params, me.params);
        Ext.apply(params, me.baseParams);

        Ext.applyIf(options, {
            url: me.url
        });


        Ext.apply(options, {
            scope: me,
            params: params,
            callback: me.onComplete
        });

        if (me.fireEvent('beforeload', me, options) === false) {
            return;
        }

        if (mask) {
            me.addMask(mask);
        }

        me.active = {
            options: options,
            mask: mask,
            scope: scope,
            callback: callback,
            success: options.success || me.success,
            failure: options.failure || me.failure,
            renderer: options.renderer || me.renderer,
            scripts: Ext.isDefined(options.scripts) ? options.scripts : me.scripts
        };
        me.active.request = Ext.Ajax.request(options);
        me.setOptions(me.active, options);
    },

    
    setOptions: Ext.emptyFn,

    
    onComplete: function(options, success, response) {
        var me = this,
            active = me.active,
            scope;

        if (active) {
            scope = active.scope;
            if (success) {
                success = me.getRenderer(active.renderer).call(me, me, response, active) !== false;
            }

            if (success) {
                Ext.callback(active.success, scope, [me, response, options]);
                me.fireEvent('load', me, response, options);
            } else {
                Ext.callback(active.failure, scope, [me, response, options]);
                me.fireEvent('exception', me, response, options);
            }
            Ext.callback(active.callback, scope, [me, success, response, options]);
            if (active.mask) {
                me.removeMask();
            }
        }

        delete me.active;
    },

    
    getRenderer: function(renderer){
        if (Ext.isFunction(renderer)) {
            return renderer;
        }
        return this.statics().Renderer.Html;
    },

    
    startAutoRefresh: function(interval, options){
        var me = this;
        me.stopAutoRefresh();
        me.autoRefresh = setInterval(function(){
            me.load(options);
        }, interval);
    },

    
    stopAutoRefresh: function(){
        clearInterval(this.autoRefresh);
        delete this.autoRefresh;
    },

    
    isAutoRefreshing: function(){
        return Ext.isDefined(this.autoRefresh);
    },

    
    destroy: function(){
        var me = this;
        me.stopAutoRefresh();
        delete me.target;
        me.abort();
        me.clearListeners();
    }
});


Ext.define('Ext.ComponentLoader', {

    

    extend:  Ext.ElementLoader ,

    statics: {
        Renderer: {
            Data: function(loader, response, active){
                var success = true;
                try {
                    loader.getTarget().update(Ext.decode(response.responseText));
                } catch (e) {
                    success = false;
                }
                return success;
            },

            Component: function(loader, response, active){
                var success = true,
                    target = loader.getTarget(),
                    items = [];


                try {
                    items = Ext.decode(response.responseText);
                } catch (e) {
                    success = false;
                }

                if (success) {
                    target.suspendLayouts();
                    if (active.removeAll) {
                        target.removeAll();
                    }
                    target.add(items);
                    target.resumeLayouts(true);
                }
                return success;
            }
        }
    },

    

    
    target: null,

    
    loadMask: false,

    

    
    renderer: 'html',

    
    setTarget: function(target){
        var me = this;

        if (Ext.isString(target)) {
            target = Ext.getCmp(target);
        }

        if (me.target && me.target != target) {
            me.abort();
        }
        me.target = target;
    },

    
    removeMask: function(){
        this.target.setLoading(false);
    },

    
    addMask: function(mask){
        this.target.setLoading(mask);
    },


    setOptions: function(active, options){
        active.removeAll = Ext.isDefined(options.removeAll) ? options.removeAll : this.removeAll;
    },

    
    getRenderer: function(renderer){
        if (Ext.isFunction(renderer)) {
            return renderer;
        }

        var renderers = this.statics().Renderer;
        switch (renderer) {
            case 'component':
                return renderers.Component;
            case 'data':
                return renderers.Data;
            default:
                return Ext.ElementLoader.Renderer.Html;
        }
    }
});


Ext.define('Ext.layout.SizeModel', {
    constructor: function (config) {
        var me = this,
            SizeModel = me.self,
            sizeModelsArray = SizeModel.sizeModelsArray,
            name;

        Ext.apply(me, config);

        me[name = me.name] = true; 

        me.fixed = !(me.auto = me.natural || me.shrinkWrap);

        
        sizeModelsArray[me.ordinal = sizeModelsArray.length] =
            SizeModel[name] =
            SizeModel.sizeModels[name] = me;
    },

    statics: {
        
        sizeModelsArray: [],

        
        sizeModels: {}
    },

    

    

    
    calculated: false,

    
    configured: false,

    
    constrainedMax: false,

    
    constrainedMin: false,

    

    
    natural: false,

    
    shrinkWrap: false,

    
    calculatedFromConfigured: false,

    
    calculatedFromNatural: false,

    
    calculatedFromShrinkWrap: false,

    
    names: null
},
function () {
    var SizeModel = this,
        sizeModelsArray = SizeModel.sizeModelsArray,
        i, j, n, pairs, sizeModel;

    
    

    new SizeModel({
        name: 'calculated'
    });

    new SizeModel({
        name: 'configured',
        names: { width: 'width', height: 'height' }
    });

    new SizeModel({
        name: 'natural'
    });

    new SizeModel({
        name: 'shrinkWrap'
    });

    
    
    

    new SizeModel({
        name: 'calculatedFromConfigured',
        configured: true,
        names: { width: 'width', height: 'height' }
    });

    new SizeModel({
        name: 'calculatedFromNatural',
        natural: true
    });

    new SizeModel({
        name: 'calculatedFromShrinkWrap',
        shrinkWrap: true
    });

    new SizeModel({
        name: 'constrainedMax',
        configured: true,
        constrained: true,
        names: { width: 'maxWidth', height: 'maxHeight' }
    });

    new SizeModel({
        name: 'constrainedMin',
        configured: true,
        constrained: true,
        names: { width: 'minWidth', height: 'minHeight' }
    });

    new SizeModel({
        name: 'constrainedDock',
        configured: true,
        constrained: true,
        constrainedByMin: true,
        names: { width: 'dockConstrainedWidth', height: 'dockConstrainedHeight' }
    });

    for (i = 0, n = sizeModelsArray.length; i < n; ++i) {
        sizeModel = sizeModelsArray[i];

        
        sizeModel.pairsByHeightOrdinal = pairs = [];

        for (j = 0; j < n; ++j) {
            pairs.push({
                width: sizeModel,
                height: sizeModelsArray[j]
            });
        }
    }
});


Ext.define('Ext.layout.Layout', {
               
                        
                              
      

                                   

    
    isLayout: true,
    initialized: false,
    running: false,

    autoSizePolicy: {
        readsWidth: 1,
        readsHeight: 1,
        setsWidth: 0,
        setsHeight: 0
    },

    statics: {
        layoutsByType: {},

        create: function(layout, defaultType) {
            var ClassManager = Ext.ClassManager,
                layoutsByType = this.layoutsByType,
                alias, className, config, layoutClass, type, load;

            if (!layout || typeof layout === 'string') {
                type = layout || defaultType;
                config = {};                    
            } else if (layout.isLayout) {
                return layout;
            } else {
                config = layout;
                type = layout.type || defaultType;
            }

            if (!(layoutClass = layoutsByType[type])) {
                alias = 'layout.' + type;
                className = ClassManager.getNameByAlias(alias);

                
                if (!className) {
                    load = true;
                }
                
                layoutClass = ClassManager.get(className);
                if (load || !layoutClass) {
                    return ClassManager.instantiateByAlias(alias, config || {});
                }
                layoutsByType[type] = layoutClass;
            }

            return new layoutClass(config);
        }
    },

    constructor : function(config) {
        var me = this;

        me.id = Ext.id(null, me.type + '-');
        Ext.apply(me, config);
        me.layoutCount = 0;
    },

    

    
    beginLayout: Ext.emptyFn,

    
    beginLayoutCycle: function (ownerContext) {
        var me = this,
            context = me.context,
            changed;

        if (me.lastWidthModel != ownerContext.widthModel) {
            if (me.lastWidthModel) {
                changed = true;
            }
            me.lastWidthModel = ownerContext.widthModel;
        }

        if (me.lastHeightModel != ownerContext.heightModel) {
            if (me.lastWidthModel) {
                changed = true;
            }
            me.lastHeightModel = ownerContext.heightModel;
        }

        if (changed) {
            (context = ownerContext.context).clearTriggers(me, false);
            context.clearTriggers(me, true);
            me.triggerCount = 0;
        }
    },

    

    

    

    
    finishedLayout: function (ownerContext) {
        this.lastWidthModel = ownerContext.widthModel;
        this.lastHeightModel = ownerContext.heightModel;
        this.ownerContext = null;
    },
    
    
    
    redoLayout: Ext.emptyFn,
    undoLayout: Ext.emptyFn,

    getAnimatePolicy: function() {
        return this.animatePolicy;
    },

    
    getItemSizePolicy: function (item) {
        return this.autoSizePolicy;
    },

    isItemBoxParent: function (itemContext) {
        return false;
    },

    isItemLayoutRoot: function (item) {
        var sizeModel = item.getSizeModel(),
            width = sizeModel.width,
            height = sizeModel.height;

        
        
        if (!item.componentLayout.lastComponentSize && (width.calculated || height.calculated)) {
            return false;
        }

        
        return !width.shrinkWrap && !height.shrinkWrap;
    },

    isItemShrinkWrap: function (item) {
        return item.shrinkWrap;
    },

    isRunning: function () {
        return !!this.ownerContext;
    },

    
    
    

    getItemsRenderTree: function (items, renderCfgs) {
        var length = items.length,
            i, item, itemConfig, result;

        if (length) {
            result = [];
            for (i = 0; i < length; ++i) {
                item = items[i];

                
                
                if (!item.rendered) {

                    
                    
                    
                    
                    
                    if (renderCfgs && (renderCfgs[item.id] !== undefined)) {
                        itemConfig = renderCfgs[item.id];
                    } else {
                        
                        this.configureItem(item);
                        itemConfig = item.getRenderTree();
                        if (renderCfgs) {
                            renderCfgs[item.id] = itemConfig;
                        }
                    }

                    
                    if (itemConfig) {
                        result.push(itemConfig);
                    }
                }
            }
        }

        return result;
    },

    finishRender: Ext.emptyFn,

    finishRenderItems: function (target, items) {
        var length = items.length,
            i, item;

        for (i = 0; i < length; i++) {
            item = items[i];

            
            if (item.rendering) {

                
                item.finishRender(i);

                this.afterRenderItem(item);
            }
        }
    },

    renderChildren: function () {
        var me = this,
            items = me.getLayoutItems(),
            target = me.getRenderTarget();

        me.renderItems(items, target);
    },

    
    renderItems : function(items, target) {
        var me = this,
            ln = items.length,
            i = 0,
            item;

        if (ln) {
            Ext.suspendLayouts();
            for (; i < ln; i++) {
                item = items[i];
                if (item && !item.rendered) {
                    me.renderItem(item, target, i);
                } else if (!me.isValidParent(item, target, i)) {
                    me.moveItem(item, target, i);
                } else {
                    
                    me.configureItem(item);
                }
            }
            Ext.resumeLayouts(true);
        }
    },

    
    isValidParent : function(item, target, position) {
        var itemDom = item.el ? item.el.dom : Ext.getDom(item),
            targetDom = (target && target.dom) || target,
            parentNode = itemDom.parentNode,
            className;

        
        if (parentNode) {
            className = parentNode.className;
            if (className && className.indexOf(Ext.baseCSSPrefix + 'resizable-wrap') !== -1) {
                itemDom = itemDom.parentNode;
            }
        }

        
        if (itemDom && targetDom) {
            if (typeof position == 'number') {
                position = this.getPositionOffset(position);
                return itemDom === targetDom.childNodes[position];
            }
            return itemDom.parentNode === targetDom;
        }

        return false;
    },
    
    getPositionOffset: function(position){
        return position;
    },

    
    configureItem: function(item) {
        item.ownerLayout = this;
    },

    
    renderItem : function(item, target, position) {
        var me = this;
        if (!item.rendered) {
            me.configureItem(item);
            item.render(target, position);
            me.afterRenderItem(item);
        }
    },

    
    moveItem : function(item, target, position) {
        target = target.dom || target;
        if (typeof position == 'number') {
            position = target.childNodes[position];
        }
        target.insertBefore(item.el.dom, position || null);
        item.container = Ext.get(target);
        this.configureItem(item);
    },

    
    onContentChange: function () {
        this.owner.updateLayout();
        return true;
    },

    
    initLayout : function() {
        this.initialized = true;
    },

    
    setOwner : function(owner) {
        this.owner = owner;
    },

    
    getLayoutItems : function() {
        return [];
    },

    onAdd: function (item) {
        item.ownerLayout = this;
    },
    afterRenderItem: Ext.emptyFn,
    onRemove : Ext.emptyFn,
    onDestroy : Ext.emptyFn,

    
    afterRemove : function(item) {
        var me = this,
            el = item.el,
            owner = me.owner,
            removeClasses;

        if (item.rendered) {
            removeClasses = [].concat(me.itemCls || []);
            if (owner.itemCls) {
                removeClasses = Ext.Array.push(removeClasses, owner.itemCls);
            }
            if (removeClasses.length) {
                el.removeCls(removeClasses);
            }
        }

        delete item.ownerLayout;
    },

    
    destroy : function() {
        var me = this,
            target;

        if (me.targetCls) {
            target = me.getTarget();
            if (target) {
                target.removeCls(me.targetCls);
            }
        }

        me.onDestroy();
    },

    sortWeightedItems: function (items, reverseProp) {
        for (var i = 0, length = items.length; i < length; ++i) {
            items[i].$i = i;
        }

        Ext.Array.sort(items, function (item1, item2) {
            var ret = item2.weight - item1.weight;

            if (!ret) {
                ret = item1.$i - item2.$i;
                if (item1[reverseProp]) {
                    ret = -ret;
                }
            }

            return ret;
        });

        for (i = 0; i < length; ++i) {
            delete items[i].$i;
        }
    }
}, function () {
    var Layout = this;

    Layout.prototype.sizeModels = Layout.sizeModels = Ext.layout.SizeModel.sizeModels;
});


Ext.define('Ext.layout.container.Container', {

    

    alias: ['layout.container'],

    extend:  Ext.layout.Layout ,

    alternateClassName: 'Ext.layout.ContainerLayout',

    mixins: {
        elementCt:  Ext.util.ElementContainer 
    },

               
                       
      

    type: 'container',

    

    

    
    beginCollapse: Ext.emptyFn,

    
    beginExpand: Ext.emptyFn,

    
    animatePolicy: null,

    childEls: [
        
        'overflowPadderEl'
    ],

    renderTpl: [
        '{%this.renderBody(out,values)%}'
    ],

    usesContainerHeight: true,
    usesContainerWidth: true,
    usesHeight: true,
    usesWidth: true,

    constructor: function () {
        this.callParent(arguments);
        this.mixins.elementCt.constructor.call(this);
    },

    destroy : function() {
        this.callParent();
        this.mixins.elementCt.destroy.call(this);
    },

    
    beginLayout: function (ownerContext) {
        this.callParent(arguments);

        ownerContext.targetContext = ownerContext.paddingContext = ownerContext.getEl('getTarget', this);

        this.cacheChildItems(ownerContext);
    },

    beginLayoutCycle: function (ownerContext, firstCycle) {
        var me = this;

        me.callParent(arguments);

        if (firstCycle) {
            if (me.usesContainerHeight) {
                ++ownerContext.consumersContainerHeight;
            }
            if (me.usesContainerWidth) {
                ++ownerContext.consumersContainerWidth;
            }
        }
    },

    cacheChildItems: function (ownerContext) {
        var context = ownerContext.context,
            childItems = [],
            items = this.getVisibleItems(),
            length = items.length,
            i;

        ownerContext.childItems = childItems;
        ownerContext.visibleItems = items;

        for (i = 0; i < length; ++i) {
            childItems.push(context.getCmp(items[i]));
        }
    },

    cacheElements: function () {
        var owner = this.owner;

        this.applyChildEls(owner.el, owner.id); 
    },

    
    configureItem: function(item) {
        var me = this,
            itemCls = me.itemCls,
            ownerItemCls = me.owner.itemCls,
            addClasses;

        
        item.ownerLayout = me;

        if (itemCls) {
            
            addClasses = typeof itemCls === 'string' ? [itemCls] : itemCls;
        }
        if (ownerItemCls) {
            addClasses = Ext.Array.push(addClasses||[], ownerItemCls);
        }
        if (addClasses) {
            item.addCls(addClasses);
        }
    },

    doRenderBody: function (out, renderData) {
        
        

        this.renderItems(out, renderData);
        this.renderContent(out, renderData);
    },

    doRenderContainer: function (out, renderData) {
        
        

        var me = renderData.$comp.layout,
            tpl = me.getRenderTpl(),
            data = me.getRenderData();

        tpl.applyOut(data, out);
    },

    doRenderItems: function (out, renderData) {
        
        

        var me = renderData.$layout,
            tree = me.getRenderTree();

        if (tree) {
            Ext.DomHelper.generateMarkup(tree, out);
        }
    },

    finishRender: function () {
        var me = this,
            target, items;

        me.callParent();

        me.cacheElements();

        target = me.getRenderTarget();
        items = me.getLayoutItems();


        me.finishRenderItems(target, items);
    },

    
    notifyOwner: function() {
        this.owner.afterLayout(this);
    },

    
    getContainerSize : function(ownerContext, inDom) {
        
        
        
        
        

        var targetContext = ownerContext.targetContext,
            frameInfo = targetContext.getFrameInfo(),
            padding = ownerContext.paddingContext.getPaddingInfo(),
            got = 0,
            needed = 0,
            gotWidth, gotHeight, width, height;

        
        
        

        
        
        

        if (!ownerContext.widthModel.shrinkWrap) {
            ++needed;
            width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width');
            gotWidth = (typeof width == 'number');
            if (gotWidth) {
                ++got;
                width -= frameInfo.width + padding.width;
                if (width < 0) {
                    width = 0;
                }
            }
        }

        if (!ownerContext.heightModel.shrinkWrap) {
            ++needed;
            height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height');
            gotHeight = (typeof height == 'number');
            if (gotHeight) {
                ++got;
                height -= frameInfo.height + padding.height;
                if (height < 0) {
                    height = 0;
                }
            }
        }

        return {
            width: width,
            height: height,
            needed: needed,
            got: got,
            gotAll: got == needed,
            gotWidth: gotWidth,
            gotHeight: gotHeight
        };
    },
    
    
    
    
    
    
    
    
    
    getPositionOffset: function(position) {
        if (!this.createsInnerCt) {
            var offset = this.owner.itemNodeOffset;
            if (offset) {
                position += offset;
            }
        }
        return position;
    },

    
    getLayoutItems: function() {
        var owner = this.owner,
            items = owner && owner.items;

        return (items && items.items) || [];
    },

    getRenderData: function () {
        var comp = this.owner;

        return {
            $comp: comp,
            $layout: this,
            ownerId: comp.id
        };
    },

    
    getRenderedItems: function() {
        var me = this,
            target = me.getRenderTarget(),
            items = me.getLayoutItems(),
            ln = items.length,
            renderedItems = [],
            i, item;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.rendered && me.isValidParent(item, target, i)) {
                renderedItems.push(item);
            }
        }

        return renderedItems;
    },

    
    getRenderTarget: function() {
        return this.owner.getTargetEl();
    },

    
    getElementTarget: function() {
        return this.getRenderTarget();
    },

    getRenderTpl: function () {
        var me = this,
            renderTpl = Ext.XTemplate.getTpl(this, 'renderTpl');

        
        
        if (!renderTpl.renderContent) {
            me.owner.setupRenderTpl(renderTpl);
        }

        return renderTpl;
    },

    getRenderTree: function () {
        var result,
            items = this.owner.items,
            itemsGen,
            renderCfgs = {};
        
        do {
            itemsGen = items.generation;
            result = this.getItemsRenderTree(this.getLayoutItems(), renderCfgs);
        } while (items.generation !== itemsGen);
        return result;
    },
    
    renderChildren: function () {
        var me = this,
            ownerItems = me.owner.items,
            target = me.getRenderTarget(),
            itemsGen, items;
            
        
        
        
        do {
            itemsGen = ownerItems.generation;
            items = me.getLayoutItems();
            me.renderItems(items, target);
        } while (ownerItems.generation !== itemsGen);
    },

    getScrollbarsNeeded: function (width, height, contentWidth, contentHeight) {
        var scrollbarSize = Ext.getScrollbarSize(),
            hasWidth = typeof width == 'number',
            hasHeight = typeof height == 'number',
            needHorz = 0,
            needVert = 0;

        
        if (!scrollbarSize.width) {
            return 0;
        }
        if (hasHeight && height < contentHeight) {
            needVert = 2;
            width -= scrollbarSize.width;
        }

        if (hasWidth && width < contentWidth) {
            needHorz = 1;
            if (!needVert && hasHeight) {
                height -= scrollbarSize.height;
                if (height < contentHeight) {
                    needVert = 2;
                }
            }
        }

        return needVert + needHorz;
    },

    
    getTarget: function() {
        return this.owner.getTargetEl();
    },

    
    getVisibleItems: function() {
        var target   = this.getRenderTarget(),
            items = this.getLayoutItems(),
            ln = items.length,
            visibleItems = [],
            i, item;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.rendered && this.isValidParent(item, target, i) && item.hidden !== true) {
                visibleItems.push(item);
            }
        }

        return visibleItems;
    },

    setupRenderTpl: function (renderTpl) {
        var me = this;

        renderTpl.renderBody = me.doRenderBody;
        renderTpl.renderContainer = me.doRenderContainer;
        renderTpl.renderItems = me.doRenderItems;
    },
    
    getContentTarget: function(){
        return this.owner.getDefaultContentTarget();
    }

});


Ext.define('Ext.layout.container.Auto', {

    

    alias: ['layout.auto', 'layout.autocontainer'],

    extend:  Ext.layout.container.Container ,

    

    type: 'autocontainer',

    childEls: [
        'outerCt',
        'innerCt',
        'clearEl'
    ],

    
    reserveScrollbar: false,

    
    managePadding: true,

    
    manageOverflow: false,

    
    lastOverflowAdjust: {
        width: 0,
        height: 0
    },

    
    
    
    
    
    
    
    
    
    
    
    
    renderTpl: [
        '{% if (!(Ext.isIEQuirks || Ext.isIE7m)) { %}',
            
            
            
            
            
            
            
            '<span id="{ownerId}-outerCt" style="display:table;">',
                
                
                '<div id="{ownerId}-innerCt" style="display:table-cell;height:100%;',
                'vertical-align:top;{%this.renderPadding(out, values)%}" class="{innerCtCls}">',
                    '{%this.renderBody(out,values)%}',
                '</div>',
            '</span>',
        '{% } else if (values.shrinkWrapWidth) { %}',
            
            
            
            '<table id="{ownerId}-outerCt" class="' + Ext.plainTableCls + '">',
                '<tr>',
                    '<td id="{ownerId}-innerCt" style="vertical-align:top;padding:0;',
                        '{%this.renderPadding(out, values)%}" class="{innerCtCls}">',
                        '{%this.renderBody(out,values)%}',
                         
                        '<div id="{ownerId}-clearEl" class="', Ext.baseCSSPrefix,  'clear"',
                            'role="presentation"></div>',
                    '</td>',
                '</tr>',
            '</table>',
        '{% } else { %}',
            
            
            
            
            
            
            '<div id="{ownerId}-outerCt" style="zoom:1;{%this.renderPadding(out, values)%}">',
                '<div id="{ownerId}-innerCt" style="zoom:1;height:100%;" class="{innerCtCls}">',
                    '{%this.renderBody(out,values)%}',
                     
                    '<div id="{ownerId}-clearEl" class="', Ext.baseCSSPrefix,  'clear"',
                        'role="presentation"></div>',
                '</div>',
            '</div>',
            
            '{% values.$layout.isShrinkWrapTpl = false %}',
        '{% } %}'
    ],

    
    
    
    
    tableTpl: [
        '<table id="{ownerId}-outerCt" class="' + Ext.plainTableCls + '">',
            '<tr>',
                '<td id="{ownerId}-innerCt" style="vertical-align:top;padding:0;',
                    '{%this.renderPadding(out, values)%}" class="{innerCtCls}">',
                '</td>',
            '</tr>',
        '</table>'
    ],

    isShrinkWrapTpl: true,


    beginLayout: function(ownerContext) {
        var me = this,
            bottomPadding, overflowYStyle, overflowXStyle, needsTable;
        
        me.callParent(arguments);

        me.initContextItems(ownerContext);

        if (!me.isShrinkWrapTpl) {
            
            
            
            
            if (ownerContext.widthModel.shrinkWrap) {
                needsTable = true;
            }

            
            
            if (Ext.isStrict && Ext.isIE7) {
                overflowXStyle = me.getOverflowXStyle(ownerContext);
                if ((overflowXStyle === 'auto' || overflowXStyle === 'scroll') &&
                    ownerContext.paddingContext.getPaddingInfo().right) {
                    needsTable = true;
                }
            }

            if (needsTable) {
                me.insertTableCt(ownerContext);
            }
        }

        
        
        
        if (!me.isShrinkWrapTpl && Ext.isIE7 && Ext.isStrict && !me.clearElHasPadding) {
             bottomPadding = ownerContext.paddingContext.getPaddingInfo().bottom;
             overflowYStyle = me.getOverflowYStyle(ownerContext);
             if (bottomPadding && (overflowYStyle === 'auto' || overflowYStyle === 'scroll')) {
                 me.clearEl.setStyle('height', bottomPadding);
                 me.clearElHasPadding = true;
             }
        }
    },
    
    beforeLayoutCycle: function(ownerContext){
        var comp = this.owner,
            hierarchyState = comp.hierarchyState,
            hierarchyStateInner = comp.hierarchyStateInner;

        if (!hierarchyState || hierarchyState.invalid) {
            hierarchyState = comp.getHierarchyState(); 
            hierarchyStateInner = comp.hierarchyStateInner;
        }
        if (ownerContext.widthModel.shrinkWrap && this.isShrinkWrapTpl) {
            hierarchyStateInner.inShrinkWrapTable = true;
        } else {
            delete hierarchyStateInner.inShrinkWrapTable;
        }
    },

    beginLayoutCycle: function(ownerContext) {
        var me = this,
            outerCt = me.outerCt,
            lastOuterCtWidth = me.lastOuterCtWidth || '',
            lastOuterCtHeight = me.lastOuterCtHeight || '',
            lastOuterCtTableLayout = me.lastOuterCtTableLayout || '',
            state = ownerContext.state,
            overflowXStyle, overflowYStyle, outerCtWidth, outerCtHeight, outerCtTableLayout,
            deferWidth, hierarchyStateInner;

        me.callParent(arguments);

        
        outerCtWidth = outerCtHeight = outerCtTableLayout = '';

        if (!ownerContext.widthModel.shrinkWrap && me.isShrinkWrapTpl) {
            
            
            
            if (Ext.isIE7m && Ext.isStrict) {
                overflowYStyle = me.getOverflowYStyle(ownerContext);
                if (overflowYStyle === 'auto' || overflowYStyle === 'scroll') {
                    
                    
                    
                    deferWidth = true;
                }
            }

            if (!deferWidth) {
                
                outerCtWidth = '100%';
            }
            hierarchyStateInner = me.owner.hierarchyStateInner;
            
            
            
            overflowXStyle = me.getOverflowXStyle(ownerContext);
            outerCtTableLayout = (hierarchyStateInner.inShrinkWrapTable ||
                                  overflowXStyle === 'auto' || 
                                  overflowXStyle === 'scroll') ? '' : 'fixed';
        }

        if (!ownerContext.heightModel.shrinkWrap && 
            !Ext.supports.PercentageHeightOverflowBug) {
            
            
            
            
            
            outerCtHeight = '100%';
        }

        
        
        
        if ((outerCtWidth !== lastOuterCtWidth) || me.hasOuterCtPxWidth) {
            outerCt.setStyle('width', outerCtWidth);
            me.lastOuterCtWidth = outerCtWidth;
            me.hasOuterCtPxWidth = false;
        }

        
        if (outerCtTableLayout !== lastOuterCtTableLayout) {
            outerCt.setStyle('table-layout', outerCtTableLayout);
            me.lastOuterCtTableLayout = outerCtTableLayout;
        }

        
        
        
        if ((outerCtHeight !== lastOuterCtHeight) || me.hasOuterCtPxHeight) {
            outerCt.setStyle('height', outerCtHeight);
            me.lastOuterCtHeight = outerCtHeight;
            me.hasOuterCtPxHeight = false;
        }

        if (me.hasInnerCtPxHeight) {
            me.innerCt.setStyle('height', '');
            me.hasInnerCtPxHeight = false;
        }

        
        
        
        state.overflowAdjust = state.overflowAdjust || me.lastOverflowAdjust;
    },

    calculate: function(ownerContext) {
        var me = this,
            state = ownerContext.state,
            containerSize = me.getContainerSize(ownerContext, true),
            
            calculatedItems = state.calculatedItems ||
                (state.calculatedItems = me.calculateItems ?
                me.calculateItems(ownerContext, containerSize) : true);

        me.setCtSizeIfNeeded(ownerContext, containerSize);

        if (calculatedItems && ownerContext.hasDomProp('containerChildrenSizeDone')) {

            me.calculateContentSize(ownerContext);

            if (containerSize.gotAll) {
                if (me.manageOverflow && !ownerContext.state.secondPass && !me.reserveScrollbar) {
                    me.calculateOverflow(ownerContext, containerSize);
                }
                return;
            }
        }
        
        me.done = false;
    },

    calculateContentSize: function (ownerContext) {
        var me = this,
            containerDimensions = ((ownerContext.widthModel.shrinkWrap ? 1 : 0) |
                                   (ownerContext.heightModel.shrinkWrap ? 2 : 0)),
            calcWidth = (containerDimensions & 1) || undefined,
            calcHeight = (containerDimensions & 2) || undefined,
            needed = 0,
            props = ownerContext.props;

        if (calcWidth) {
            if (isNaN(props.contentWidth)) {
                ++needed;
            } else {
                calcWidth = undefined;
            }
        }
        if (calcHeight) {
            if (isNaN(props.contentHeight)) {
                ++needed;
            } else {
                calcHeight = undefined;
            }
        }

        if (needed) {
            if (calcWidth && !ownerContext.setContentWidth(me.measureContentWidth(ownerContext))) {
                me.done = false;
            }
            if (calcHeight && !ownerContext.setContentHeight(me.measureContentHeight(ownerContext))) {
                me.done = false;
            }

            
            
            
            
            
        }
    },

    
    calculateOverflow: function (ownerContext) {
        var me = this,
            width, height, scrollbarSize, scrollbars, xauto, yauto, targetEl;

        
        
        xauto = (me.getOverflowXStyle(ownerContext) === 'auto');
        yauto = (me.getOverflowYStyle(ownerContext) === 'auto');

        if (xauto || yauto) {
            scrollbarSize = Ext.getScrollbarSize();
            targetEl = ownerContext.overflowContext.el.dom;
            scrollbars = 0;

            if (targetEl.scrollWidth > targetEl.clientWidth) {
                
                scrollbars |= 1;
            }

            if (targetEl.scrollHeight > targetEl.clientHeight) {
                
                scrollbars |= 2;
            }

            width = (yauto && (scrollbars & 2)) ? scrollbarSize.width : 0;
            height = (xauto && (scrollbars & 1)) ? scrollbarSize.height : 0;

            if (width !== me.lastOverflowAdjust.width || height !== me.lastOverflowAdjust.height) {
                me.done = false;

                
                
                ownerContext.invalidate({
                    state: {
                        overflowAdjust: {
                            width: width,
                            height: height
                        },
                        overflowState: scrollbars,
                        secondPass: true
                    }
                });
            }
        }
    },

    completeLayout: function(ownerContext) {
       this.lastOverflowAdjust = ownerContext.state.overflowAdjust;
    },

    doRenderPadding: function(out, renderData) {
        
        

        var me = renderData.$layout,
            owner = renderData.$layout.owner,
            padding = owner[owner.contentPaddingProperty];

        if (me.managePadding && padding) {
            out.push('padding:', owner.unitizeBox(padding));
        }
    },

    finishedLayout: function (ownerContext) {
        var innerCt = this.innerCt;

        this.callParent(arguments);

        if (Ext.isIEQuirks || Ext.isIE8m)  {
            
            
            
            innerCt.repaint();
        }

        if (Ext.isOpera) {
            
            
            
            innerCt.setStyle('position', 'relative');
            innerCt.dom.scrollWidth;
            innerCt.setStyle('position', '');
        }
    },

    
    getContainerSize : function(ownerContext, inDom) {
        
        
        
        
        
        
        var size = this.callParent(arguments),
            overflowAdjust = ownerContext.state.overflowAdjust;

        if (overflowAdjust) {
            size.width -= overflowAdjust.width;
            size.height -= overflowAdjust.height;
        }

        return size;
    },

    getRenderData: function() {
        var owner = this.owner,
            data = this.callParent();
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        if ((Ext.isIEQuirks || Ext.isIE7m) && 
            ((owner.shrinkWrap & 1) || 
            (owner.floating && !owner.width))) { 
            data.shrinkWrapWidth = true;
        }

        return data;
    },

    
    
    getRenderTarget: function() {
        return this.innerCt;
    },

    
    
    getElementTarget: function() {
        return this.innerCt;
    },

    
    getOverflowXStyle: function(ownerContext) {
        return ownerContext.overflowXStyle ||
            (ownerContext.overflowXStyle = this.owner.scrollFlags.overflowX || ownerContext.overflowContext.getStyle('overflow-x'));
    },

    
    getOverflowYStyle: function(ownerContext) {
        return ownerContext.overflowYStyle || 
            (ownerContext.overflowYStyle = this.owner.scrollFlags.overflowY || ownerContext.overflowContext.getStyle('overflow-y'));
    },

    initContextItems: function(ownerContext) {
        var me = this,
            target = ownerContext.target,
            customOverflowEl = me.owner.customOverflowEl;

        ownerContext.outerCtContext = ownerContext.getEl('outerCt', me);
        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);
        
        if (customOverflowEl) {
            ownerContext.overflowContext = ownerContext.getEl(customOverflowEl);    
        } else {
            ownerContext.overflowContext = ownerContext.targetContext;
        }
        
        if (target[target.contentPaddingProperty] !== undefined) {
            
            
            
            
            
            ownerContext.paddingContext = me.isShrinkWrapTpl ?
                ownerContext.innerCtContext : ownerContext.outerCtContext;
        }
    },

    initLayout: function() {
        var me = this,
            scrollbarWidth = Ext.getScrollbarSize().width,
            owner = me.owner;

        me.callParent();

        
        
        
        if (scrollbarWidth && me.manageOverflow && !me.hasOwnProperty('lastOverflowAdjust')) {
            if (owner.autoScroll || me.reserveScrollbar) {
                me.lastOverflowAdjust = {
                    width: scrollbarWidth,
                    height: 0
                };
            }
        }
    },

    
    insertTableCt: function(ownerContext) {
        var me = this,
            owner = me.owner,
            i = 0,
            renderTpl, fragment, childNodes, childLength, targetEl;

        
        renderTpl = Ext.XTemplate.getTpl(this, 'tableTpl');
        renderTpl.renderPadding = me.doRenderPadding

        
        
        me.outerCt.dom.removeChild(me.innerCt.dom);
        
        
        fragment = document.createDocumentFragment();
        childNodes = me.innerCt.dom.childNodes;
        childLength = childNodes.length;
        
        for (; i < childLength; i++) {
            fragment.appendChild(childNodes[0]);
        }
        
        targetEl = me.getTarget();
        targetEl.dom.innerHTML = renderTpl.apply({
            $layout: me,
            ownerId: me.owner.id
        });

        
        targetEl.down('td').dom.appendChild(fragment);
        
        
        
        me.applyChildEls(owner.el, owner.id)
        
        
        
        me.isShrinkWrapTpl = true;
        
        ownerContext.removeEl(me.outerCt);
        ownerContext.removeEl(me.innerCt);
        me.initContextItems(ownerContext);
    },

    measureContentHeight: function (ownerContext) {
        
        var contentHeight = this.outerCt.getHeight(),
            target = ownerContext.target;

        if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) {
            
            
            
            
            contentHeight += ownerContext.targetContext.getPaddingInfo().height;
        }
        return contentHeight;
    },

    measureContentWidth: function (ownerContext) {
        var dom, style, old, contentWidth, target;
            
        
        
        
        if (this.chromeCellMeasureBug) {
            dom = this.innerCt.dom;
            style = dom.style;
            old = style.display;
            
            if (old == 'table-cell') {
                style.display = '';
                dom.offsetWidth;
                style.display = old;
            }    
        }
        
        
        contentWidth = this.outerCt.getWidth();
        target = ownerContext.target;

        if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) {
            
            
            
            
            contentWidth += ownerContext.targetContext.getPaddingInfo().width;
        }
        return contentWidth;
    },

    
    setCtSizeIfNeeded: function(ownerContext, containerSize) {
        var me = this,
            width = containerSize.width,
            height = containerSize.height,
            padding = ownerContext.paddingContext.getPaddingInfo(),
            targetEl = me.getTarget(),
            overflowXStyle = me.getOverflowXStyle(ownerContext),
            overflowYStyle = me.getOverflowYStyle(ownerContext),
            canOverflowX = (overflowXStyle === 'auto' || overflowXStyle === 'scroll'),
            canOverflowY = (overflowYStyle === 'auto' || overflowYStyle === 'scroll'),
            scrollbarSize = Ext.getScrollbarSize(),
            isShrinkWrapTpl = me.isShrinkWrapTpl,
            manageOverflow = me.manageOverflow,
            overflowStyleName, needsOuterHeight, needsInnerHeight, needsInnerCtPaddingHeight;

        if (width && !ownerContext.widthModel.shrinkWrap &&
            
            
            ((Ext.isIE7m && Ext.isStrict && isShrinkWrapTpl && canOverflowY) ||
            
            
            
            (Ext.isIEQuirks && !isShrinkWrapTpl && !canOverflowX))) {

            if (!manageOverflow) {
                
                
                
                if (canOverflowY && (targetEl.dom.scrollHeight > targetEl.dom.clientHeight)) {
                    
                    width -= scrollbarSize.width;
                }
            }

            ownerContext.outerCtContext.setProp('width', width + padding.width);
            me.hasOuterCtPxWidth = true;
        }

        if (height && !ownerContext.heightModel.shrinkWrap) {
            if (Ext.supports.PercentageHeightOverflowBug) {
                
                
                needsOuterHeight = true;
            }
            if (((Ext.isIE8 && Ext.isStrict) ||
                Ext.isIE7m && Ext.isStrict && isShrinkWrapTpl)) {
                
                
                
                needsInnerHeight = true;
                
                
                needsInnerCtPaddingHeight = !Ext.isIE8;
            }

            if ((needsOuterHeight || needsInnerHeight) && canOverflowX && 
                (targetEl.dom.scrollWidth > targetEl.dom.clientWidth)) {
                
                
                
                height = Math.max(height - scrollbarSize.height, 0);
            }

            if (needsOuterHeight) {
                ownerContext.outerCtContext.setProp('height', height + padding.height);
                me.hasOuterCtPxHeight = true;
            }
            
            if (needsInnerHeight) {
                if (needsInnerCtPaddingHeight) {
                    height += padding.height;
                }
                ownerContext.innerCtContext.setProp('height', height);
                me.hasInnerCtPxHeight = true;
            }
        }

        if (Ext.isIE7 && Ext.isStrict && !isShrinkWrapTpl && (overflowYStyle === 'auto')) {
            
            
            
            
            
            
            
            overflowStyleName = (overflowXStyle === 'auto') ? 'overflow-x' : 'overflow-y';
            targetEl.setStyle(overflowStyleName, 'hidden');
            targetEl.setStyle(overflowStyleName, 'auto');
        }
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);

        renderTpl.renderPadding = this.doRenderPadding;
    },

    getContentTarget: function(){
        return this.innerCt;
    }

}, function(){
    this.prototype.chromeCellMeasureBug = Ext.isChrome && Ext.chromeVersion >= 26;
});


Ext.define('Ext.ZIndexManager', {
    alternateClassName: 'Ext.WindowGroup',

    statics: {
        zBase : 9000
    },

    constructor: function(container) {
        var me = this;

        me.list = {};
        me.zIndexStack = [];
        me.front = null;

        if (container) {

            
            if (container.isContainer) {
                container.on('resize', me._onContainerResize, me);
                me.zseed = Ext.Number.from(me.rendered ? container.getEl().getStyle('zIndex') : undefined, me.getNextZSeed());
                
                me.targetEl = container.getTargetEl();
                me.container = container;
            }
            
            else {
                Ext.EventManager.onWindowResize(me._onContainerResize, me);
                me.zseed = me.getNextZSeed();
                me.targetEl = Ext.get(container);
            }
        }
        
        
        else {
            Ext.EventManager.onWindowResize(me._onContainerResize, me);
            me.zseed = me.getNextZSeed();
            Ext.onDocumentReady(function() {
                me.targetEl = Ext.getBody();
            });
        }
    },

    getNextZSeed: function() {
        return (Ext.ZIndexManager.zBase += 10000);
    },

    setBase: function(baseZIndex) {
        this.zseed = baseZIndex;
        var result = this.assignZIndices();
        this._activateLast();
        return result;
    },

    
    assignZIndices: function() {
        var a = this.zIndexStack,
            len = a.length,
            i = 0,
            zIndex = this.zseed,
            comp,
            topModal;

        for (; i < len; i++) {
            comp = a[i];
            if (comp && !comp.hidden) {

                
                
                
                
                
                
                
                zIndex = comp.setZIndex(zIndex);
                if (comp.modal) {
                    topModal = comp;
                }
            }
        }
        
        
        if (topModal) {
            this._showModalMask(topModal)
        }
        return zIndex;
    },

    
    _setActiveChild: function(comp, oldFront) {
        var front = this.front,
            oldPreventFocus = comp.preventFocusOnActivate;

        if (comp !== front) {

            if (front && !front.destroying) {
                front.setActive(false, comp);
            }
            this.front = comp;
            if (comp && comp != oldFront) {

                
                comp.preventFocusOnActivate = comp.preventFocusOnActivate || oldFront && (oldFront.preventFocusOnActivate || !oldFront.focusOnToFront);

                comp.setActive(true);
                
                
                if (comp.modal) {
                    this._showModalMask(comp);
                }

                
                comp.preventFocusOnActivate = oldPreventFocus;
            }
        }
    },

    onComponentHide: function(comp){
        this._activateLast();
    },

    
    _activateLast: function() {
        var me = this,
            stack = me.zIndexStack,
            i = stack.length - 1,
            comp;

        
        
        
        for (; i >= 0 && stack[i].hidden; --i);

        
        if ((comp = stack[i])) {
            me._setActiveChild(comp, me.front);
            if (comp.modal) {
                return;
            }
        }
        
        else {
            if (me.front && !me.front.destroying) {
                me.front.setActive(false);
            }
            me.front = null;
        }

        
        
        for (; i >= 0; --i) {
            comp = stack[i];
            
            if (comp.isVisible() && comp.modal) {
                me._showModalMask(comp);
                return;
            }
        }

        
        
        me._hideModalMask();
    },

    _showModalMask: function(comp) {
        var me = this,
            zIndex = comp.el.getStyle('zIndex') - 4,
            maskTarget = comp.floatParent ? comp.floatParent.getTargetEl() : comp.container,
            mask = me.mask,
            shim = me.maskShim,
            viewSize;

        if (!mask) {
            if (Ext.isIE6) {
                shim = me.maskShim = Ext.getBody().createChild({
                    tag: 'iframe',
                    cls : Ext.baseCSSPrefix + 'shim ' + Ext.baseCSSPrefix + 'mask-shim'
                });
                shim.setVisibilityMode(Ext.Element.DISPLAY);
            }

            
            mask = me.mask = Ext.getBody().createChild({
                cls: Ext.baseCSSPrefix + 'mask',
                style: 'height:0;width:0'
            });
            mask.setVisibilityMode(Ext.Element.DISPLAY);
            mask.on('click', me._onMaskClick, me);
        }
        
        mask.maskTarget = maskTarget;
        viewSize = me.getMaskBox();

        if (shim) {
            shim.setStyle('zIndex', zIndex);
            shim.show();
            shim.setBox(viewSize);
        }
        mask.setStyle('zIndex', zIndex);

        
        
        mask.show();
        mask.setBox(viewSize);
    },

    _hideModalMask: function() {
        var mask = this.mask,
            maskShim = this.maskShim;

        if (mask && mask.isVisible()) {
            mask.maskTarget = undefined;
            mask.hide();
            if (maskShim) {
                maskShim.hide();
            }
        }
    },

    _onMaskClick: function() {
        if (this.front) {
            this.front.focus();
        }
    },
    
    getMaskBox: function(){
        var maskTarget = this.mask.maskTarget;
        if (maskTarget.dom === document.body) {
            return {
                height: Math.max(document.body.scrollHeight, Ext.dom.Element.getDocumentHeight()),
                width: Math.max(document.body.scrollWidth, document.documentElement.clientWidth),
                x: 0,
                y: 0
            };
        } else {
            return maskTarget.getBox();
        } 
    },

    _onContainerResize: function() {
        var me = this,
            mask = me.mask,
            maskShim = me.maskShim,
            viewSize;

        if (mask && mask.isVisible()) {

            
            
            mask.hide();
            if (maskShim) {
                maskShim.hide();
            }

            viewSize = me.getMaskBox();
            if (maskShim) {
                maskShim.setSize(viewSize);
                maskShim.show();
            }
            mask.setSize(viewSize);
            mask.show();
        }
    },

    
    register : function(comp) {
        var me = this,
            compAfterHide = comp.afterHide;
        
        if (comp.zIndexManager) {
            comp.zIndexManager.unregister(comp);
        }
        comp.zIndexManager = me;

        me.list[comp.id] = comp;
        me.zIndexStack.push(comp);
        
        
        comp.afterHide = function() {
            compAfterHide.apply(comp, arguments);
            me.onComponentHide(comp);
        };
    },

    
    unregister : function(comp) {
        var me = this,
            list = me.list;
        
        delete comp.zIndexManager;
        if (list && list[comp.id]) {
            delete list[comp.id];
            
            
            delete comp.afterHide;
            Ext.Array.remove(me.zIndexStack, comp);

            
            me._activateLast();
        }
    },

    
    get : function(id) {
        return id.isComponent ? id : this.list[id];
    },

   
    bringToFront : function(comp, preventFocus) {
        var me = this,
            result = false,
            zIndexStack = me.zIndexStack;
        
        comp = me.get(comp);
        if (comp !== me.front) {
            Ext.Array.remove(zIndexStack, comp);
            if (comp.preventBringToFront) {
                
                zIndexStack.unshift(comp);
            } else {
                
                zIndexStack.push(comp);
            }

            me.assignZIndices();

            
            if (!preventFocus) {
                me._activateLast();
            }
            result = true;
            me.front = comp;
            
            
            if (comp.modal) {
                me._showModalMask(comp);
            }
        }
        return result;
    },

    
    sendToBack : function(comp) {
        var me = this;
        
        comp = me.get(comp);
        Ext.Array.remove(me.zIndexStack, comp);
        me.zIndexStack.unshift(comp);
        me.assignZIndices();
        this._activateLast();
        return comp;
    },

    
    hideAll : function() {
        var list = this.list,
            item,
            id;
            
        for (id in list) {
            if (list.hasOwnProperty(id)) {
                item = list[id];
                if (item.isComponent && item.isVisible()) {
                    item.hide();
                }
            }
        }
    },

    
    hide: function() {
        var i = 0,
            stack = this.zIndexStack,
            len = stack.length,
            comp;

        this.tempHidden = [];
        for (; i < len; i++) {
            comp = stack[i];
            if (comp.isVisible()) {
                this.tempHidden.push(comp);
                comp.el.hide();
                comp.hidden = true;
            }
        }
    },

    
    show: function() {
        var i = 0,
            tempHidden = this.tempHidden,
            len = tempHidden ? tempHidden.length : 0,
            comp;

        for (; i < len; i++) {
            comp = tempHidden[i];
            comp.el.show();
            comp.hidden = false;
            comp.setPosition(comp.x, comp.y);
        }
        delete this.tempHidden;
    },

    
    getActive : function() {
        return this.front;
    },

    
    getBy : function(fn, scope) {
        var r = [],
            i = 0,
            stack = this.zIndexStack,
            len = stack.length,
            comp;

        for (; i < len; i++) {
            comp = stack[i];
            if (fn.call(scope||comp, comp) !== false) {
                r.push(comp);
            }
        }
        return r;
    },

    
    each : function(fn, scope) {
        var list = this.list,
            id,
            comp;
            
        for (id in list) {
            if (list.hasOwnProperty(id)) {
                comp = list[id];
                if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                    return;
                }
            }
        }
    },

    
    eachBottomUp: function (fn, scope) {
        var stack = this.zIndexStack,
            i = 0,
            len = stack.length,
            comp;

        for (; i < len; i++) {
            comp = stack[i];
            if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                return;
            }
        }
    },

    
    eachTopDown: function (fn, scope) {
        var stack = this.zIndexStack,
            i = stack.length,
            comp;

        for (; i-- > 0; ) {
            comp = stack[i];
            if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                return;
            }
        }
    },

    destroy: function() {
        var me   = this,
            list = me.list,
            comp,
            id;

        for (id in list) {
            if (list.hasOwnProperty(id)) {
                comp = list[id];

                if (comp.isComponent) {
                    comp.destroy();
                }
            }
        }

        delete me.zIndexStack;
        delete me.list;
        delete me.container;
        delete me.targetEl;
    }
}, function() {
    
    Ext.WindowManager = Ext.WindowMgr = new this();
});


Ext.define('Ext.Queryable', {
    
    isQueryable: true,
    
    
    query : function(selector) {
        selector = selector || '*';
        return Ext.ComponentQuery.query(selector, this);
    },
    
    
    queryBy: function(fn, scope) {
        var out = [],
            items = this.getRefItems(true),
            i = 0,
            len = items.length,
            item;
            
        for (; i < len; ++i) {
            item = items[i];
            if (fn.call(scope || item, item) !== false) {
                out.push(item);
            }
        }
        return out;
    },
    
    
    queryById: function(id){
        return this.down('#' + id);
    },

    
    child: function (selector) {
        if (selector && selector.isComponent) {
            selector = '#' + Ext.escapeId(selector.getItemId());
        }

        selector = selector || '';
        return this.query('> ' + selector)[0] || null;
    },
    
    
    down: function (selector) {
        if (selector && selector.isComponent) {
            selector = '#' + Ext.escapeId(selector.getItemId());
        }

        selector = selector || '';
        return this.query(selector)[0] || null;
    },
    
    getRefItems: function(){
        return [];
    }
        
});


Ext.define('Ext.layout.component.Component', {

    

    extend:  Ext.layout.Layout ,

    

    type: 'component',

    isComponentLayout: true,

    nullBox: {},

    usesContentHeight: true,
    usesContentWidth: true,
    usesHeight: true,
    usesWidth: true,

    beginLayoutCycle: function (ownerContext, firstCycle) {
        var me = this,
            owner = me.owner,
            ownerCtContext = ownerContext.ownerCtContext,
            heightModel = ownerContext.heightModel,
            widthModel = ownerContext.widthModel,
            body = owner.el.dom === document.body,
            lastBox = owner.lastBox || me.nullBox,
            lastSize = owner.el.lastBox || me.nullBox,
            dirty = !body,
            ownerLayout, v, widthName, heightName;

        me.callParent(arguments);

        if (firstCycle) {
            if (me.usesContentWidth) {
                ++ownerContext.consumersContentWidth;
            }
            if (me.usesContentHeight) {
                ++ownerContext.consumersContentHeight;
            }
            if (me.usesWidth) {
                ++ownerContext.consumersWidth;
            }
            if (me.usesHeight) {
                ++ownerContext.consumersHeight;
            }

            if (ownerCtContext && !ownerCtContext.hasRawContent) {
                ownerLayout = owner.ownerLayout;

                if (ownerLayout.usesWidth) {
                    ++ownerContext.consumersWidth;
                }
                if (ownerLayout.usesHeight) {
                    ++ownerContext.consumersHeight;
                }
            }
        }

        
        

        if (widthModel.configured) {
            
            
            
            
            widthName = widthModel.names.width;

            if (!body) {
                dirty = firstCycle ? owner[widthName] !== lastSize.width
                                   : widthModel.constrained;
            }
            
            ownerContext.setWidth(owner[widthName], dirty);
        } else if (ownerContext.isTopLevel) {
            if (widthModel.calculated) {
                v = lastBox.width;
                ownerContext.setWidth(v, v != lastSize.width);
            }

            v = lastBox.x;
            ownerContext.setProp('x', v, v != lastSize.x);
        }

        if (heightModel.configured) {
            heightName = heightModel.names.height;

            if (!body) {
                dirty = firstCycle ? owner[heightName] !== lastSize.height
                                   : heightModel.constrained;
            }

            ownerContext.setHeight(owner[heightName], dirty);
        } else if (ownerContext.isTopLevel) {
            if (heightModel.calculated) {
                v = lastBox.height;
                ownerContext.setHeight(v, v != lastSize.height);
            }

            v = lastBox.y;
            ownerContext.setProp('y', v, v != lastSize.y);
        }
    },

    finishedLayout: function(ownerContext) {
        var me = this,
            elementChildren = ownerContext.children,
            owner = me.owner,
            len, i, elContext, lastBox, props;

        

        
        
        if (elementChildren) {
            len = elementChildren.length;
            for (i = 0; i < len; i++) {
                elContext = elementChildren[i];
                elContext.el.lastBox = elContext.props;
            }
        }

        
        ownerContext.previousSize = me.lastComponentSize;

        
        me.lastComponentSize = owner.el.lastBox = props = ownerContext.props;
        
        
        
        lastBox = owner.lastBox || (owner.lastBox = {});
        lastBox.x = props.x;
        lastBox.y = props.y;
        lastBox.width = props.width;
        lastBox.height = props.height;
        lastBox.invalid = false;
        
        me.callParent(arguments);
    },
    
    notifyOwner: function(ownerContext) {
        var me = this,
            currentSize = me.lastComponentSize,
            prevSize = ownerContext.previousSize,
            args = [currentSize.width, currentSize.height];

        if (prevSize) {
            args.push(prevSize.width, prevSize.height);
        }

        
        me.owner.afterComponentLayout.apply(me.owner, args);
    },

    
    getTarget : function() {
        return this.owner.el;
    },

    
    getRenderTarget : function() {
        return this.owner.el;
    },

    cacheTargetInfo: function(ownerContext) {
        var me = this,
            targetInfo = me.targetInfo,
            target;

        if (!targetInfo) {
            target = ownerContext.getEl('getTarget', me);

            me.targetInfo = targetInfo = {
                padding: target.getPaddingInfo(),
                border: target.getBorderInfo()
            };
        }

        return targetInfo;
    },

    measureAutoDimensions: function (ownerContext, dimensions) {
        
        
        
        
        

        var me = this,
            owner = me.owner,
            containerLayout = owner.layout,
            heightModel = ownerContext.heightModel,
            widthModel = ownerContext.widthModel,
            boxParent = ownerContext.boxParent,
            isBoxParent = ownerContext.isBoxParent,
            props = ownerContext.props,
            isContainer,
            ret = {
                gotWidth: false,
                gotHeight: false,
                isContainer: (isContainer = !ownerContext.hasRawContent)
            },
            hv = dimensions || 3,
            zeroWidth, zeroHeight,
            needed = 0,
            got = 0,
            ready, size, temp;

        
        

        
        

        if (widthModel.shrinkWrap && ownerContext.consumersContentWidth) {
            ++needed;
            zeroWidth = !(hv & 1);

            if (isContainer) {
                
                
                if (zeroWidth) {
                    ret.contentWidth = 0;
                    ret.gotWidth = true;
                    ++got;
                } else if ((ret.contentWidth = ownerContext.getProp('contentWidth')) !== undefined) {
                    ret.gotWidth = true;
                    ++got;
                }
            } else {
                size = props.contentWidth;

                if (typeof size == 'number') { 
                    ret.contentWidth = size;
                    ret.gotWidth = true;
                    ++got;
                } else {
                    if (zeroWidth) {
                        ready = true;
                    } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {
                        ready = false;
                    } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {
                        
                        
                        
                        ready = true;
                    } else {
                        
                        
                        
                        ready = boxParent.hasDomProp('width');
                    }

                    if (ready) {
                        if (zeroWidth) {
                            temp = 0;
                        } else if (containerLayout && containerLayout.measureContentWidth) {
                            
                            
                            temp = containerLayout.measureContentWidth(ownerContext);
                        } else {
                            temp = me.measureContentWidth(ownerContext);
                        }

                        if (!isNaN(ret.contentWidth = temp)) {
                            ownerContext.setContentWidth(temp, true);
                            ret.gotWidth = true;
                            ++got;
                        }
                    }
                }
            }
        } else if (widthModel.natural && ownerContext.consumersWidth) {
            ++needed;
            size = props.width;
            

            if (typeof size == 'number') { 
                ret.width = size;
                ret.gotWidth = true;
                ++got;
            } else {
                if (isBoxParent || !boxParent) {
                    ready = true;
                } else {
                    
                    
                    
                    ready = boxParent.hasDomProp('width');
                }

                if (ready) {
                    if (!isNaN(ret.width = me.measureOwnerWidth(ownerContext))) {
                        ownerContext.setWidth(ret.width, false);
                        ret.gotWidth = true;
                        ++got;
                    }
                }
            }
        }

        
        

        if (heightModel.shrinkWrap && ownerContext.consumersContentHeight) {
            ++needed;
            zeroHeight = !(hv & 2);

            if (isContainer) {
                
                if (zeroHeight) {
                    ret.contentHeight = 0;
                    ret.gotHeight = true;
                    ++got;
                } else if ((ret.contentHeight = ownerContext.getProp('contentHeight')) !== undefined) {
                    ret.gotHeight = true;
                    ++got;
                }
            } else {
                size = props.contentHeight;

                if (typeof size == 'number') { 
                    ret.contentHeight = size;
                    ret.gotHeight = true;
                    ++got;
                } else {
                    if (zeroHeight) {
                        ready = true;
                    } else if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {
                        ready = false;
                    } else if (owner.noWrap) {
                        ready = true;
                    } else if (!widthModel.shrinkWrap) {
                        
                        ready = (ownerContext.bodyContext || ownerContext).hasDomProp('width');
                    } else if (isBoxParent || !boxParent || boxParent.widthModel.shrinkWrap) {
                        
                        
                        
                        ready = true;
                    } else {
                        
                        
                        
                        ready = boxParent.hasDomProp('width');
                    }

                    if (ready) {
                        if (zeroHeight) {
                            temp = 0;
                        } else if (containerLayout && containerLayout.measureContentHeight) {
                            
                            
                            temp = containerLayout.measureContentHeight(ownerContext);
                        } else {
                            temp = me.measureContentHeight(ownerContext);
                        }

                        if (!isNaN(ret.contentHeight = temp)) {
                            ownerContext.setContentHeight(temp, true);
                            ret.gotHeight = true;
                            ++got;
                        }
                    }
                }
            }
        } else if (heightModel.natural && ownerContext.consumersHeight) {
            ++needed;
            size = props.height;
            

            if (typeof size == 'number') { 
                ret.height = size;
                ret.gotHeight = true;
                ++got;
            } else {
                if (isBoxParent || !boxParent) {
                    ready = true;
                } else {
                    
                    
                    
                    ready = boxParent.hasDomProp('width');
                }

                if (ready) {
                    if (!isNaN(ret.height = me.measureOwnerHeight(ownerContext))) {
                        ownerContext.setHeight(ret.height, false);
                        ret.gotHeight = true;
                        ++got;
                    }
                }
            }
        }

        if (boxParent) {
            ownerContext.onBoxMeasured();
        }

        ret.gotAll = got == needed;
        
        return ret;
    },

    measureContentWidth: function (ownerContext) {
        
        return ownerContext.el.getWidth() - ownerContext.getFrameInfo().width;
    },

    measureContentHeight: function (ownerContext) {
        
        return ownerContext.el.getHeight() - ownerContext.getFrameInfo().height;
    },

    measureOwnerHeight: function (ownerContext) {
        return ownerContext.el.getHeight();
    },

    measureOwnerWidth: function (ownerContext) {
        return ownerContext.el.getWidth();
    }
});


Ext.define('Ext.layout.component.Auto', {

    

    alias: 'layout.autocomponent',

    extend:  Ext.layout.component.Component ,

    

    type: 'autocomponent',

    
    setHeightInDom: false,

    
    setWidthInDom: false,

    waitForOuterHeightInDom: false,
    waitForOuterWidthInDom: false,
    
    beginLayoutCycle: function(ownerContext, firstCycle){
        var me = this,
            lastWidthModel = me.lastWidthModel,
            lastHeightModel = me.lastHeightModel,
            el = me.owner.el;
            
        me.callParent(arguments);
            
        if (lastWidthModel && lastWidthModel.fixed && ownerContext.widthModel.shrinkWrap) {
            el.setWidth(null);
        }
            
        if (lastHeightModel && lastHeightModel.fixed && ownerContext.heightModel.shrinkWrap) {
            el.setHeight(null);
        }    
    },

    calculate: function(ownerContext) {
        var me = this,
            measurement = me.measureAutoDimensions(ownerContext),
            heightModel = ownerContext.heightModel,
            widthModel = ownerContext.widthModel,
            width, height;

        
        
        if (measurement.gotWidth) {
            if (widthModel.shrinkWrap) {
                me.publishOwnerWidth(ownerContext, measurement.contentWidth);
            } else if (me.publishInnerWidth) {
                me.publishInnerWidth(ownerContext, measurement.width);
            }
        } else if (!widthModel.auto && me.publishInnerWidth) {
            width = me.waitForOuterWidthInDom ? ownerContext.getDomProp('width')
                        : ownerContext.getProp('width');
            if (width === undefined) {
                me.done = false;
            } else {
                me.publishInnerWidth(ownerContext, width);
            }
        }

        if (measurement.gotHeight) {
            if (heightModel.shrinkWrap) {
                me.publishOwnerHeight(ownerContext, measurement.contentHeight);
            } else if (me.publishInnerHeight) {
                me.publishInnerHeight(ownerContext, measurement.height);
            }
        } else if (!heightModel.auto && me.publishInnerHeight) {
            height = me.waitForOuterHeightInDom ? ownerContext.getDomProp('height')
                        : ownerContext.getProp('height');
            if (height === undefined) {
                me.done = false;
            } else {
               me.publishInnerHeight(ownerContext, height);
            }
        }

        if (!measurement.gotAll) {
            me.done = false;
        }
    },

    calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
        return contentHeight + ownerContext.getFrameInfo().height;
    },

    calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) {
        return contentWidth + ownerContext.getFrameInfo().width;
    },

    publishOwnerHeight: function (ownerContext, contentHeight) {
        var me = this,
            owner = me.owner,
            height = me.calculateOwnerHeightFromContentHeight(ownerContext, contentHeight),
            constrainedHeight, dirty, heightModel;

        if (isNaN(height)) {
            me.done = false;
        } else {
            constrainedHeight = Ext.Number.constrain(height, owner.minHeight, owner.maxHeight);

            if (constrainedHeight == height) {
                dirty = me.setHeightInDom;
            } else {
                heightModel = me.sizeModels[
                    (constrainedHeight < height) ? 'constrainedMax' : 'constrainedMin'];
                height = constrainedHeight;

                if (ownerContext.heightModel.calculatedFromShrinkWrap) {
                    
                    
                    
                    ownerContext.heightModel = heightModel;
                } else {
                    ownerContext.invalidate({ heightModel: heightModel });
                }
            }
            
            ownerContext.setHeight(height, dirty);
        }
    },

    publishOwnerWidth: function (ownerContext, contentWidth) {
        var me = this,
            owner = me.owner,
            width = me.calculateOwnerWidthFromContentWidth(ownerContext, contentWidth),
            constrainedWidth, dirty, widthModel;

        if (isNaN(width)) {
            me.done = false;
        } else {
            constrainedWidth = Ext.Number.constrain(width, owner.minWidth, owner.maxWidth);

            if (constrainedWidth == width) {
                dirty = me.setWidthInDom;
            } else {
                widthModel = me.sizeModels[
                    (constrainedWidth < width) ? 'constrainedMax' : 'constrainedMin'];
                width = constrainedWidth;

                if (ownerContext.widthModel.calculatedFromShrinkWrap) {
                    
                    
                    
                    ownerContext.widthModel = widthModel;
                } else {
                    ownerContext.invalidate({ widthModel: widthModel });
                }
            }

            ownerContext.setWidth(width, dirty);
        }
    }
});


Ext.define('Ext.container.AbstractContainer', {

    

    extend:  Ext.Component ,

               
                                   
                                    
                           
      
    
    mixins: {
        queryable:  Ext.Queryable 
    },

    

    renderTpl: '{%this.renderContainer(out,values)%}',

    

    

    

    

    
    suspendLayout : false,

    
    autoDestroy : true,

     
    defaultType: 'panel',
    
    
    detachOnRemove: true,

    
    isContainer : true,

    
    layoutCounter : 0,

    baseCls: Ext.baseCSSPrefix + 'container',

    

    defaultLayoutType: 'auto',

    
    initComponent : function(){
        var me = this;
        me.addEvents(
            
            'afterlayout',
            
            'beforeadd',
            
            'beforeremove',
            
            'add',
            
            'remove'
        );

        me.callParent();

        me.getLayout();
        me.initItems();
    },

    
    initItems : function() {
        var me = this,
            items = me.items;

        
        me.items = new Ext.util.AbstractMixedCollection(false, me.getComponentId);
        me.floatingItems = new Ext.util.MixedCollection(false, me.getComponentId);

        if (items) {
            if (!Ext.isArray(items)) {
                items = [items];
            }

            me.add(items);
        }
    },

    
    getFocusEl: function() {
        return this.getTargetEl();
    },

    finishRenderChildren: function () {
        this.callParent();

        var layout = this.getLayout();

        if (layout) {
            layout.finishRender();
        }
    },

    beforeRender: function () {
        var me = this,
            layout = me.getLayout(),
            targetCls;

        me.callParent();

        if (!layout.initialized) {
            layout.initLayout();
        }

        targetCls = layout.targetCls;

        if (targetCls) {
            me.applyTargetCls(targetCls);
        }
    },

    
    
    
    
    
    applyTargetCls: function(targetCls) {
        this.addCls(targetCls);
    },

    afterComponentLayout: function() {
        var floaters = this.floatingItems.items,
            floaterCount = floaters.length,
            i, floater
            
        this.callParent(arguments);

        
        for (i = 0; i < floaterCount; i++) {
            floater = floaters[i];
            if (!floater.rendered && floater.autoShow) {
                floater.show();
            }
        }
    },

    onPosition: function() {
        this.callParent(arguments);
        this.repositionFloatingItems();
    },

    onResize: function() {
        this.callParent(arguments);
        this.repositionFloatingItems();
    },

    repositionFloatingItems: function() {
        var floaters = this.floatingItems.items,
            floaterCount = floaters.length,
            i, floater;

        
        for (i = 0; i < floaterCount; i++) {
            floater = floaters[i];
            if (floater.el && !floater.hidden) {
                floater.setPosition(floater.x, floater.y);
            }
        }
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);
        this.getLayout().setupRenderTpl(renderTpl);
    },
    
    
    getDefaultContentTarget: function() {
        return this.el;
    },
    
    
    getContentTarget: function(){
        return this.getLayout().getContentTarget();
    },

    
    setLayout : function(layout) {
        var currentLayout = this.layout;

        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
            currentLayout.setOwner(null);
        }

        this.layout = layout;
        layout.setOwner(this);
    },

    
    getLayout : function() {
        var me = this;
        if (!me.layout || !me.layout.isLayout) {
            
            me.setLayout(Ext.layout.Layout.create(me.layout, me.self.prototype.layout || me.defaultLayoutType));
        }

        return me.layout;
    },

    
    doLayout : function() {
        this.updateLayout();
        return this;
    },

    
    afterLayout : function(layout) {
        var me = this;
        ++me.layoutCounter;
        if (me.hasListeners.afterlayout) {
            me.fireEvent('afterlayout', me, layout);
        }
    },

    
    prepareItems : function(items, applyDefaults) {
        
        
        if (Ext.isArray(items)) {
            items = items.slice();
        } else {
            items = [items];
        }

        
        var me = this,
            i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (item == null) {
                Ext.Array.erase(items, i, 1);
                --i;
                --len;
            } else {
                if (applyDefaults) {
                    item = this.applyDefaults(item);
                }

                
                item.isContained = me;
                items[i] = me.lookupComponent(item);
                
                delete item.isContained;
                delete items[i].isContained;
            }
        }

        return items;
    },

    
    applyDefaults : function(config) {
        var defaults = this.defaults;

        if (defaults) {
            if (Ext.isFunction(defaults)) {
                defaults = defaults.call(this, config);
            }

            if (Ext.isString(config)) {
                config = Ext.ComponentManager.get(config);
            }
            Ext.applyIf(config, defaults);
        }
        return config;
    },

    
    lookupComponent : function(comp) {
        return (typeof comp == 'string') ? Ext.ComponentManager.get(comp)
                                         : Ext.ComponentManager.create(comp, this.defaultType);
    },

    
    getComponentId : function(comp) {
        return comp.getItemId && comp.getItemId();
    },

    
    add : function() {
        var me = this,
            args = Ext.Array.slice(arguments),
            index = (typeof args[0] == 'number') ? args.shift() : -1,
            layout = me.getLayout(),
            addingArray, items, i, length, item, pos, ret;

        if (args.length == 1 && Ext.isArray(args[0])) {
            items = args[0];
            addingArray = true;
        } else {
            items = args;
        }

        if (me.rendered) {
            Ext.suspendLayouts(); 
        }

        ret = items = me.prepareItems(items, true);
        length = items.length;

        if (!addingArray && length == 1) { 
            ret = items[0];
        }

        
        for (i = 0; i < length; i++) {
            item = items[i];

            pos = (index < 0) ? me.items.length : (index + i);

            
            if (item.floating) {
                me.floatingItems.add(item);
                item.onAdded(me, pos);

                if (me.hasListeners.add) {
                    me.fireEvent('add', me, item, pos);
                }
            } else if ((!me.hasListeners.beforeadd || me.fireEvent('beforeadd', me, item, pos) !== false) && me.onBeforeAdd(item) !== false) {
                me.items.insert(pos, item);
                item.onAdded(me, pos);
                me.onAdd(item, pos);
                layout.onAdd(item, pos);

                if (me.hasListeners.add) {
                    me.fireEvent('add', me, item, pos);
                }
            }
        }

        
        me.updateLayout();
        if (me.rendered) {
            Ext.resumeLayouts(true);
        }

        return ret;
    },

    
    onAdd : Ext.emptyFn,

    
    onRemove : Ext.emptyFn,

    
    insert : function(index, comp) {
        var compIdx;
        if (comp && comp.isComponent) {
            compIdx = this.items.indexOf(comp);
            if (compIdx !== -1) {
                return this.move(compIdx, index);
            }
        }
        return this.add(index, comp);
    },

    
    move : function(fromIdx, toIdx) {
        var items = this.items,
            item;
            
        if (fromIdx.isComponent) {
            fromIdx = items.indexOf(fromIdx);
        }
        item = items.removeAt(fromIdx);
        if (item === false) {
            return false;
        }
        items.insert(toIdx, item);
        this.onMove(item, fromIdx, toIdx);
        this.updateLayout();
        return item;
    },
    
    onMove: Ext.emptyFn,

    
    onBeforeAdd : function(item) {
        
        if (item.ownerCt && item.ownerCt !== this) {
            item.ownerCt.remove(item, false);
        }
    },

    
    remove : function(comp, autoDestroy) {
        var me = this,
            c = me.getComponent(comp);

        if (c && (!me.hasListeners.beforeremove || me.fireEvent('beforeremove', me, c) !== false)) {
            me.doRemove(c, autoDestroy);
            if (me.hasListeners.remove) {
                me.fireEvent('remove', me, c);
            }

            if (!me.destroying && !c.floating) {
                me.updateLayout();
            }
        }

        return c;
    },

    
    doRemove : function(component, doDestroy) {
        
        doDestroy = doDestroy === true || (doDestroy !== false && this.autoDestroy);

        var me = this,
            layout = me.layout,
            hasLayout = layout && me.rendered,

            
            isDestroying = component.destroying || doDestroy,
            floating = component.floating;

        if (floating) {
            me.floatingItems.remove(component);
        } else {
            me.items.remove(component);
        }

        
        if (hasLayout && !floating) {
            
            if (layout.running) {
                Ext.AbstractComponent.cancelLayout(component, isDestroying);
            }
            layout.onRemove(component, isDestroying);
        }

        component.onRemoved(isDestroying);

        me.onRemove(component, isDestroying);

        
        if (doDestroy) {
            component.destroy();
        }
        
        else {
            if (hasLayout && !floating) {
                layout.afterRemove(component);       
            }
            if (me.detachOnRemove && component.rendered) {
                me.detachComponent(component);
            }
        }
    },
    
    
    detachComponent: function(component){
        Ext.getDetachedBody().appendChild(component.getEl());
    },

    
    removeAll : function(autoDestroy) {
        var me = this,
            removeItems = me.items.items.slice().concat(me.floatingItems.items),
            items = [],
            i = 0,
            len = removeItems.length,
            item;

        
        me.suspendLayouts();
        for (; i < len; i++) {
            item = removeItems[i];
            me.remove(item, autoDestroy);

            if (item.ownerCt !== me) {
                items.push(item);
            }
        }

        
        me.resumeLayouts(!!len);
        return items;
    },

    
    getRefItems : function(deep) {
        var me = this,
            items = me.items.items,
            len = items.length,
            i = 0,
            item,
            result = [];

        for (; i < len; i++) {
            item = items[i];
            result[result.length] = item;
            if (deep && item.getRefItems) {
                result.push.apply(result, item.getRefItems(true));
            }
        }

        
        items = me.floatingItems.items;
        len = items.length;
        for (i = 0; i < len; i++) {
            item = items[i];
            result[result.length] = item;
            if (deep && item.getRefItems) {
                result.push.apply(result, item.getRefItems(true));
            }
        }

        return result;
    },

    
    cascade : function(fn, scope, origArgs){
        var me = this,
            cs = me.items ? me.items.items : [],
            len = cs.length,
            i = 0,
            c,
            args = origArgs ? origArgs.concat(me) : [me],
            componentIndex = args.length - 1;

        if (fn.apply(scope || me, args) !== false) {
            for (; i < len; i++){
                c = cs[i];
                if (c.cascade) {
                    c.cascade(fn, scope, origArgs);
                } else {
                    args[componentIndex] = c;
                    fn.apply(scope || cs, args);
                }
            }
        }
        return this;
    },

    
    isAncestor: function(possibleDescendant) {
        while (possibleDescendant) {
            if (possibleDescendant.ownerCt === this) {
                return true;
            }
            possibleDescendant = possibleDescendant.ownerCt;
        }
    },

    
    getComponent : function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }
        
        var c = this.items.get(comp);
             
        
        if (!c && typeof comp != 'number') {
            c = this.floatingItems.get(comp);
        }

        return c;
    },

    
    contains: function(comp, deep) {
        var result = false;
        if (deep) {
            this.cascade(function(c) {
                
                if (c.contains && c.contains(comp)) {
                    result = true;
                    return false;
                }
            });
            return result;
        } else {
            return this.items.contains(comp) || this.floatingItems.contains(comp);
        }
    },

    nextChild: function(child, selector) {
        var me = this,
            result,
            childIndex = me.items.indexOf(child);

        if (childIndex !== -1) {
            result = selector ? Ext.ComponentQuery(selector, me.items.items.slice(childIndex + 1)) : me.items.getAt(childIndex + 1);
            if (!result && me.ownerCt) {
                result = me.ownerCt.nextChild(me, selector);
            }
        }
        return result;
    },

    prevChild: function(child, selector) {
        var me = this,
            result,
            childIndex = me.items.indexOf(child);

        if (childIndex !== -1) {
            result = selector ? Ext.ComponentQuery(selector, me.items.items.slice(childIndex + 1)) : me.items.getAt(childIndex + 1);
            if (!result && me.ownerCt) {
                result = me.ownerCt.nextChild(me, selector);
            }
        }
        return result;
    },

    
    
    
    enable: function() {
        this.callParent(arguments);

        var itemsToDisable = this.getChildItemsToDisable(),
            length         = itemsToDisable.length,
            item, i;

        for (i = 0; i < length; i++) {
            item = itemsToDisable[i];

            if (item.resetDisable) {
                item.enable();
            }
        }

        return this;
    },

    
    
    
    disable: function() {
        this.callParent(arguments);

        var itemsToDisable = this.getChildItemsToDisable(),
            length         = itemsToDisable.length,
            item, i;

        for (i = 0; i < length; i++) {
            item = itemsToDisable[i];

            if (item.resetDisable !== false && !item.disabled) {
                item.disable();
                item.resetDisable = true;
            }
        }

        return this;
    },
    
    
    getChildItemsToDisable: function(){
        return this.query('[isFormField],button');
    },

    
    
    beforeDestroy : function() {
        var me = this,
            items = me.items,
            floatingItems = me.floatingItems,
            c;

        if (items) {
            while ((c = items.first())) {
                me.doRemove(c, true);
            }
        }
        
        if (floatingItems) {
            while ((c = floatingItems.first())) {
                me.doRemove(c, true);
            }
        }

        Ext.destroy(
            me.layout
        );
        me.callParent();
    }
});


Ext.define('Ext.container.Container', {
    extend:  Ext.container.AbstractContainer ,
    alias: 'widget.container',
    alternateClassName: 'Ext.Container',

    
    getChildByElement: function(el, deep) {
        var item,
            itemEl,
            i = 0,
            it = this.getRefItems(),
            ln = it.length;

        el = Ext.getDom(el);
        for (; i < ln; i++) {
            item = it[i];
            itemEl = item.getEl();
            if (itemEl && ((itemEl.dom === el) || itemEl.contains(el))) {
                return (deep && item.getChildByElement) ? item.getChildByElement(el, deep) : item;
            }
        }
        return null;
    }
});


Ext.define('Ext.layout.container.Editor', {

    

    alias: 'layout.editor',

    extend:  Ext.layout.container.Container ,

    

    autoSizeDefault: {
        width: 'field',
        height: 'field'    
    },

    sizePolicies: {
        
        $: {
            
            $: {
                readsWidth: 1,
                readsHeight: 1,
                setsWidth: 0,
                setsHeight: 0
            },
            boundEl: {
                readsWidth: 1,
                readsHeight: 0,
                setsWidth: 0,
                setsHeight: 1
            }
        },

        boundEl: {
            
            $: {
                readsWidth: 0,
                readsHeight: 1,
                setsWidth: 1,
                setsHeight: 0
            },
            boundEl: {
                readsWidth: 0,
                readsHeight: 0,
                setsWidth: 1,
                setsHeight: 1
            }
        }
    },

    getItemSizePolicy: function (item) {
        var me = this,
            autoSize = me.owner.autoSize,
            key = autoSize && autoSize.width,
            policy = me.sizePolicies;

        policy = policy[key] || policy.$;

        key = autoSize && autoSize.height;
        policy = policy[key] || policy.$;

        return policy;
    },

    calculate: function(ownerContext) {
        var me = this,
            owner = me.owner,
            autoSize = owner.autoSize,
            fieldWidth,
            fieldHeight;
            
        if (autoSize === true) {
            autoSize = me.autoSizeDefault;
        }

        
        if (autoSize) {
            fieldWidth  = me.getDimension(owner, autoSize.width,  'getWidth',  owner.width);
            fieldHeight = me.getDimension(owner, autoSize.height, 'getHeight', owner.height);
        }

        
        ownerContext.childItems[0].setSize(fieldWidth, fieldHeight);

        
        ownerContext.setWidth(fieldWidth);
        ownerContext.setHeight(fieldHeight);

        
        ownerContext.setContentSize(fieldWidth || owner.field.getWidth(),
                                    fieldHeight || owner.field.getHeight());
    },

    getDimension: function(owner, type, getMethod, ownerSize){
        switch (type) {
            
            case 'boundEl':
                return owner.boundEl[getMethod]();

            
            case 'field':
                return undefined;

            
            default:
                return ownerSize;
        }
    }
});


Ext.define('Ext.Editor', {

    

    extend:  Ext.container.Container ,

    alias: 'widget.editor',

                                              

    

   layout: 'editor',

    

    
    allowBlur: true,

    

    
    revertInvalid: true,

    

    

    
    value : '',

    
    alignment: 'c-c?',

    
    offsets: [0, 0],

    
    shadow : 'frame',

    
    constrain : false,

    
    swallowKeys : true,

    
    completeOnEnter : true,

    
    cancelOnEsc : true,

    
    updateEl : false,

    
    
    
    focusOnToFront: false,

    

    
    hidden: true,
    baseCls: Ext.baseCSSPrefix + 'editor',

    initComponent : function() {
        var me = this,
            field = me.field = Ext.ComponentManager.create(me.field, 'textfield');

        Ext.apply(field, {
            inEditor: true,
            msgTarget: field.msgTarget == 'title' ? 'title' :  'qtip'
        });
        me.mon(field, {
            scope: me,
            blur: me.onFieldBlur,
            specialkey: me.onSpecialKey
        });

        if (field.grow) {
            me.mon(field, 'autosize', me.onFieldAutosize,  me, {delay: 1});
        }
        me.floating = {
            constrain: me.constrain
        };
        me.items = field;

        me.callParent(arguments);

        me.addEvents(
            
            'beforestartedit',

            
            'startedit',

            
            'beforecomplete',
            
            'complete',
            
            'canceledit',
            
            'specialkey'
        );
    },

    
    onFieldAutosize: function(){
        this.updateLayout();
    },

    
    afterRender : function(ct, position) {
        var me = this,
            field = me.field,
            inputEl = field.inputEl;

        me.callParent(arguments);

        
        if (inputEl) {
            inputEl.dom.name = '';
            if (me.swallowKeys) {
                inputEl.swallowEvent([
                    'keypress', 
                    'keydown'   
                ]);
            }
        }
    },

    
    onSpecialKey : function(field, event) {
        var me = this,
            key = event.getKey(),
            complete = me.completeOnEnter && key == event.ENTER,
            cancel = me.cancelOnEsc && key == event.ESC;

        if (complete || cancel) {
            event.stopEvent();
            
            
            Ext.defer(function() {
                if (complete) {
                    me.completeEdit();
                } else {
                    me.cancelEdit();
                }
                if (field.triggerBlur) {
                    field.triggerBlur(event);
                }
            }, 10);
        }

        me.fireEvent('specialkey', me, field, event);
    },

    
    startEdit : function(el, value) {
        var me = this,
            field = me.field;

        me.completeEdit();
        me.boundEl = Ext.get(el);
        value = Ext.isDefined(value) ? value : Ext.String.trim(me.boundEl.dom.innerText || me.boundEl.dom.innerHTML);

        if (!me.rendered) {
            
            
            
            
            if (me.ownerCt) {
                me.parentEl = me.ownerCt.el;
                me.parentEl.position();
            }
            me.render(me.parentEl || document.body);
        }

        if (me.fireEvent('beforestartedit', me, me.boundEl, value) !== false) {
            me.startValue = value;
            me.show();
            
            field.suspendEvents();
            field.reset();
            field.setValue(value);
            field.resumeEvents();
            me.realign(true);
            field.focus();
            if (field.autoSize) {
                field.autoSize();
            }
            me.editing = true;
        }
    },

    
    realign : function(autoSize) {
        var me = this;
        if (autoSize === true) {
            me.updateLayout();
        }
        me.alignTo(me.boundEl, me.alignment, me.offsets);
    },

    
    completeEdit : function(remainVisible) {
        var me = this,
            field = me.field,
            value;

        if (!me.editing) {
            return;
        }

        
        if (field.assertValue) {
            field.assertValue();
        }

        value = me.getValue();
        if (!field.isValid()) {
            if (me.revertInvalid !== false) {
                me.cancelEdit(remainVisible);
            }
            return;
        }

        if (String(value) === String(me.startValue) && me.ignoreNoChange) {
            me.hideEdit(remainVisible);
            return;
        }

        if (me.fireEvent('beforecomplete', me, value, me.startValue) !== false) {
            
            value = me.getValue();
            if (me.updateEl && me.boundEl) {
                me.boundEl.update(value);
            }
            me.hideEdit(remainVisible);
            me.fireEvent('complete', me, value, me.startValue);
        }
    },

    
    onShow : function() {
        var me = this;

        me.callParent(arguments);
        if (me.hideEl !== false) {
            me.boundEl.hide();
        }
        me.fireEvent('startedit', me, me.boundEl, me.startValue);
    },

    
    cancelEdit : function(remainVisible) {
        var me = this,
            startValue = me.startValue,
            field = me.field,
            value;

        if (me.editing) {
            value = me.getValue();
            
            field.suspendEvents();
            me.setValue(startValue);
            field.resumeEvents();
            me.hideEdit(remainVisible);
            me.fireEvent('canceledit', me, value, startValue);
        }
    },

    
    hideEdit: function(remainVisible) {
        if (remainVisible !== true) {
            this.editing = false;
            this.hide();
        }
    },

    
    onFieldBlur : function(field, e) {
        var me = this,
            target = Ext.Element.getActiveElement();

        
        if(me.allowBlur === true && me.editing && me.selectSameEditor !== true) {
            me.completeEdit();
        }

        
        if (Ext.fly(target).isFocusable() || target.getAttribute('tabIndex')) {
            target.focus();
        }
    },

    
    onHide : function() {
        var me = this,
            field = me.field;

        if (me.editing) {
            me.completeEdit();
            return;
        }
        
        
        if (field.hasFocus && field.triggerBlur) {
            field.triggerBlur();
        }
        if (field.collapse) {
            field.collapse();
        }

        
        if (me.hideEl !== false) {
            me.boundEl.show();
        }
        me.callParent(arguments);
    },

    
    setValue : function(value) {
        this.field.setValue(value);
    },

    
    getValue : function() {
        return this.field.getValue();
    },

    beforeDestroy : function() {
        var me = this;

        Ext.destroy(me.field);
        delete me.field;
        delete me.parentEl;
        delete me.boundEl;

        me.callParent(arguments);
    }
});


Ext.define('Ext.util.KeyMap', {
    alternateClassName: 'Ext.KeyMap',

    

    

    

    

    
    eventName: 'keydown',

    constructor: function(config) {
        var me = this;

        
        
        if ((arguments.length !== 1) || (typeof config === 'string') || config.dom || config.tagName || config === document || config.isComponent) {
            me.legacyConstructor.apply(me, arguments);
            return;
        }

        Ext.apply(me, config);
        me.bindings = [];

        if (!me.target.isComponent) {
            me.target = Ext.get(me.target);
        }

        if (me.binding) {
            me.addBinding(me.binding);
        } else if (config.key) {
            me.addBinding(config);
        }
        me.enable();
    },

    
    legacyConstructor: function(el, binding, eventName){
        var me = this;

        Ext.apply(me, {
            target: Ext.get(el),
            eventName: eventName || me.eventName,
            bindings: []
        });
        if (binding) {
            me.addBinding(binding);
        }
        me.enable();
    },

    
    addBinding : function(binding){
        var me = this,
            keyCode = binding.key,
            i,
            len;

        if (me.processing) {
            me.bindings = bindings.slice(0);
        }
        
        if (Ext.isArray(binding)) {
            for (i = 0, len = binding.length; i < len; i++) {
                me.addBinding(binding[i]);
            }
            return;
        }

        me.bindings.push(Ext.apply({
            keyCode: me.processKeys(keyCode)
        }, binding));
    },
    
    
    removeBinding: function(binding){
        var me = this,
            bindings = me.bindings,
            len = bindings.length,
            i, item, keys;
            
        if (me.processing) {
            me.bindings = bindings.slice(0);
        }
        
        keys = me.processKeys(binding.key);
        for (i = 0; i < len; ++i) {
            item = bindings[i];
            if (item.fn === binding.fn && item.scope === binding.scope) {
                if (binding.alt == item.alt && binding.crtl == item.crtl && binding.shift == item.shift) {
                    if (Ext.Array.equals(item.keyCode, keys)) {
                        Ext.Array.erase(me.bindings, i, 1);
                        return;
                    }
                }
            }
        }
    },
    
    processKeys: function(keyCode){
        var processed = false,
            key, keys, keyString, len, i;
            
        if (Ext.isString(keyCode)) {
            keys = [];
            keyString = keyCode.toUpperCase();

            for (i = 0, len = keyString.length; i < len; ++i){
                keys.push(keyString.charCodeAt(i));
            }
            keyCode = keys;
            processed = true;
        }

        if (!Ext.isArray(keyCode)) {
            keyCode = [keyCode];
        }

        if (!processed) {
            for (i = 0, len = keyCode.length; i < len; ++i) {
                key = keyCode[i];
                if (Ext.isString(key)) {
                    keyCode[i] = key.toUpperCase().charCodeAt(0);
                }
            }
        }
        return keyCode;
    },

    
    handleTargetEvent: (function() {
        var tagRe = /input|textarea/i;

        return function(event) {
            var me = this,
                bindings, i, len,
                target, contentEditable;

            if (me.enabled) { 
                bindings = me.bindings;
                i = 0;
                len = bindings.length;

                
                event = me.processEvent.apply(me||me.processEventScope, arguments);

                
                if (me.ignoreInputFields) {
                    target = event.target;
                    contentEditable = target.contentEditable;
                    
                    
                    
                    if (tagRe.test(target.tagName) || (contentEditable === '' || contentEditable === 'true')) {
                        return;
                    }
                }

                
                
                if (!event.getKey) {
                    return event;
                }
                me.processing = true;
                for(; i < len; ++i){
                    me.processBinding(bindings[i], event);
                }
                me.processing = false;
            }
        }
    }()),

    
    processEvent: Ext.identityFn,

    
    processBinding: function(binding, event){
        if (this.checkModifiers(binding, event)) {
            var key = event.getKey(),
                handler = binding.fn || binding.handler,
                scope = binding.scope || this,
                keyCode = binding.keyCode,
                defaultEventAction = binding.defaultEventAction,
                i,
                len,
                keydownEvent = new Ext.EventObjectImpl(event);


            for (i = 0, len = keyCode.length; i < len; ++i) {
                if (key === keyCode[i]) {
                    if (handler.call(scope, key, event) !== true && defaultEventAction) {
                        keydownEvent[defaultEventAction]();
                    }
                    break;
                }
            }
        }
    },

    
    checkModifiers: function(binding, e) {
        var keys = ['shift', 'ctrl', 'alt'],
            i = 0,
            len = keys.length,
            val, key;

        for (; i < len; ++i){
            key = keys[i];
            val = binding[key];
            if (!(val === undefined || (val === e[key + 'Key']))) {
                return false;
            }
        }
        return true;
    },

    
    on: function(key, fn, scope) {
        var keyCode, shift, ctrl, alt;
        if (Ext.isObject(key) && !Ext.isArray(key)) {
            keyCode = key.key;
            shift = key.shift;
            ctrl = key.ctrl;
            alt = key.alt;
        } else {
            keyCode = key;
        }
        this.addBinding({
            key: keyCode,
            shift: shift,
            ctrl: ctrl,
            alt: alt,
            fn: fn,
            scope: scope
        });
    },
    
    
    un: function(key, fn, scope) {
        var keyCode, shift, ctrl, alt;
        if (Ext.isObject(key) && !Ext.isArray(key)) {
            keyCode = key.key;
            shift = key.shift;
            ctrl = key.ctrl;
            alt = key.alt;
        } else {
            keyCode = key;
        }
        this.removeBinding({
            key: keyCode,
            shift: shift,
            ctrl: ctrl,
            alt: alt,
            fn: fn,
            scope: scope
        });
    },

    
    isEnabled : function() {
        return this.enabled;
    },

    
    enable: function() {
        var me = this;
        
        if (!me.enabled) {
            me.target.on(me.eventName, me.handleTargetEvent, me);
            me.enabled = true;
        }
    },

    
    disable: function() {
        var me = this;
        
        if (me.enabled) {
            me.target.removeListener(me.eventName, me.handleTargetEvent, me);
            me.enabled = false;
        }
    },

    
    setDisabled : function(disabled) {
        if (disabled) {
            this.disable();
        } else {
            this.enable();
        }
    },

    
    destroy: function(removeTarget) {
        var me = this,
            target = me.target;

        me.bindings = [];
        me.disable();
        if (removeTarget === true) {
            if (target.isComponent) {
                target.destroy();
            } else {
                target.remove();
            }
        }
        delete me.target;
    }
});


Ext.define('Ext.util.KeyNav', {
    alternateClassName: 'Ext.KeyNav',

                                  

    statics: {
        keyOptions: {
            left: 37,
            right: 39,
            up: 38,
            down: 40,
            space: 32,
            pageUp: 33,
            pageDown: 34,
            del: 46,
            backspace: 8,
            home: 36,
            end: 35,
            enter: 13,
            esc: 27,
            tab: 9
        }
    },

    constructor: function(config) {
        var me = this;
        if (arguments.length === 2) {
            me.legacyConstructor.apply(me, arguments);
            return;
        }
        me.setConfig(config);
    },

    
    legacyConstructor: function(el, config) {
        this.setConfig(Ext.apply({
            target: el
        }, config));
    },

    
    setConfig: function(config) {
        var me = this,
            keymapCfg = {
                target: config.target,
                ignoreInputFields: config.ignoreInputFields,
                eventName: me.getKeyEvent('forceKeyDown' in config ? config.forceKeyDown : me.forceKeyDown, config.eventName)
            },
            map, keyCodes, defaultScope, keyName, binding;

        if (me.map) {
            me.map.destroy();
        }

        if (config.processEvent) {
            keymapCfg.processEvent = config.processEvent;
            keymapCfg.processEventScope = config.processEventScope||me;
        }

        
        if (config.keyMap) {
            map = me.map = config.keyMap;
        }
        
        else {
            map = me.map = new Ext.util.KeyMap(keymapCfg);
            me.destroyKeyMap = true;
        }
        keyCodes = Ext.util.KeyNav.keyOptions;
        defaultScope = config.scope || me;

        for (keyName in keyCodes) {
            if (keyCodes.hasOwnProperty(keyName)) {

                
                
                if (binding = config[keyName]) {
                    if (typeof binding === 'function') {
                        binding = {
                            handler: binding,
                            defaultEventAction: (config.defaultEventAction !== undefined) ? config.defaultEventAction : me.defaultEventAction
                        };
                    }
                    map.addBinding({
                        key: keyCodes[keyName],
                        handler: Ext.Function.bind(me.handleEvent, binding.scope||defaultScope, binding.handler||binding.fn, true),
                        defaultEventAction: (binding.defaultEventAction !== undefined) ? binding.defaultEventAction : me.defaultEventAction
                    });
                }
            }
        }

        map.disable();
        if (!config.disabled) {
            map.enable();
        }
    },

    
    handleEvent: function(keyCode, event, handler){
        return handler.call(this, event);
    },

    
    disabled: false,

    
    defaultEventAction: "stopEvent",

    
    forceKeyDown: false,

    

    
    eventName: 'keypress',

    

    

    

    

    
    destroy: function(removeEl) {
        if (this.destroyKeyMap) {
            this.map.destroy(removeEl);
        }
        delete this.map;
    },

    
    enable: function() {
        
        if (this.map) {
            this.map.enable();
            this.disabled = false;
        }
    },

    
    disable: function() {
        
        if (this.map) {
            this.map.disable();
        }
        this.disabled = true;
    },

    
    setDisabled : function(disabled) {
        this.map.setDisabled(disabled);
        this.disabled = disabled;
    },

    
    getKeyEvent: function(forceKeyDown, configuredEventName) {
        if (forceKeyDown || (Ext.EventManager.useKeyDown && !configuredEventName)) {
            return 'keydown';
        } else {
            return configuredEventName||this.eventName;
        }
    }
});


Ext.define('Ext.FocusManager', {
    singleton: true,
    alternateClassName: ['Ext.FocusMgr' ],

    mixins: {
        observable:  Ext.util.Observable 
    },

               
                                
                        
                               
                             
                           
                         
      

    
    enabled: false,

    

    focusElementCls: Ext.baseCSSPrefix + 'focus-element',

    focusFrameCls: Ext.baseCSSPrefix + 'focus-frame',

    
    whitelist: [
        'textfield'
    ],

    constructor: function(config) {
        var me = this,
            CQ = Ext.ComponentQuery;

        me.mixins.observable.constructor.call(me, config);

        me.addEvents(
            
            'beforecomponentfocus',

            
            'componentfocus',

            
            'disable',

            
            'enable'
        );

        me.focusTask = new Ext.util.DelayedTask(me.handleComponentFocus, me);

        
        Ext.override(Ext.AbstractComponent, {
            onFocus: function() {
                this.callParent(arguments);
                if (me.enabled && this.hasFocus) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentFocus.apply(me, arguments);
                }
            },
            onBlur: function() {
                this.callParent(arguments);
                if (me.enabled && !this.hasFocus) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentBlur.apply(me, arguments);
                }
            },
            onDestroy: function() {
                this.callParent(arguments);
                if (me.enabled) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentDestroy.apply(me, arguments);
                }
            }
        });
        Ext.override(Ext.Component, {
            afterHide: function() {
                this.callParent(arguments);
                if (me.enabled) {
                    Array.prototype.unshift.call(arguments, this);
                    me.onComponentHide.apply(me, arguments);
                }
            }
        });
        
        
        me.keyNav = new Ext.util.KeyNav(Ext.getDoc(), {
            disabled: true,
            scope: me,

            backspace: me.focusLast,
            enter: me.navigateIn,
            esc: me.navigateOut,
            tab: me.navigateSiblings,
            space: me.navigateIn,
            del: me.focusLast,
            left: me.navigateSiblings,
            right: me.navigateSiblings,
            down: me.navigateSiblings,
            up: me.navigateSiblings
        });

        me.focusData = {};
        me.subscribers = new Ext.util.HashMap();
        me.focusChain = {};

        
        Ext.apply(CQ.pseudos, {
            
            nextFocus: function(cmps, idx, step) {
                step = step || 1;
                idx = parseInt(idx, 10);

                var len = cmps.length,
                    i = idx, c;

                for (;;) {
                    
                    if ((i += step) >= len) {
                        i = 0;
                    } else if (i < 0) {
                        i = len - 1;
                    }

                    
                    if (i === idx) {
                        return [];
                    }

                    
                    if ((c = cmps[i]).isFocusable()) {
                        return [c];
                    }
                }

                return [];
            },

            prevFocus: function(cmps, idx) {
                return this.nextFocus(cmps, idx, -1);
            },

            root: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;

                for (; i < len; i++) {
                    c = cmps[i];
                    if (!c.ownerCt) {
                        results.push(c);
                    }
                }

                return results;
            }
        });
    },

    
    addXTypeToWhitelist: function(xtype) {
        var me = this;

        if (Ext.isArray(xtype)) {
            Ext.Array.forEach(xtype, me.addXTypeToWhitelist, me);
            return;
        }

        if (!Ext.Array.contains(me.whitelist, xtype)) {
            me.whitelist.push(xtype);
        }
    },

    clearComponent: function(cmp) {
        clearTimeout(this.cmpFocusDelay);
        if (!cmp.isDestroyed) {
            cmp.blur();
        }
    },

    
    disable: function() {
        var me = this;

        if (!me.enabled) {
            return;
        }

        delete me.options;
        me.enabled = false;

        me.removeDOM();

        
        me.keyNav.disable();

        me.fireEvent('disable', me);
    },

    
    enable: function(options) {
        var me = this;

        if (options === true) {
            options = { focusFrame: true };
        }
        me.options = options = options || {};

        if (me.enabled) {
            return;
        }

        
        me.enabled = true;
        me.initDOM(options);

        
        me.keyNav.enable();

        
        me.focusEl.focus();
        delete me.focusedCmp;

        me.fireEvent('enable', me);
    },

    focusLast: function(e) {
        var me = this;

        if (me.isWhitelisted(me.focusedCmp)) {
            return true;
        }

        
        if (me.previousFocusedCmp) {
            me.previousFocusedCmp.focus();
        }
    },

    getRootComponents: function() {
        var CQ = Ext.ComponentQuery,
            inline = CQ.query(':focusable:root:not([floating])'),
            floating = CQ.query(':focusable:root[floating]');

        
        
        floating.sort(function(a, b) {
            return a.el.getZIndex() > b.el.getZIndex();
        });

        return floating.concat(inline);
    },

    initDOM: function(options) {
        var me = this,
            cls = me.focusFrameCls,
            needListeners = Ext.ComponentQuery.query('{getFocusEl()}:not([focusListenerAdded])'),
            i = 0, len = needListeners.length;

        if (!Ext.isReady) {
            return Ext.onReady(me.initDOM, me);
        }

        
        
        
        for (; i < len; i++) {
            needListeners[i].addFocusListener();
        }

        
        if (!me.focusEl) {
            me.focusEl = Ext.getBody();
            me.focusEl.dom.tabIndex = -1;
        }

        
        if (!me.focusFrame && options.focusFrame) {
            me.focusFrame = Ext.getBody().createChild({
                cls: cls,
                children: [
                    { cls: cls + '-top' },
                    { cls: cls + '-bottom' },
                    { cls: cls + '-left' },
                    { cls: cls + '-right' }
                ],
                style: 'top: -100px; left: -100px;'
            });
            me.focusFrame.setVisibilityMode(Ext.Element.DISPLAY);
            me.focusFrame.hide().setLocalXY(0, 0);
        }
    },

    isWhitelisted: function(cmp) {
        return cmp && Ext.Array.some(this.whitelist, function(x) {
            return cmp.isXType(x);
        });
    },

    navigateIn: function(e) {
        var me = this,
            focusedCmp = me.focusedCmp,
            defaultRoot,
            firstChild;

        if (me.isWhitelisted(focusedCmp)) {
            return true;
        }

        if (!focusedCmp) {
            
            defaultRoot = me.getRootComponents()[0];
            if (defaultRoot) {
                
                
                if (defaultRoot.getFocusEl() === me.focusEl) {
                    me.focusEl.blur();
                }
                defaultRoot.focus();
            }
        } else {
            
            
            firstChild = focusedCmp.hasFocus ? Ext.ComponentQuery.query('>:focusable', focusedCmp)[0] : focusedCmp;
            if (firstChild) {
                firstChild.focus();
            } else {
                
                if (Ext.isFunction(focusedCmp.onClick)) {
                    e.button = 0;
                    focusedCmp.onClick(e);
                    if (focusedCmp.isVisible(true)) {
                        focusedCmp.focus();
                    } else {
                        me.navigateOut();
                    }
                }
            }
        }
    },

    navigateOut: function(e) {
        var me = this,
            parent;

        if (!me.focusedCmp || !(parent = me.focusedCmp.up(':focusable'))) {
            me.focusEl.focus();
        } else {
            parent.focus();
        }

        
        
        
        return true;
    },

    navigateSiblings: function(e, source, parent) {
        var me = this,
            src = source || me,
            key = e.getKey(),
            EO = Ext.EventObject,
            goBack = e.shiftKey || key == EO.LEFT || key == EO.UP,
            checkWhitelist = key == EO.LEFT || key == EO.RIGHT || key == EO.UP || key == EO.DOWN,
            nextSelector = goBack ? 'prev' : 'next',
            idx, next, focusedCmp, siblings;

        focusedCmp = (src.focusedCmp && src.focusedCmp.comp) || src.focusedCmp;
        if (!focusedCmp && !parent) {
            return true;
        }

        if (checkWhitelist && me.isWhitelisted(focusedCmp)) {
            return true;
        }

        
        if (!focusedCmp || focusedCmp.is(':root')) {
            siblings = me.getRootComponents();
        } else {
            
            parent = parent || focusedCmp.up();
            if (parent) {
                siblings = parent.getRefItems();
            }
        }


        
        if (siblings) {
            idx = focusedCmp ? Ext.Array.indexOf(siblings, focusedCmp) : -1;
            next = Ext.ComponentQuery.query(':' + nextSelector + 'Focus(' + idx + ')', siblings)[0];
            if (next && focusedCmp !== next) {
                next.focus();
                return next;
            }
        }
    },

    onComponentBlur: function(cmp, e) {
        var me = this;

        if (me.focusedCmp === cmp) {
            me.previousFocusedCmp = cmp;
            delete me.focusedCmp;
        }

        if (me.focusFrame) {
            me.focusFrame.hide();
        }
    },

    onComponentFocus: function(cmp, e) {
        var me = this,
            chain = me.focusChain,
            parent;

        if (!cmp.isFocusable()) {
            me.clearComponent(cmp);

            
            
            
            
            if (chain[cmp.id]) {
                return;
            }

            
            parent = cmp.up();
            if (parent) {
                
                
                
                chain[cmp.id] = true;
                parent.focus();
            }

            return;
        }
        
        me.focusChain = {};

        
        
        
        me.focusTask.delay(10, null, null, [cmp, cmp.getFocusEl()]);
    },

    handleComponentFocus: function(cmp, focusEl) {
        var me = this,
            cls,
            ff,
            box,
            bt,
            bl,
            bw,
            bh,
            ft,
            fb,
            fl,
            fr;

        if (me.fireEvent('beforecomponentfocus', me, cmp, me.previousFocusedCmp) === false) {
            me.clearComponent(cmp);
            return;
        }

        me.focusedCmp = cmp;

        
        if (me.shouldShowFocusFrame(cmp)) {
            cls = '.' + me.focusFrameCls + '-';
            ff = me.focusFrame;
            
            
            box = (focusEl.dom ? focusEl : focusEl.el).getBox();

            
            
            
            bt = box.top;
            bl = box.left;
            bw = box.width;
            bh = box.height;
            ft = ff.child(cls + 'top');
            fb = ff.child(cls + 'bottom');
            fl = ff.child(cls + 'left');
            fr = ff.child(cls + 'right');

            ft.setWidth(bw).setLocalXY(bl, bt);
            fb.setWidth(bw).setLocalXY(bl, bt + bh - 2);
            fl.setHeight(bh - 2).setLocalXY(bl, bt + 2);
            fr.setHeight(bh - 2).setLocalXY(bl + bw - 2, bt + 2);

            ff.show();
        }

        me.fireEvent('componentfocus', me, cmp, me.previousFocusedCmp);
    },

    onComponentHide: function(cmp) {
        var me = this,
            cmpHadFocus = false,
            focusedCmp = me.focusedCmp,
            parent;

        if (focusedCmp) {
            
            
            cmpHadFocus = cmp.hasFocus || (cmp.isContainer && cmp.isAncestor(me.focusedCmp));
        }

        me.clearComponent(cmp);

        
        if (cmpHadFocus && (parent = cmp.up(':focusable'))) {
            parent.focus();
        } else {
            me.focusEl.focus();
        }
    },

    onComponentDestroy: function() {

    },

    removeDOM: function() {
        var me = this;

        
        
        if (me.enabled || me.subscribers.length) {
            return;
        }

        Ext.destroy(
            me.focusFrame
        );
        delete me.focusEl;
        delete me.focusFrame;
    },

    
    removeXTypeFromWhitelist: function(xtype) {
        var me = this;

        if (Ext.isArray(xtype)) {
            Ext.Array.forEach(xtype, me.removeXTypeFromWhitelist, me);
            return;
        }

        Ext.Array.remove(me.whitelist, xtype);
    },

    setupSubscriberKeys: function(container, keys) {
        var me = this,
            el = container.getFocusEl(),
            scope = keys.scope,
            handlers = {
                backspace: me.focusLast,
                enter: me.navigateIn,
                esc: me.navigateOut,
                scope: me
            },

            navSiblings = function(e) {
                if (me.focusedCmp === container) {
                    
                    
                    
                    return me.navigateSiblings(e, me, container);
                } else {
                    return me.navigateSiblings(e);
                }
            };

        Ext.iterate(keys, function(key, cb) {
            handlers[key] = function(e) {
                var ret = navSiblings(e);

                if (Ext.isFunction(cb) && cb.call(scope || container, e, ret) === true) {
                    return true;
                }

                return ret;
            };
        }, me);

        return new Ext.util.KeyNav(el, handlers);
    },

    shouldShowFocusFrame: function(cmp) {
        var me = this,
            opts = me.options || {};

        
        
        
        if (!me.focusFrame || !cmp) {
            return false;
        }

        
        if (opts.focusFrame) {
            return true;
        }

        if (me.focusData[cmp.id].focusFrame) {
            return true;
        }

        return false;
    }
});


Ext.define('Ext.Img', {
    extend:  Ext.Component ,
    alias: ['widget.image', 'widget.imagecomponent'],

    autoEl: 'img',

    baseCls: Ext.baseCSSPrefix + 'img',

    
    src: '',

    
    alt: '',

    
    title: '',

    
    imgCls: '',

    

    initComponent: function() {
        if (this.glyph) {
            this.autoEl = 'div';
        }
        this.callParent();
    },

    getElConfig: function() {
        var me = this,
            config = me.callParent(),
            glyphFontFamily = Ext._glyphFontFamily,
            glyph = me.glyph,
            img, glyphParts;

        
        
        if (me.autoEl == 'img') {
            img = config;
        } else if (me.glyph) {
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                glyphFontFamily = glyphParts[1];
            }
            config.html = '&#' + glyph + ';';
            if (glyphFontFamily) {
                config.style = 'font-family:' + glyphFontFamily;
            }
        } else {
            config.cn = [img = {
                tag: 'img',
                id: me.id + '-img'
            }];
        }

        if (img) {
            if (me.imgCls) {
                img.cls = (img.cls ? img.cls + ' ' : '') + me.imgCls;
            }

            img.src = me.src || Ext.BLANK_IMAGE_URL;
        }

        if (me.alt) {
            (img || config).alt = me.alt;
        }
        if (me.title) {
            (img || config).title = me.title;
        }

        return config;
    },

    onRender: function () {
        var me = this,
            el;

        me.callParent(arguments);

        el = me.el;
        me.imgEl = (me.autoEl == 'img') ? el : el.getById(me.id + '-img');
    },

    onDestroy: function () {
        Ext.destroy(this.imgEl);
        this.imgEl = null;
        this.callParent();
    },

    
    setSrc: function(src) {
        var me = this,
            imgEl = me.imgEl;

        me.src = src;

        if (imgEl) {
            imgEl.dom.src = src || Ext.BLANK_IMAGE_URL;
        }
    },

    setGlyph: function(glyph) {
        var me = this,
            glyphFontFamily = Ext._glyphFontFamily,
            glyphParts, dom;

        if (glyph != me.glyph) {
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                glyphFontFamily = glyphParts[1];
            }

            dom = me.el.dom;

            dom.innerHTML = '&#' + glyph + ';';
            if (glyphFontFamily) {
                dom.style = 'font-family:' + glyphFontFamily;
            }
        }
    }
});


Ext.define('Ext.util.Bindable', {

    
    bindStore: function(store, initial, propertyName) {
        
        
        
        propertyName = propertyName || 'store';

        var me = this,
            oldStore = me[propertyName];

        if (!initial && oldStore) {
            
            me.onUnbindStore(oldStore, initial, propertyName);

            if (store !== oldStore && oldStore.autoDestroy) {
                oldStore.destroyStore();
            } else {
                me.unbindStoreListeners(oldStore);
            }
        }
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            me.bindStoreListeners(store);
            me.onBindStore(store, initial, propertyName);
        }
        me[propertyName] = store || null;
        return me;
    },

    
    getStore: function(){
        return this.store;
    },

    
    unbindStoreListeners: function(store) {
        
        var listeners = this.storeListeners;
        if (listeners) {
            store.un(listeners);
        }
    },

    
    bindStoreListeners: function(store) {
        
        var me = this,
            listeners = Ext.apply({}, me.getStoreListeners(store));

        if (!listeners.scope) {
            listeners.scope = me;
        }
        me.storeListeners = listeners;
        store.on(listeners);
    },

    
    getStoreListeners: Ext.emptyFn,

    
    onUnbindStore: Ext.emptyFn,

    
    onBindStore: Ext.emptyFn    
});


Ext.define('Ext.LoadMask', {

    extend:  Ext.Component ,

    alias: 'widget.loadmask',

    

    mixins: {
        floating:  Ext.util.Floating ,
        bindable:  Ext.util.Bindable 
    },

                                    

    
    
    

    

    
    
    msg : 'Loading...',
    

    
    msgCls : Ext.baseCSSPrefix + 'mask-loading',

    
    maskCls: Ext.baseCSSPrefix + 'mask',

    
    useMsg: true,

    
    useTargetEl: false,

    baseCls: Ext.baseCSSPrefix + 'mask-msg',

    childEls: [
        'msgEl',
        'msgTextEl'
    ],

    renderTpl: [
        '<div id="{id}-msgEl" class="{[values.$comp.msgCls]} ',
            Ext.baseCSSPrefix, 'mask-msg-inner{childElCls}">',
            '<div id="{id}-msgTextEl" class="', Ext.baseCSSPrefix ,'mask-msg-text',
                '{childElCls}"></div>',
        '</div>'
    ],

    
    floating: {
        shadow: 'frame'
    },

    
    focusOnToFront: false,

    
    
    bringParentToFront: false,

    
    constructor : function(config) {
        var me = this,
            comp;

        if (arguments.length === 2) {
            comp = config;
            config = arguments[1];
        } else {
            comp = config.target;
        }

        
        if (!comp.isComponent) {
            comp = Ext.get(comp);
            this.isElement = true;
        }

        me.ownerCt = comp;
        if (!this.isElement) {
            me.bindComponent(comp);
        }
        me.callParent([config]);

        if (me.store) {
            me.bindStore(me.store, true);
        }
    },

    bindComponent: function(comp) {
        var me = this,
            listeners = {
                scope: this,
                resize: me.sizeMask,
                added: me.onComponentAdded,
                removed: me.onComponentRemoved
            };
            
        if (comp.floating) {
            listeners.move = me.sizeMask;
            me.activeOwner = comp;
        } else if (comp.ownerCt) {
            me.onComponentAdded(comp.ownerCt);
        } else {
            
            me.preventBringToFront = true;
        }

        me.mon(comp, listeners);
        
        
        me.mon(me.hierarchyEventSource, {
            show: me.onContainerShow,
            hide: me.onContainerHide,
            expand: me.onContainerExpand,
            collapse: me.onContainerCollapse,
            scope: me
        });
    },

    onComponentAdded: function(owner) {
        var me = this;
        delete me.activeOwner;
        me.floatParent = owner;
        if (!owner.floating) {
            owner = owner.up('[floating]');
        }
        if (owner) {
            me.activeOwner = owner;
            me.mon(owner, 'move', me.sizeMask, me);
        } else {
            me.preventBringToFront = true;
        }
        owner = me.floatParent.ownerCt;
        if (me.rendered && me.isVisible() && owner) {
            me.floatOwner = owner;
            me.mon(owner, 'afterlayout', me.sizeMask, me, {single: true});
        }
    },

    onComponentRemoved: function(owner) {
        var me = this,
            activeOwner = me.activeOwner,
            floatOwner = me.floatOwner;

        if (activeOwner) {
            me.mun(activeOwner, 'move', me.sizeMask, me);
        }
        if (floatOwner) {
            me.mun(floatOwner, 'afterlayout', me.sizeMask, me);
        }
        delete me.activeOwner;
        delete me.floatOwner;
    },

    afterRender: function() {
        this.callParent(arguments);
            this.container = this.floatParent.getContentTarget();
    },

    onContainerShow: function(container) {
        if (this.isActiveContainer(container)) {
            this.onComponentShow();
        }
    },

    onContainerHide: function(container) {
        if (this.isActiveContainer(container)) {
            this.onComponentHide();
        }
    },

    onContainerExpand: function(container) {
        if (this.isActiveContainer(container)) {
            this.onComponentShow();
        }
    },

    onContainerCollapse: function(container) {
        if (this.isActiveContainer(container)) {
            this.onComponentHide();
        }
    },

    isActiveContainer: function(container) {
        return this.isDescendantOf(container);
    },

    onComponentHide: function() {
        var me = this;

        if (me.rendered && me.isVisible()) {
            me.hide();
            me.showNext = true;
        }
    },

    onComponentShow: function() {
        if (this.showNext) {
            this.show();
        }
        delete this.showNext;
    },

    
    sizeMask: function() {
        var me = this,
            target;

        if (me.rendered && me.isVisible()) {
            me.center();

            target = me.getMaskTarget();
            me.getMaskEl().show().setSize(target.getSize()).alignTo(target, 'tl-tl');

        }
    },

    
    bindStore : function(store, initial) {
        var me = this;
        me.mixins.bindable.bindStore.apply(me, arguments);
        store = me.store;
        if (store && store.isLoading()) {
            me.onBeforeLoad();
        }
    },

    getStoreListeners: function(store) {
        var load = this.onLoad,
            beforeLoad = this.onBeforeLoad,
            result = {
                
                cachemiss: beforeLoad,

                
                cachefilled: load
            };

        
        if (!store.proxy.isSynchronous) {
            result.beforeLoad = beforeLoad;
            result.load = load;
        }
        return result;
    },

    onDisable : function() {
        this.callParent(arguments);
        if (this.loading) {
            this.onLoad();
        }
    },

    getOwner: function() {
        return this.ownerCt || this.floatParent;
    },

    getMaskTarget: function() {
        var owner = this.getOwner();
        return this.useTargetEl ? owner.getTargetEl() : owner.getEl();
    },

    
    onBeforeLoad : function() {
        var me = this,
            owner = me.getOwner(),
            origin;

        if (!me.disabled) {
            me.loading = true;
            
            
            if (owner.componentLayoutCounter) {
                me.maybeShow();
            } else {
                
                origin = owner.afterComponentLayout;
                owner.afterComponentLayout = function() {
                    owner.afterComponentLayout = origin;
                    origin.apply(owner, arguments);
                    me.maybeShow();
                };
            }
        }
    },

    maybeShow: function() {
        var me = this,
            owner = me.getOwner();

        if (!owner.isVisible(true)) {
            me.showNext = true;
        }
        else if (me.loading && owner.rendered) {
            me.show();
        }
    },

    getMaskEl: function(){
        var me = this;
        return me.maskEl || (me.maskEl = me.el.insertSibling({
            cls: me.maskCls,
            style: {
                zIndex: me.el.getStyle('zIndex') - 2
            }
        }, 'before'));
    },

    onShow: function() {
        var me = this,
            msgEl = me.msgEl;

        me.callParent(arguments);
        me.loading = true;

        if (me.useMsg) {
            msgEl.show();
            me.msgTextEl.update(me.msg);
        } else {
            msgEl.parent().hide();
        }
    },

    hide: function() {
        
        if (this.isElement) {
            this.ownerCt.unmask();
            this.fireEvent('hide', this);
            return;
        }
        delete this.showNext;
        return this.callParent(arguments);
    },

    onHide: function() {
        this.callParent();
        this.getMaskEl().hide();
    },

    show: function() {
        
        if (this.isElement) {
            this.ownerCt.mask(this.useMsg ? this.msg : '', this.msgCls);
            this.fireEvent('show', this);
            return;
        }
        return this.callParent(arguments);
    },

    afterShow: function() {
        this.callParent(arguments);
        this.sizeMask();
    },

    setZIndex: function(index) {
        var me = this,
            owner = me.activeOwner;
            
        if (owner) {
            
            
            
            index = parseInt(owner.el.getStyle('zIndex'), 10) + 1;
        }

        me.getMaskEl().setStyle('zIndex', index - 1);
        return me.mixins.floating.setZIndex.apply(me, arguments);
    },

    
    onLoad : function() {
        this.loading = false;
        this.hide();
    },

    onDestroy: function() {
        var me = this;

        if (me.isElement) {
            me.ownerCt.unmask();
        }

        Ext.destroy(me.maskEl);
        me.callParent();
    }
});


Ext.define('Ext.data.association.Association', {
    alternateClassName: 'Ext.data.Association',
    

    

    

    
    primaryKey: 'id',

    
    
    

    associationKeyFunction : null,

    defaultReaderType: 'json',

    isAssociation: true,

    initialConfig: null,

    statics: {
        AUTO_ID: 1000,
        
        create: function(association){
            if (Ext.isString(association)) {
                association = {
                    type: association
                };
            }

            switch (association.type) {
                case 'belongsTo':
                    return new Ext.data.association.BelongsTo(association);
                case 'hasMany':
                    return new Ext.data.association.HasMany(association);
                case 'hasOne':
                    return new Ext.data.association.HasOne(association);
                


                default:
            }
            return association;
        }
    },

    
    constructor: function(config) {
        Ext.apply(this, config);

        var me              = this,
            types           = Ext.ModelManager.types,
            ownerName       = config.ownerModel,
            associatedName  = config.associatedModel,
            ownerModel      = types[ownerName],
            associatedModel = types[associatedName],
            associationKey  = config.associationKey,
            keyReIdx;

        if (associationKey) {
            keyReIdx = String(associationKey).search(/[\[\.]/);

            if (keyReIdx >= 0) {
                me.associationKeyFunction = Ext.functionFactory('obj', 'return obj' + (keyReIdx > 0 ? '.' : '') + associationKey);
            }
        }

        me.initialConfig = config;


        me.ownerModel = ownerModel;
        me.associatedModel = associatedModel;

        

        

        Ext.applyIf(me, {
            ownerName : ownerName,
            associatedName: associatedName
        });
        
        me.associationId = 'association' + (++me.statics().AUTO_ID);
    },

    
    getReader: function(){
        var me = this,
            reader = me.reader,
            model = me.associatedModel;

        if (reader) {
            if (Ext.isString(reader)) {
                reader = {
                    type: reader
                };
            }
            if (reader.isReader) {
                reader.setModel(model);
            } else {
                Ext.applyIf(reader, {
                    model: model,
                    type : me.defaultReaderType
                });
            }
            me.reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        return me.reader || null;
    }
});


Ext.define('Ext.ModelManager', {
    extend:  Ext.AbstractManager ,
    alternateClassName: 'Ext.ModelMgr',
                                                   
    
    singleton: true,

    typeName: 'mtype',

    
    associationStack: [],

    
    registerType: function(name, config) {
        var proto = config.prototype,
            model;
        if (proto && proto.isModel) {
            
            model = config;
        } else {
            
            if (!config.extend) {
                config.extend = 'Ext.data.Model';
            }
            model = Ext.define(name, config);
        }
        this.types[name] = model;
        return model;
    },
    
    
    unregisterType: function(name) {
        delete this.types[name];
    },

    
    onModelDefined: function(model) {
        var stack  = this.associationStack,
            length = stack.length,
            create = [],
            association, i, created;

        for (i = 0; i < length; i++) {
            association = stack[i];

            if (association.associatedModel == model.modelName) {
                create.push(association);
            }
        }

        for (i = 0, length = create.length; i < length; i++) {
            created = create[i];
            this.types[created.ownerModel].prototype.associations.add(Ext.data.association.Association.create(created));
            Ext.Array.remove(stack, created);
        }
    },

    
    registerDeferredAssociation: function(association){
        this.associationStack.push(association);
    },

    
    getModel: function(id) {
        var model = id;
        if (typeof model == 'string') {
            model = this.types[model];
        }
        return model;
    },

    
    create: function(config, name, id) {
        var Con = typeof name == 'function' ? name : this.types[name || config.name];

        return new Con(config, id);
    }
}, function() {

    
    Ext.regModel = function() {
        return this.ModelManager.registerType.apply(this.ModelManager, arguments);
    };
});


Ext.define('Ext.layout.component.ProgressBar', {

    

    alias: ['layout.progressbar'],

    extend:  Ext.layout.component.Auto ,

    

    type: 'progressbar',

    beginLayout: function (ownerContext) {
        var me = this,
            i, textEls;

        me.callParent(arguments);

        if (!ownerContext.textEls) {
            textEls = me.owner.textEl; 

            if (textEls.isComposite) {
                ownerContext.textEls = [];
                textEls = textEls.elements;
                for (i = textEls.length; i--; ) {
                    ownerContext.textEls[i] = ownerContext.getEl(Ext.get(textEls[i]));
                }
            } else {
                ownerContext.textEls = [ ownerContext.getEl('textEl') ];
            }
        }
    },

    calculate: function(ownerContext) {
        var me = this,
            i, textEls, width;

        me.callParent(arguments);

        if (Ext.isNumber(width = ownerContext.getProp('width'))) {
            width -= ownerContext.getBorderInfo().width;
            textEls = ownerContext.textEls;

            for (i = textEls.length; i--; ) {
                textEls[i].setWidth(width);
            }
        } else {
            me.done = false;
        }
    }
});


Ext.define('Ext.ProgressBar', {
    extend:  Ext.Component ,
    alias: 'widget.progressbar',

               
                       
                               
                          
                                          
      

                          

   

   

   

   
    baseCls: Ext.baseCSSPrefix + 'progress',

    
    animate: false,

    
    text: '',

    
    waitTimer: null,

    childEls: [
        'bar'
    ],

    renderTpl: [
        '<tpl if="internalText">',
            '<div class="{baseCls}-text {baseCls}-text-back">{text}</div>',
        '</tpl>',
        '<div id="{id}-bar" class="{baseCls}-bar {baseCls}-bar-{ui}" style="width:{percentage}%">',
            '<tpl if="internalText">',
                '<div class="{baseCls}-text">',
                    '<div>{text}</div>',
                '</div>',
            '</tpl>',
        '</div>'
    ],

    componentLayout: 'progressbar',

    
    initComponent: function() {
        this.callParent();

        this.addEvents(
            
            "update"
        );
    },

    initRenderData: function() {
        var me = this;
        return Ext.apply(me.callParent(), {
            internalText : !me.hasOwnProperty('textEl'),
            text         : me.text || '&#160;',
            percentage   : me.value ? me.value * 100 : 0
        });
    },

    onRender : function() {
        var me = this;

        me.callParent(arguments);

        
        if (me.textEl) {
            me.textEl = Ext.get(me.textEl);
            me.updateText(me.text);
        }
        
        else {
            
            
            me.textEl = me.el.select('.' + me.baseCls + '-text');
        }
    },

    
    updateProgress: function(value, text, animate) {
        var me = this,
            oldValue = me.value;

        me.value = value || 0;
        if (text) {
            me.updateText(text);
        }
        if (me.rendered && !me.isDestroyed) {
            if (animate === true || (animate !== false && me.animate)) {
                me.bar.stopAnimation();
                me.bar.animate(Ext.apply({
                    from: {
                        width: (oldValue * 100) + '%'
                    },
                    to: {
                        width: (me.value * 100) + '%'
                    }
                }, me.animate));
            } else {
                me.bar.setStyle('width', (me.value * 100) + '%');
            }
        }
        me.fireEvent('update', me, me.value, text);
        return me;
    },

    
    updateText: function(text) {
        var me = this;
        
        me.text = text;
        if (me.rendered) {
            me.textEl.update(me.text);
        }
        return me;
    },

    applyText : function(text) {
        this.updateText(text);
    },
    
    getText: function(){
        return this.text;    
    },

    
    wait: function(o) {
        var me = this, scope;
            
        if (!me.waitTimer) {
            scope = me;
            o = o || {};
            me.updateText(o.text);
            me.waitTimer = Ext.TaskManager.start({
                run: function(i){
                    var inc = o.increment || 10;
                    i -= 1;
                    me.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);
                },
                interval: o.interval || 1000,
                duration: o.duration,
                onStop: function(){
                    if (o.fn) {
                        o.fn.apply(o.scope || me);
                    }
                    me.reset();
                },
                scope: scope
            });
        }
        return me;
    },

    
    isWaiting: function(){
        return this.waitTimer !== null;
    },

    
    reset: function(hide){
        var me = this;
        
        me.updateProgress(0);
        me.clearTimer();
        if (hide === true) {
            me.hide();
        }
        return me;
    },

    
    clearTimer: function(){
        var me = this;
        
        if (me.waitTimer) {
            me.waitTimer.onStop = null; 
            Ext.TaskManager.stop(me.waitTimer);
            me.waitTimer = null;
        }
    },

    onDestroy: function(){
        var me = this,
            bar = me.bar;
        
        me.clearTimer();
        if (me.rendered) {
            if (me.textEl.isComposite) {
                me.textEl.clear();
            }
            Ext.destroyMembers(me, 'textEl', 'progressBar');
            if (bar && me.animate) {
                bar.stopAnimation();
            }
        }
        me.callParent();
    }
});


Ext.define('Ext.ShadowPool', {
    singleton: true,
                                

    markup: (function() {
        return Ext.String.format(
            '<div class="{0}{1}-shadow" role="presentation"></div>',
            Ext.baseCSSPrefix,
            Ext.isIE && !Ext.supports.CSS3BoxShadow ? 'ie' : 'css'
        );
    }()),

    shadows: [],

    pull: function() {
        var sh = this.shadows.shift();
        if (!sh) {
            sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, this.markup));
            sh.autoBoxAdjust = false;
        }
        return sh;
    },

    push: function(sh) {
        this.shadows.push(sh);
    },
    
    reset: function() {
        var shadows = [].concat(this.shadows),
            s,
            sLen    = shadows.length;

        for (s = 0; s < sLen; s++) {
            shadows[s].remove();
        }

        this.shadows = [];
    }
});


Ext.define('Ext.Shadow', {
                                 

    localXYNames: {
        get: 'getLocalXY',
        set: 'setLocalXY'
    },

    
    constructor: function(config) {
        var me = this,
            adjusts,
            offset,
            rad;
        
        Ext.apply(me, config);
        if (!Ext.isString(me.mode)) {
            me.mode = me.defaultMode;
        }
        offset = me.offset;
        rad = Math.floor(offset / 2);
        me.opacity = 50;
        switch (me.mode.toLowerCase()) {
            
            case "drop":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts = {
                        t: offset,
                        l: offset,
                        h: -offset,
                        w: -offset
                    };
                }
                else {
                    adjusts = {
                        t: -rad,
                        l: -rad,
                        h: -rad,
                        w: -rad
                    };
                }
                break;
            case "sides":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts = {
                        t: offset,
                        l: 0,
                        h: -offset,
                        w: 0
                    };
                }
                else {
                    adjusts = {
                        t: - (1 + rad),
                        l: 1 + rad - 2 * offset,
                        h: -1,
                        w: rad - 1
                    };
                }
                break;
            case "frame":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts = {
                        t: 0,
                        l: 0,
                        h: 0,
                        w: 0
                    };
                }
                else {
                    adjusts = {
                        t: 1 + rad - 2 * offset,
                        l: 1 + rad - 2 * offset,
                        h: offset - rad - 1,
                        w: offset - rad - 1
                    };
                }
                break;
            case "bottom":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts = {
                        t: offset,
                        l: 0,
                        h: -offset,
                        w: 0
                    };
                }
                else {
                    adjusts = {
                        t: offset,
                        l: 0,
                        h: 0,
                        w: 0
                    };
                }
                break;
        }
        me.adjusts = adjusts;
    },

    
    getShadowSize: function() {
        var me = this,
            offset = me.el ? me.offset : 0,
            result = [offset, offset, offset, offset],
            mode = me.mode.toLowerCase();

        
        if (me.el && mode !== 'frame') {
            result[0] = 0;
            if (mode == 'drop') {
                result[3] = 0;
            }
        }
        return result;
    },

    

    
    offset: 4,

    
    defaultMode: "drop",

    
    boxShadowProperty: (function() {
        var property = 'boxShadow',
            style = document.documentElement.style;

        if (!('boxShadow' in style)) {
            if ('WebkitBoxShadow' in style) {
                
                property = 'WebkitBoxShadow';
            }
            else if ('MozBoxShadow' in style) {
                
                property = 'MozBoxShadow';
            }
        }

        return property;
    }()),

    
    show: function(target) {
        var me = this,
            index, xy;

        target = Ext.get(target);
        
        
        index = (parseInt(target.getStyle("z-index"), 10) - 1) || 0;
        xy = target[me.localXYNames.get]();

        
        if (!me.el) {
            me.el = Ext.ShadowPool.pull();
            
            if (me.fixed) {
                me.el.dom.style.position = 'fixed';
            } else {
                me.el.dom.style.position = '';
            }
            if (me.el.dom.nextSibling != target.dom) {
                me.el.insertBefore(target);
            }
        }
        me.el.setStyle("z-index", me.zIndex || index);
        if (Ext.isIE && !Ext.supports.CSS3BoxShadow) {
            me.el.dom.style.filter = "progid:DXImageTransform.Microsoft.alpha(opacity=" + me.opacity + ") progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (me.offset) + ")";
        }
        me.realign(
            xy[0],
            xy[1],
            target.dom.offsetWidth,
            target.dom.offsetHeight
        );
        me.el.dom.style.display = "block";
    },

    
    isVisible: function() {
        return this.el ? true: false;
    },

    
    realign: function(l, t, targetWidth, targetHeight) {
        if (!this.el) {
            return;
        }
        var adjusts = this.adjusts,
            el = this.el,
            targetStyle = el.dom.style,
            shadowWidth,
            shadowHeight,
            sws,
            shs;

        el[this.localXYNames.set](l + adjusts.l, t + adjusts.t);
        shadowWidth = Math.max(targetWidth + adjusts.w, 0);
        shadowHeight = Math.max(targetHeight + adjusts.h, 0);
        sws = shadowWidth + "px";
        shs = shadowHeight + "px";
        if (targetStyle.width != sws || targetStyle.height != shs) {
            targetStyle.width = sws;
            targetStyle.height = shs;

            if (Ext.supports.CSS3BoxShadow) {
                targetStyle[this.boxShadowProperty] = '0 0 ' + (this.offset + 2) + 'px #888';
            }
        }
    },

    
    hide: function() {
        var me = this;
        
        if (me.el) {
            me.el.dom.style.display = "none";
            Ext.ShadowPool.push(me.el);
            delete me.el;
        }
    },

    
    setZIndex: function(z) {
        this.zIndex = z;
        if (this.el) {
            this.el.setStyle("z-index", z);
        }
    },
    
    
    setOpacity: function(opacity){
        if (this.el) {
            if (Ext.isIE && !Ext.supports.CSS3BoxShadow) {
                opacity = Math.floor(opacity * 100 / 2) / 100;
            }
            this.opacity = opacity;
            this.el.setOpacity(opacity);
        }
    }
});



Ext.define('Ext.app.EventDomain', {
               
                        
      

    statics: {
        
        instances: {}
    },
    
    
     
    isEventDomain: true,

    constructor: function() {
        var me = this;

        Ext.app.EventDomain.instances[me.type] = me;

        me.bus = {};
        me.monitoredClasses = [];
    },

    
    dispatch: function(target, ev, args) {
        var me = this,
            bus = me.bus,
            selectors = bus[ev],
            selector, controllers, id, events, event, i, ln;

        if (!selectors) {
            return true;
        }

        
        for (selector in selectors) {
            
            if (selectors.hasOwnProperty(selector) && me.match(target, selector)) {
                
                controllers = selectors[selector];

                for (id in controllers) {
                    if (controllers.hasOwnProperty(id)) {
                        
                        events = controllers[id];

                        for (i = 0, ln = events.length; i < ln; i++) {
                            event = events[i];

                            
                            if (event.fire.apply(event, args) === false) {
                                return false;
                            }
                        }
                    }
                }
            }
        }

        return true;
    },

    
    listen: function(selectors, controller) {
        var me = this,
            bus = me.bus,
            idProperty = me.idProperty,
            monitoredClasses = me.monitoredClasses,
            monitoredClassesCount = monitoredClasses.length,
            i, tree, list, selector, options, listener, scope, event, listeners, ev;

        for (selector in selectors) {
            if (selectors.hasOwnProperty(selector) && (listeners = selectors[selector])) {
                if (idProperty) {
                
                    selector = selector === '*' ? selector : selector.substring(1);
                }
                
                for (ev in listeners) {
                    if (listeners.hasOwnProperty(ev)) {
                        options  = null;
                        listener = listeners[ev];
                        scope    = controller;
                        event    = new Ext.util.Event(controller, ev);

                        
                        if (Ext.isObject(listener)) {
                            options  = listener;
                            listener = options.fn;
                            scope    = options.scope || controller;

                            delete options.fn;
                            delete options.scope;
                        }

                        if (typeof listener === 'string') {
                            listener = scope[listener];
                        }
                        event.addListener(listener, scope, options);

                        for (i = monitoredClassesCount; i-- > 0;) {
                            monitoredClasses[i].hasListeners._incr_(ev);
                        }

                        
                        tree = bus[ev]             || (bus[ev] = {});
                        tree = tree[selector]      || (tree[selector] = {});
                        list = tree[controller.id] || (tree[controller.id] = []);

                        
                        list.push(event);
                    }
                } 
            }
        } 
    },

    
    match: function(target, selector) {
        var idProperty = this.idProperty;
        
        if (idProperty) {
            return selector === '*' || target[idProperty] === selector;
        }
        
        return false;
    },

    
    monitor: function(observable) {
        var domain = this,
            prototype = observable.isInstance ? observable : observable.prototype,
            fireEventArgs = prototype.fireEventArgs;

        domain.monitoredClasses.push(observable);

        prototype.fireEventArgs = function(ev, args) {
            var ret = fireEventArgs.apply(this, arguments);

            if (ret !== false) {
                ret = domain.dispatch(this, ev, args);
            }

            return ret;
        };
    },

    
    unlisten: function(controllerId) {
        var bus = this.bus,
            controllers, ev, selector, selectors;

        for (ev in bus) {
            if (bus.hasOwnProperty(ev) && (selectors = bus[ev])) {
                for (selector in selectors) {
                    controllers = selectors[selector];
                    delete controllers[controllerId];  
                }
            }
        }
    }
});


Ext.define('Ext.app.domain.Component', {
    extend:  Ext.app.EventDomain ,
    singleton: true,

               
                       
      

    type: 'component',

    constructor: function() {
        var me = this;
        
        me.callParent();
        me.monitor(Ext.Component);
    },

    match: function(target, selector) {
        return target.is(selector);
    }
});


Ext.define('Ext.app.EventBus', {
    singleton: true,

               
                                  
      
    
    constructor: function() {
        var me = this,
            domains = Ext.app.EventDomain.instances;

        me.callParent();

        me.domains = domains;
        me.bus = domains.component.bus; 
    },

    
    control: function(selectors, controller) {
        return this.domains.component.listen(selectors, controller);
    },

    
    listen: function(to, controller) {
        var domains = this.domains,
            domain;

        for (domain in to) {
            if (to.hasOwnProperty(domain)) {
                domains[domain].listen(to[domain], controller);
            }
        }
    },

    
    unlisten: function(controllerId) {
        var domains = Ext.app.EventDomain.instances,
            domain;
        
        for (domain in domains) {
            domains[domain].unlisten(controllerId);
        }
    }
});


Ext.define('Ext.data.StoreManager', {
    extend:  Ext.util.MixedCollection ,
    alternateClassName: ['Ext.StoreMgr', 'Ext.data.StoreMgr', 'Ext.StoreManager'],
    singleton: true,
                                  
    
    

    
    register : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.add(s);
        }
    },

    
    unregister : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.remove(this.lookup(s));
        }
    },

    
    lookup : function(store) {
        
        if (Ext.isArray(store)) {
            var fields = ['field1'], 
                expand = !Ext.isArray(store[0]),
                data = store,
                i,
                len;
                
            if(expand){
                data = [];
                for (i = 0, len = store.length; i < len; ++i) {
                    data.push([store[i]]);
                }
            } else {
                for(i = 2, len = store[0].length; i <= len; ++i){
                    fields.push('field' + i);
                }
            }
            return new Ext.data.ArrayStore({
                data  : data,
                fields: fields,
                autoDestroy: true,
                autoCreated: true,
                expanded: expand
            });
        }
        
        if (Ext.isString(store)) {
            
            return this.get(store);
        } else {
            
            return Ext.data.AbstractStore.create(store);
        }
    },

    
    getKey : function(o) {
         return o.storeId;
    }
}, function() {    
    
    Ext.regStore = function(name, config) {
        var store;

        if (Ext.isObject(name)) {
            config = name;
        } else {
            config.storeId = name;
        }

        if (config instanceof Ext.data.Store) {
            store = config;
        } else {
            store = new Ext.data.Store(config);
        }

        return Ext.data.StoreManager.register(store);
    };

    
    Ext.getStore = function(name) {
        return Ext.data.StoreManager.lookup(name);
    };
});


Ext.define('Ext.app.domain.Global', {
    extend:  Ext.app.EventDomain ,
    singleton: true,

    type: 'global',

    constructor: function() {
        var me = this;
        
        me.callParent();
        me.monitor(Ext.globalEvents);
    },
    
                  
    listen: function(listeners, controller) {
        
        
        this.callParent([{ global: listeners }, controller]);
    },

    match: function() {
        return true;
    }
});


Ext.define('Ext.data.ResultSet', {
    
    loaded: true,

    
    count: 0,

    
    total: 0,

    
    success: false,

    

    
    constructor: function(config) {
        Ext.apply(this, config);

        
        this.totalRecords = this.total;

        if (config.count === undefined) {
            this.count = this.records.length;
        }
    }
});


Ext.define('Ext.data.reader.Reader', {
                                                      
    alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],

    mixins: {
        observable:  Ext.util.Observable 
    },

    

    
    totalProperty: 'total',

    
    successProperty: 'success',

    
    root: '',
    
    
    
    
    implicitIncludes: true,
    
    
    readRecordsOnFailure: true,
    
    
    
    
    isReader: true,

    
    
    
    applyDefaults: true,

    lastFieldGeneration: null,
    
    
    constructor: function(config) {
        var me = this;
        
        me.mixins.observable.constructor.call(me, config);
        me.fieldCount = 0;
        me.model = Ext.ModelManager.getModel(me.model);

        
        
        
        
        
        if (me.model && me.model.prototype.fields) {
            me.buildExtractors();
        }

        this.addEvents(
            
            'exception'
        );
    },

    
    setModel: function(model, setOnProxy) {
        var me = this;
        
        me.model = Ext.ModelManager.getModel(model);
        if (model) {
            me.buildExtractors(true);
        }
        
        if (setOnProxy && me.proxy) {
            me.proxy.setModel(me.model, true);
        }
    },

    
    read: function(response) {
        var data;

        if (response) {
            data = response.responseText ? this.getResponseData(response) : this.readRecords(response);
        }

        return data || this.nullResultSet;
    },

    
    readRecords: function(data) {
        var me = this,
            success,
            recordCount,
            records,
            root,
            total,
            value,
            message;
        
        
        if (me.lastFieldGeneration !== me.model.prototype.fields.generation) {
            me.buildExtractors(true);
        }
        
        
        me.rawData = data;

        data = me.getData(data);
        
        success = true;
        recordCount = 0;
        records = [];
            
        if (me.successProperty) {
            value = me.getSuccess(data);
            if (value === false || value === 'false') {
                success = false;
            }
        }
        
        if (me.messageProperty) {
            message = me.getMessage(data);
        }

        
        
        if (me.readRecordsOnFailure || success) {
            
            
            root = Ext.isArray(data) ? data : me.getRoot(data);
            
            if (root) {
                total = root.length;
            }

          if (me.totalProperty) {
                value = parseInt(me.getTotal(data), 10);
                if (!isNaN(value)) {
                    total = value;
                }
            }

           if (root) {
                records = me.extractData(root);
                recordCount = records.length;
            }
        }

        return new Ext.data.ResultSet({
            total  : total || recordCount,
            count  : recordCount,
            records: records,
            success: success,
            message: message
        });
    },

    
    extractData : function(root) {
        var me = this,
            Model   = me.model,
            length  = root.length,
            records = new Array(length),
            convertedValues, node, record, i;

        if (!root.length && Ext.isObject(root)) {
            root = [root];
            length = 1;
        }

        for (i = 0; i < length; i++) {
            node = root[i];
            if (node.isModel) {
                
                
                records[i] = node;
            } else {
                
                
                
                records[i] = record = new Model(undefined, me.getId(node), node, convertedValues = {});

                
                
                record.phantom = false;

                
                me.convertRecordData(convertedValues, node, record);

                if (me.implicitIncludes && record.associations.length) {
                    me.readAssociated(record, node);
                }
            }
        }

        return records;
    },

    
    readAssociated: function(record, data) {
        var associations = record.associations.items,
            i            = 0,
            length       = associations.length,
            association, associationData, proxy, reader;
        
        for (; i < length; i++) {
            association     = associations[i];
            associationData = this.getAssociatedDataRoot(data, association.associationKeyFunction || association.associationKey || association.name);
            
            if (associationData) {
                reader = association.getReader();
                if (!reader) {
                    proxy = association.associatedModel.getProxy();
                    
                    if (proxy) {
                        reader = proxy.getReader();
                    } else {
                        reader = new this.constructor({
                            model: association.associatedName
                        });
                    }
                }
                association.read(record, reader, associationData);
            }  
        }
    },
    
    
    getAssociatedDataRoot: function(data, associationName) {
        if (Ext.isFunction(associationName)) {
            return associationName(data);
        }

        return data[associationName];
    },
    
    getFields: function() {
        return this.model.prototype.fields.items;
    },

    
    getData: Ext.identityFn,

    
    getRoot: Ext.identityFn,

    
    getResponseData: function(response) {
    },

    
    onMetaChange : function(meta) {
        var me = this,
            fields = meta.fields || me.getFields(),
            newModel,
            clientIdProperty;
        
        
        me.metaData = meta;
        
        
        me.root = meta.root || me.root;
        me.idProperty = meta.idProperty || me.idProperty;
        me.totalProperty = meta.totalProperty || me.totalProperty;
        me.successProperty = meta.successProperty || me.successProperty;
        me.messageProperty = meta.messageProperty || me.messageProperty;
        clientIdProperty = meta.clientIdProperty;

        if (me.model) {
            me.model.setFields(fields, me.idProperty, clientIdProperty);
            me.setModel(me.model, true);
        }
        else {
            newModel = Ext.define("Ext.data.reader.Json-Model" + Ext.id(), {
                extend: 'Ext.data.Model',
                fields: fields,
                clientIdProperty: clientIdProperty
            });
            if (me.idProperty) {
                
                
                
                
                newModel.idProperty = me.idProperty;
            }
            me.setModel(newModel, true);
        }
    },
    
    
    getIdProperty: function() {
        var idField = this.model.prototype.idField,
            idProperty = this.idProperty;

        if (!idProperty && idField  && (idProperty = idField.mapping) == null) {
            idProperty = idField.name;
        }
        return idProperty;
    },

    
    buildExtractors: function(force) {
        var me          = this,
            idProp      = me.getIdProperty(),
            totalProp   = me.totalProperty,
            successProp = me.successProperty,
            messageProp = me.messageProperty,
            accessor;
            
        if (force === true) {
            delete me.convertRecordData;
        }
        
        if (me.convertRecordData) {
            return;
        }   

        
        if (totalProp) {
            me.getTotal = me.createAccessor(totalProp);
        }

        if (successProp) {
            me.getSuccess = me.createAccessor(successProp);
        }

        if (messageProp) {
            me.getMessage = me.createAccessor(messageProp);
        }

        
        if (idProp) {
            accessor = me.createAccessor(idProp);
            me.getId = function(record) {
                var id = accessor.call(me, record);
                return (id === undefined || id === '') ? null : id;
            };
        } else {
            me.getId = function() {
                return null;
            };
        }
        me.convertRecordData = me.buildRecordDataExtractor();
        me.lastFieldGeneration = me.model.prototype.fields.generation;
    },

    recordDataExtractorTemplate : [
        'var me = this\n',
        '    ,fields = me.model.prototype.fields\n',
        '    ,value\n',
        '    ,internalId\n',
        '<tpl for="fields">',
        '    ,__field{#} = fields.map["{name}"]\n',
        '</tpl>', ';\n',

        'return function(dest, source, record) {\n',
        '<tpl for="fields">',
        '{% var fieldAccessExpression =  this.createFieldAccessExpression(values, "__field" + xindex, "source");',
        '   if (fieldAccessExpression) { %}',
        
        '    value = {[ this.createFieldAccessExpression(values, "__field" + xindex, "source") ]};\n',

        
            '<tpl if="hasCustomConvert">',
        '    dest["{name}"] = value === undefined ? __field{#}.convert(__field{#}.defaultValue, record) : __field{#}.convert(value, record);\n',

        
            '<tpl elseif="defaultValue !== undefined">',
        '    if (value === undefined) {\n',
        '        if (me.applyDefaults) {\n',
                '<tpl if="convert">',
        '            dest["{name}"] = __field{#}.convert(__field{#}.defaultValue, record);\n',
                '<tpl else>',
        '            dest["{name}"] = __field{#}.defaultValue\n',
                '</tpl>',
        '        };\n',
        '    } else {\n',
                '<tpl if="convert">',
        '        dest["{name}"] = __field{#}.convert(value, record);\n',
                '<tpl else>',
        '        dest["{name}"] = value;\n',
                '</tpl>',
        '    };\n',

        
            '<tpl else>',
        '    if (value !== undefined) {\n',
                '<tpl if="convert">',
        '        dest["{name}"] = __field{#}.convert(value, record);\n',
                '<tpl else>',
        '        dest["{name}"] = value;\n',
                '</tpl>',
        '    }\n',
            '</tpl>',
            
        
        
        
        '{% } else { %}',
            '<tpl if="defaultValue !== undefined">',
                '<tpl if="convert">',
        '    dest["{name}"] = __field{#}.convert(__field{#}.defaultValue, record);\n',
                '<tpl else>',
        '    dest["{name}"] = __field{#}.defaultValue\n',
                '</tpl>',
            '</tpl>',
        '{% } %}',
        '</tpl>',

        
        
        
        '<tpl if="clientIdProp">',
        '    if (record && (internalId = {[ this.createFieldAccessExpression(\{mapping: values.clientIdProp\}, null, "source") ]})) {\n',
        '        record.{["internalId"]} = internalId;\n',
        '    }\n',
        '</tpl>',

        '};'
    ],

    
    buildRecordDataExtractor: function() {
        var me = this,
            modelProto = me.model.prototype,
            templateData = {
                clientIdProp: modelProto.clientIdProperty,
                fields: modelProto.fields.items
            };

        me.recordDataExtractorTemplate.createFieldAccessExpression = function() { 
            return me.createFieldAccessExpression.apply(me,arguments);
        };
        
        
        
        
        
        return Ext.functionFactory(me.recordDataExtractorTemplate.apply(templateData)).call(me);
    },

    destroyReader: function() {
        var me = this;
        delete me.proxy;
        delete me.model;
        delete me.convertRecordData;
        delete me.getId;
        delete me.getTotal;
        delete me.getSuccess;
        delete me.getMessage;
    }
}, function() {
    var proto = this.prototype;
    Ext.apply(proto, {
        
        nullResultSet: new Ext.data.ResultSet({
            total  : 0,
            count  : 0,
            records: [],
            success: true,
            message: ''
        }),
        recordDataExtractorTemplate: new Ext.XTemplate(proto.recordDataExtractorTemplate)
    });
});


Ext.define('Ext.data.reader.Json', {
    extend:  Ext.data.reader.Reader ,
    alternateClassName: 'Ext.data.JsonReader',
    alias : 'reader.json',

    root: '',

    
    
    
    metaProperty: 'metaData',

    
    useSimpleAccessors: false,

    
    readRecords: function(data) {
        var me = this,
            meta;
            
        
        if (me.getMeta) {
            meta = me.getMeta(data);
            if (meta) {
                me.onMetaChange(meta);
            }
        } else if (data.metaData) {
            me.onMetaChange(data.metaData);
        }

        
        me.jsonData = data;
        return me.callParent([data]);
    },

    
    getResponseData: function(response) {
        var data, error;
 
        try {
            data = Ext.decode(response.responseText);
            return this.readRecords(data);
        } catch (ex) {
            error = new Ext.data.ResultSet({
                total  : 0,
                count  : 0,
                records: [],
                success: false,
                message: ex.message
            });

            this.fireEvent('exception', this, response, error);

            Ext.Logger.warn('Unable to parse the JSON returned by the server');

            return error;
        }
    },

    
    buildExtractors : function() {
        var me = this,
            metaProp = me.metaProperty;

        me.callParent(arguments);

        if (me.root) {
            me.getRoot = me.createAccessor(me.root);
        } else {
            me.getRoot = Ext.identityFn;
        }
        
        if (metaProp) {
            me.getMeta = me.createAccessor(metaProp);
        }
    },

    
    extractData: function(root) {
        var recordName = this.record,
            data = [],
            length, i;

        if (recordName) {
            length = root.length;
            
            if (!length && Ext.isObject(root)) {
                length = 1;
                root = [root];
            }

            for (i = 0; i < length; i++) {
                data[i] = root[i][recordName];
            }
        } else {
            data = root;
        }
        return this.callParent([data]);
    },

    
    createAccessor: (function() {
        var re = /[\[\.]/;

        return function(expr) {
            if (Ext.isEmpty(expr)) {
                return Ext.emptyFn;
            }
            if (Ext.isFunction(expr)) {
                return expr;
            }
            if (this.useSimpleAccessors !== true) {
                var i = String(expr).search(re);
                if (i >= 0) {
                    return Ext.functionFactory('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
                }
            }
            return function(obj) {
                return obj[expr];
            };
        };
    }()),

    
    createFieldAccessExpression: (function() {
        var re = /[\[\.]/;

        return function(field, fieldVarName, dataName) {
            var mapping = field.mapping,
                hasMap = mapping || mapping === 0,
                map    = hasMap ? mapping : field.name,
                result,
                operatorIndex;

            
            if (mapping === false) {
                return;
            }

            if (typeof map === 'function') {
                result = fieldVarName + '.mapping(' + dataName + ', this)';
            } else if (this.useSimpleAccessors === true || ((operatorIndex = String(map).search(re)) < 0)) {
                if (!hasMap || isNaN(map)) {
                    
                    map = '"' + map + '"';
                }
                result = dataName + "[" + map + "]";
            } else if (operatorIndex === 0) {
                
                
                result = dataName + map;
            } else {
                
                
                
                
                
                
                var parts = map.split('.'),
                    len = parts.length,
                    i = 1,
                    tempResult = dataName + '.' + parts[0],
                    buffer = [tempResult]; 
                
                for (; i < len; i++) {
                    tempResult += '.' + parts[i];
                    buffer.push(tempResult);
                }
                result = buffer.join(' && ');
            }
            return result;
        };
    }())
});


Ext.define('Ext.data.writer.Writer', {
    alias: 'writer.base',
    alternateClassName: ['Ext.data.DataWriter', 'Ext.data.Writer'],

    
    writeAllFields: true,

    

    
    nameProperty: 'name',

    
    writeRecordId: true,

    
    isWriter: true,

    
    constructor: function(config) {
        Ext.apply(this, config);
    },

    
    write: function(request) {
        var operation = request.operation,
            records   = operation.records || [],
            len       = records.length,
            i         = 0,
            data      = [];

        for (; i < len; i++) {
            data.push(this.getRecordData(records[i], operation));
        }
        return this.writeRecords(request, data);
    },

    
    getRecordData: function(record, operation) {
        var isPhantom = record.phantom === true,
            writeAll = this.writeAllFields || isPhantom,
            fields = record.fields,
            fieldItems = fields.items,
            data = {},
            clientIdProperty = record.clientIdProperty,
            changes,
            field,
            key,
            mappedIdProperty,
            f, fLen;

        if (writeAll) {
            fLen = fieldItems.length;

            for (f = 0; f < fLen; f++) {
                field = fieldItems[f];
                if (field.persist) {
                    this.writeValue(data, field, record);
                }
            }
        } else {
            
            changes = record.getChanges();
            for (key in changes) {
                if (changes.hasOwnProperty(key)) {
                    field = fields.get(key);
                    if (field.persist) {
                        this.writeValue(data, field, record);
                    }
                }
            }
        }
        if (isPhantom) {
            if (clientIdProperty && operation && operation.records.length > 1) {
                
                
                data[clientIdProperty] = record.internalId;
            }
        } else if (this.writeRecordId) {
            
            mappedIdProperty = fields.get(record.idProperty)[this.nameProperty] || record.idProperty;
            data[mappedIdProperty] = record.getId();
        }

        return data;
    },

    writeValue: function(data, field, record){
        var name = field[this.nameProperty],
            dateFormat = this.dateFormat || field.dateWriteFormat || field.dateFormat,
            value = record.get(field.name);

        
        
        if (name == null) {
            name = field.name;
        }

        if (field.serialize) {
            data[name] = field.serialize(value, record);
        } else if (field.type === Ext.data.Types.DATE && dateFormat && Ext.isDate(value)) {
            data[name] = Ext.Date.format(value, dateFormat);
        } else {
            data[name] = value;
        }
    }
});


Ext.define('Ext.data.writer.Json', {
    extend:  Ext.data.writer.Writer ,
    alternateClassName: 'Ext.data.JsonWriter',
    alias: 'writer.json',
    
    
    root: undefined,
    
    
    encode: false,
    
    
    allowSingle: true,
    
    
    expandData: false,
    
    
    getExpandedData: function(data) {
        var dataLength = data.length,
            i = 0,
            item,
            prop,
            nameParts,
            j,
            tempObj,
            
            toObject = function(name, value) {
                var o = {};
                o[name] = value;
                return o;
            };
        
        for (; i < dataLength; i++) {
            item = data[i];
            
            for (prop in item) {
                if (item.hasOwnProperty(prop)) {
                    
                    nameParts = prop.split('.');
                    j = nameParts.length - 1;
                    
                    if (j > 0) {
                        
                        
                        tempObj = item[prop];
                        
                        for (; j > 0; j--) {
                            
                            
                            
                            tempObj = toObject(nameParts[j], tempObj);
                        }
                        
                        
                        
                        
                        item[nameParts[0]] = item[nameParts[0]] || {};
                        
                        
                        Ext.Object.merge(item[nameParts[0]], tempObj);
                        
                        delete item[prop];
                    }
                }
            }
        }
        return data;
    },
    
    
    writeRecords: function(request, data) {
        var root = this.root;
        
        if (this.expandData) {
            data = this.getExpandedData(data);
        }
        
        if (this.allowSingle && data.length === 1) {
            
            data = data[0];
        }
        
        if (this.encode) {
            if (root) {
                
                request.params[root] = Ext.encode(data);
            } else {
            }
        } else {
            
            request.jsonData = request.jsonData || {};
            if (root) {
                request.jsonData[root] = data;
            } else {
                request.jsonData = data;
            }
        }
        return request;
    }
});


Ext.define('Ext.data.proxy.Proxy', {
    alias: 'proxy.proxy',
    alternateClassName: ['Ext.data.DataProxy', 'Ext.data.Proxy'],

               
                               
                              
      

           
                         
                             
                        
      

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    batchOrder: 'create,update,destroy',

    
    batchActions: true,

    
    defaultReaderType: 'json',

    
    defaultWriterType: 'json',

    

    

    

    
    isProxy: true,

    
     isSynchronous: false,

    
    constructor: function(config) {
        var me = this;

        config = config || {};
        me.proxyConfig = config;

        me.mixins.observable.constructor.call(me, config);

        if (me.model !== undefined && !(me.model instanceof Ext.data.Model)) {
            me.setModel(me.model);
        } else {
            if (me.reader) {
                me.setReader(me.reader);
            }
            
            if (me.writer) {
                me.setWriter(me.writer);
            }
        }

        
    },

    
    setModel: function(model, setOnStore) {
        var me = this;
        
        me.model = Ext.ModelManager.getModel(model);

        me.setReader(this.reader);
        me.setWriter(this.writer);

        if (setOnStore && me.store) {
            me.store.setModel(me.model);
        }
    },

    
    getModel: function() {
        return this.model;
    },

    
    setReader: function(reader) {
        var me = this,
            needsCopy = true,
            current = me.reader;

        if (reader === undefined || typeof reader == 'string') {
            reader = {
                type: reader
            };
            needsCopy = false;
        }

        if (reader.isReader) {
            reader.setModel(me.model);
        } else {
            if (needsCopy) {
                reader = Ext.apply({}, reader);
            }
            Ext.applyIf(reader, {
                proxy: me,
                model: me.model,
                type : me.defaultReaderType
            });

            reader = Ext.createByAlias('reader.' + reader.type, reader);
        }

        if (reader !== current && reader.onMetaChange) {
            reader.onMetaChange = Ext.Function.createSequence(reader.onMetaChange, this.onMetaChange, this);
        }

        me.reader = reader;
        return me.reader;
    },

    
    getReader: function() {
        return this.reader;
    },

    
    onMetaChange: function(meta) {
        this.fireEvent('metachange', this, meta);
    },

    
    setWriter: function(writer) {
        var me = this,
            needsCopy = true;
            
        if (writer === undefined || typeof writer == 'string') {
            writer = {
                type: writer
            };
            needsCopy = false;
        }

        if (!writer.isWriter) {
            if (needsCopy) {
                writer = Ext.apply({}, writer);
            }
            Ext.applyIf(writer, {
                model: me.model,
                type : me.defaultWriterType
            });

            writer = Ext.createByAlias('writer.' + writer.type, writer);
        }

        me.writer = writer;

        return me.writer;
    },

    
    getWriter: function() {
        return this.writer;
    },

    
    create: Ext.emptyFn,

    
    read: Ext.emptyFn,

    
    update: Ext.emptyFn,

    
    destroy: Ext.emptyFn,

    
    batch: function(options, listeners) {
        var me = this,
            useBatch = me.batchActions,
            batch,
            records,
            actions, aLen, action, a, r, rLen, record;

        if (options.operations === undefined) {
            
            
            options = {
                operations: options,
                listeners: listeners
            };
        }

        if (options.batch) {
            if (Ext.isDefined(options.batch.runOperation)) {
                batch = Ext.applyIf(options.batch, {
                    proxy: me,
                    listeners: {}
                });
            }
        } else {
            options.batch = {
                proxy: me,
                listeners: options.listeners || {}
            };
        }

        if (!batch) {
            batch = new Ext.data.Batch(options.batch);
        }

        batch.on('complete', Ext.bind(me.onBatchComplete, me, [options], 0));

        actions = me.batchOrder.split(',');
        aLen    = actions.length;

        for (a = 0; a < aLen; a++) {
            action  = actions[a];
            records = options.operations[action];

            if (records) {
                if (useBatch) {
                    batch.add(new Ext.data.Operation({
                        action  : action,
                        records : records
                    }));
                } else {
                    rLen = records.length;

                    for (r = 0; r < rLen; r++) {
                        record = records[r];

                        batch.add(new Ext.data.Operation({
                            action  : action,
                            records : [record]
                        }));
                    }
                }
            }
        }

        batch.start();
        return batch;
    },

    
    onBatchComplete: function(batchOptions, batch) {
        var scope = batchOptions.scope || this;

        if (batch.hasException) {
            if (Ext.isFunction(batchOptions.failure)) {
                Ext.callback(batchOptions.failure, scope, [batch, batchOptions]);
            }
        } else if (Ext.isFunction(batchOptions.success)) {
            Ext.callback(batchOptions.success, scope, [batch, batchOptions]);
        }

        if (Ext.isFunction(batchOptions.callback)) {
            Ext.callback(batchOptions.callback, scope, [batch, batchOptions]);
        }
    },

    clone: function() {
        return new this.self(this.proxyConfig);
    }
});


Ext.define('Ext.data.Operation', {
    
    synchronous: true,

    
    action: undefined,

    
    filters: undefined,

    
    sorters: undefined,

    
    groupers: undefined,

    
    start: undefined,

    
    limit: undefined,

    
    batch: undefined,
    
    

    
    callback: undefined,

    
    scope: undefined,

    
    started: false,

    
    running: false,

    
    complete: false,

    
    success: undefined,

    
    exception: false,

    
    error: undefined,

    
    actionCommitRecordsRe: /^(?:create|update)$/i,

    
    actionSkipSyncRe: /^destroy$/i,

    
    constructor: function(config) {
        Ext.apply(this, config || {});
    },

    
    commitRecords: function(serverRecords) {
        var me = this,
            commitRecords = me.actionCommitRecordsRe.test(me.action),
            mc, index, clientRecords, serverRec, clientRec, i, len,
            modifiedFields, recordModifiedFields;

        if (!me.actionSkipSyncRe.test(me.action)) {
            clientRecords = me.records;

            if (clientRecords && clientRecords.length) {

                
                
                
                
                if (commitRecords) {
                    recordModifiedFields = [];
                }
                if (clientRecords.length > 1) {
                    
                    
                    
                    
                    if (me.action == 'update' || clientRecords[0].clientIdProperty) {
                        mc = new Ext.util.MixedCollection();
                        mc.addAll(serverRecords);

                        for (index = clientRecords.length; index--; ) {
                            clientRec = clientRecords[index];
                            serverRec = mc.findBy(me.matchClientRec, clientRec);

                            
                            modifiedFields = clientRec.copyFrom(serverRec);

                            
                            if (commitRecords) {
                                recordModifiedFields.push(modifiedFields);
                            }
                        }
                    } else {
                        for (i = 0, len = clientRecords.length; i < len; ++i) {
                            clientRec = clientRecords[i];
                            serverRec = serverRecords[i];
                            if (clientRec && serverRec) {
                                modifiedFields = me.updateRecord(clientRec, serverRec);

                                
                                if (commitRecords) {
                                    recordModifiedFields.push(modifiedFields);
                                }
                            }
                        }
                    }
                } else {
                    
                    modifiedFields = me.updateRecord(clientRecords[0], serverRecords[0]);   

                    
                    if (commitRecords) {
                        recordModifiedFields[0] = modifiedFields;
                    }
                }

                if (commitRecords) {
                    for (index = clientRecords.length; index--; ) {

                        
                        
                        
                        clientRecords[index].commit(false, recordModifiedFields[index]);
                    }
                }
            }
        }
    },
    
    updateRecord: function(clientRec, serverRec) {
        
        if (serverRec && (clientRec.phantom || clientRec.getId() === serverRec.getId())) {
            return clientRec.copyFrom(serverRec);
        }

        
        return [];
    },

    
    
    
    matchClientRec: function(record) {
        var clientRec = this,
            clientRecordId = clientRec.getId();

        if(clientRecordId && record.getId() === clientRecordId) {
            return true;
        }
        
        
        
        return record.internalId === clientRec.internalId;
    },

    
    setStarted: function() {
        this.started = true;
        this.running = true;
    },

    
    setCompleted: function() {
        this.complete = true;
        this.running  = false;
    },

    
    setSuccessful: function() {
        this.success = true;
    },

    
    setException: function(error) {
        this.exception = true;
        this.success = false;
        this.running = false;
        this.error = error;
    },

    
    hasException: function() {
        return this.exception === true;
    },

    
    getError: function() {
        return this.error;
    },

    
    getRecords: function() {
        var resultSet = this.getResultSet();
        return this.records || (resultSet ? resultSet.records : null);
    },

    
    getResultSet: function() {
        return this.resultSet;
    },

    
    isStarted: function() {
        return this.started === true;
    },

    
    isRunning: function() {
        return this.running === true;
    },

    
    isComplete: function() {
        return this.complete === true;
    },

    
    wasSuccessful: function() {
        return this.isComplete() && this.success === true;
    },

    
    setBatch: function(batch) {
        this.batch = batch;
    },

    
    allowWrite: function() {
        return this.action != 'read';
    }
});


Ext.define('Ext.data.AbstractStore', {
               
                                   
                               
                             
                         
      

    mixins: {
        observable:  Ext.util.Observable ,
        sortable:  Ext.util.Sortable 
    },

    statics: {
        
        create: function(store) {
            if (!store.isStore) {
                if (!store.type) {
                    store.type = 'store';
                }
                store = Ext.createByAlias('store.' + store.type, store);
            }
            return store;
        }
    },

    onClassExtended: function(cls, data, hooks) {
        var model = data.model,
            onBeforeClassCreated;

        if (typeof model == 'string') {
            onBeforeClassCreated = hooks.onBeforeCreated;

            hooks.onBeforeCreated = function() {
                var me = this,
                    args = arguments;

                Ext.require(model, function() {
                    onBeforeClassCreated.apply(me, args);
                });
            };
        }
    },

    
    remoteSort  : false,

    
    remoteFilter: false,

    

    
    autoLoad: undefined,

    
    autoSync: false,

    
    batchUpdateMode: 'operation',

    
    filterOnLoad: true,

    
    sortOnLoad: true,

    
    implicitModel: false,

    
    defaultProxyType: 'memory',

    
    isDestroyed: false,

    
    isStore: true,

    

    

    

    

    

    sortRoot: 'data',

    
    constructor: function(config) {
        var me = this,
            filters;

        

        

        

        

        
        
        

        

        

        

        
        
        

        Ext.apply(me, config);
        

        
        me.removed = [];

        me.mixins.observable.constructor.apply(me, arguments);


        me.model = Ext.ModelManager.getModel(me.model);

        
        Ext.applyIf(me, {
            modelDefaults: null
        });

        
        if (!me.model && me.fields) {
            me.model = Ext.define('Ext.data.Store.ImplicitModel-' + (me.storeId || Ext.id()), {
                extend: 'Ext.data.Model',
                fields: me.fields,
                proxy: me.proxy || me.defaultProxyType
            });

            delete me.fields;

            me.implicitModel = true;
        }


        
        me.setProxy(me.proxy || me.model.getProxy());

        if (!me.disableMetaChangeEvent) {
            me.proxy.on('metachange', me.onMetaChange, me);
        }

        if (me.id && !me.storeId) {
            me.storeId = me.id;
            delete me.id;
        }

        if (me.storeId) {
            Ext.data.StoreManager.register(me);
        }

        me.mixins.sortable.initSortable.call(me);

        
        filters = me.decodeFilters(me.filters);
        me.filters = new Ext.util.MixedCollection();
        me.filters.addAll(filters);
    },

    
    setProxy: function(proxy) {
        var me = this;

        if (proxy instanceof Ext.data.proxy.Proxy) {
            proxy.setModel(me.model);
        } else {
            if (Ext.isString(proxy)) {
                proxy = {
                    type: proxy
                };
            }
            Ext.applyIf(proxy, {
                model: me.model
            });

            proxy = Ext.createByAlias('proxy.' + proxy.type, proxy);
        }

        me.proxy = proxy;

        return me.proxy;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    onMetaChange: function(proxy, meta) {
        this.fireEvent('metachange', this, meta);
    },

    
    create: function(data, options) {
        var me = this,
            instance = Ext.ModelManager.create(Ext.applyIf(data, me.modelDefaults), me.model.modelName),
            operation;

        options = options || {};

        Ext.applyIf(options, {
            action : 'create',
            records: [instance]
        });

        operation = new Ext.data.Operation(options);

        me.proxy.create(operation, me.onProxyWrite, me);

        return instance;
    },

    read: function() {
        return this.load.apply(this, arguments);
    },

    update: function(options) {
        var me = this,
            operation;
        options = options || {};

        Ext.applyIf(options, {
            action : 'update',
            records: me.getUpdatedRecords()
        });

        operation = new Ext.data.Operation(options);

        return me.proxy.update(operation, me.onProxyWrite, me);
    },

    
    onProxyWrite: function(operation) {
        var me = this,
            success = operation.wasSuccessful(),
            records = operation.getRecords();

        switch (operation.action) {
            case 'create':
                me.onCreateRecords(records, operation, success);
                break;
            case 'update':
                me.onUpdateRecords(records, operation, success);
                break;
            case 'destroy':
                me.onDestroyRecords(records, operation, success);
                break;
        }

        if (success) {
            me.fireEvent('write', me, operation);
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, success]);
    },
    
    
    onCreateRecords: Ext.emptyFn,
    
    
    onUpdateRecords: Ext.emptyFn,
    
    
    onDestroyRecords: function(records, operation, success) {
        if (success) {
            this.removed = [];
        }
    },

    
    
    destroy: function(options) {
        var me = this,
            operation;

        options = options || {};

        Ext.applyIf(options, {
            action : 'destroy',
            records: me.getRemovedRecords()
        });

        operation = new Ext.data.Operation(options);

        return me.proxy.destroy(operation, me.onProxyWrite, me);
    },

    
    onBatchOperationComplete: function(batch, operation) {
        return this.onProxyWrite(operation);
    },

    
    onBatchComplete: function(batch, operation) {
        var me = this,
            operations = batch.operations,
            length = operations.length,
            i;

        me.suspendEvents();

        for (i = 0; i < length; i++) {
            me.onProxyWrite(operations[i]);
        }

        me.resumeEvents();

        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },

    
    onBatchException: function(batch, operation) {
        
        
        
        
        
    },

    
    filterNew: function(item) {
        
        return item.phantom === true && item.isValid();
    },

    
    getNewRecords: function() {
        return [];
    },

    
    getUpdatedRecords: function() {
        return [];
    },

    
    getModifiedRecords : function(){
        return [].concat(this.getNewRecords(), this.getUpdatedRecords());
    },
    
    
    filterUpdated: function(item) {
        
        return item.dirty === true && item.phantom !== true && item.isValid();
    },

    
    getRemovedRecords: function() {
        return this.removed;
    },

    filter: function(filters, value) {

    },

    
    decodeFilters: function(filters) {
        if (!Ext.isArray(filters)) {
            if (filters === undefined) {
                filters = [];
            } else {
                filters = [filters];
            }
        }

        var length = filters.length,
            Filter = Ext.util.Filter,
            config, i;

        for (i = 0; i < length; i++) {
            config = filters[i];

            if (!(config instanceof Filter)) {
                Ext.apply(config, {
                    root: 'data'
                });

                
                if (config.fn) {
                    config.filterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        filterFn: config
                    };
                }

                filters[i] = new Filter(config);
            }
        }

        return filters;
    },

    clearFilter: function(supressEvent) {

    },

    isFiltered: function() {

    },

    filterBy: function(fn, scope) {

    },

    
    sync: function(options) {
        var me = this,
            operations = {},
            toCreate = me.getNewRecords(),
            toUpdate = me.getUpdatedRecords(),
            toDestroy = me.getRemovedRecords(),
            needsSync = false;

        if (toCreate.length > 0) {
            operations.create = toCreate;
            needsSync = true;
        }

        if (toUpdate.length > 0) {
            operations.update = toUpdate;
            needsSync = true;
        }

        if (toDestroy.length > 0) {
            operations.destroy = toDestroy;
            needsSync = true;
        }

        if (needsSync && me.fireEvent('beforesync', operations) !== false) {
            options = options || {};

            me.proxy.batch(Ext.apply(options, {
                operations: operations,
                listeners: me.getBatchListeners()
            }));
        }
        
        return me;
    },
    
    
    getBatchListeners: function() {
        var me = this,
            listeners = {
                scope: me,
                exception: me.onBatchException
            };

        if (me.batchUpdateMode == 'operation') {
            listeners.operationcomplete = me.onBatchOperationComplete;
        } else {
            listeners.complete = me.onBatchComplete;
        }

        return listeners;
    },

    
    save: function() {
        return this.sync.apply(this, arguments);
    },

    
    load: function(options) {
        var me = this,
            operation;

        options = Ext.apply({
            action: 'read',
            filters: me.filters.items,
            sorters: me.getSorters()
        }, options);
        me.lastOptions = options;

        operation = new Ext.data.Operation(options);

        if (me.fireEvent('beforeload', me, operation) !== false) {
            me.loading = true;
            me.proxy.read(operation, me.onProxyLoad, me);
        }

        return me;
    },

    
    reload: function(options) {
        return this.load(Ext.apply(this.lastOptions, options));
    },

    
    afterEdit : function(record, modifiedFieldNames) {
        var me = this,
            i, shouldSync;

        if (me.autoSync && !me.autoSyncSuspended) {
            for (i = modifiedFieldNames.length; i--;) {
                
                if (record.fields.get(modifiedFieldNames[i]).persist) {
                    shouldSync = true;
                    break;
                }
            }
            if (shouldSync) {
                me.sync();
            }
        }
        me.onUpdate(record, Ext.data.Model.EDIT, modifiedFieldNames);
        me.fireEvent('update', me, record, Ext.data.Model.EDIT, modifiedFieldNames);
    },

    
    afterReject : function(record) {
        
        
        
        
        
        this.onUpdate(record, Ext.data.Model.REJECT, null);
        this.fireEvent('update', this, record, Ext.data.Model.REJECT, null);
    },

    
    afterCommit : function(record, modifiedFieldNames) {
        if (!modifiedFieldNames) {
            modifiedFieldNames = null;
        }
        this.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames);
        this.fireEvent('update', this, record, Ext.data.Model.COMMIT, modifiedFieldNames);
    },

    onUpdate: Ext.emptyFn,

    onIdChanged: function(model, oldId, newId, oldInternalId){
        this.fireEvent('idchanged', this, model, oldId, newId, oldInternalId);
    },

    
    destroyStore: function() {
        var implicitModelName,
            me = this;

        if (!me.isDestroyed) {
            me.clearListeners();
            if (me.storeId) {
                Ext.data.StoreManager.unregister(me);
            }
            me.clearData();
            me.data = me.tree = me.sorters = me.filters = me.groupers = null;
            if (me.reader) {
                me.reader.destroyReader();
            }
            me.proxy = me.reader = me.writer = null;
            me.isDestroyed = true;

            if (me.implicitModel) {
                implicitModelName = Ext.getClassName(me.model);
                Ext.undefine(implicitModelName);
                Ext.ModelManager.unregisterType(implicitModelName);
            } else {
                me.model = null;
            }
        }
    },
    
    
    getState: function() {
        var me = this,
            hasState,
            result,
            hasGroupers = !!me.groupers,
            groupers = [],
            sorters = [],
            filters = [];

        if (hasGroupers) {
            me.groupers.each(function(g) {
                groupers[groupers.length] = g.serialize();
                hasState = true;
            });
        }

        if (me.sorters) {
            
            me.sorters.each(function(s) {
                
                if (hasGroupers && !me.groupers.contains(s)) {
                    sorters[sorters.length] = s.serialize();
                    hasState = true;
                }
            });
        }

        
        
        if (me.filters && me.statefulFilters) {
            me.filters.each(function(f) {
                filters[filters.length] = f.serialize();
                hasState = true;
            });
        }

        
        if (hasState) {
            result = {};
            if (groupers.length) {
                result.groupers = groupers;
            }
            if (sorters.length) {
                result.sorters = sorters;
            }
            if (filters.length) {
                result.filters = filters;
            }
            return result;
        }
    },

    
    applyState: function(state) {
        var me = this,
            hasSorters = !!me.sorters,
            hasGroupers = !!me.groupers,
            hasFilters = !!me.filters,
            locallySorted;

        if (hasGroupers && state.groupers) {
            me.groupers.clear();
            me.groupers.addAll(me.decodeGroupers(state.groupers));
        }

        if (hasSorters && state.sorters) {
            me.sorters.clear();
            me.sorters.addAll(me.decodeSorters(state.sorters));
        }

        if (hasFilters && state.filters) {
            me.filters.clear();
            me.filters.addAll(me.decodeFilters(state.filters));
        }

        if (hasSorters && hasGroupers) {
            
            me.sorters.insert(0, me.groupers.getRange());
        }

        
        if (me.autoLoad && (me.remoteSort || me.remoteGroup || me.remoteFilter)) {
            if (me.autoLoad === true) {
                me.reload();
            } else {
                me.reload(me.autoLoad);
            }
        }

        
        if (hasFilters && me.filters.length && !me.remoteFilter) {
            me.filter();
            locallySorted = me.sortOnFilter;
        }

        
        if (hasSorters && me.sorters.length && !me.remoteSort && !locallySorted) {
            me.sort();
        }
    },

    
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.data.sortBy(sorterFn);
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        me.fireEvent('sort', me, me.sorters.getRange());
    },

    
    clearData: Ext.emptyFn,
    
    
    getCount: Ext.emptyFn,

    
    getById: Ext.emptyFn,

    
    removeAll: Ext.emptyFn,
    
    

    
    isLoading: function() {
        return !!this.loading;
    },

    
    suspendAutoSync: function() {
        this.autoSyncSuspended = true;
    },

    
    resumeAutoSync: function() {
        this.autoSyncSuspended = false;
    }

});



Ext.define('Ext.app.domain.Store', {
    extend:  Ext.app.EventDomain ,
    singleton: true,
    
               
                                
      
    
    type: 'store',
    idProperty: 'storeId',
    
    constructor: function() {
        var me = this;
        
        me.callParent();
        me.monitor(Ext.data.AbstractStore);
    }
});


Ext.define('Ext.app.Controller', {
               
                           
                           
                                
                               
                                
                                   
                              
      
    
           
                                   
      

    mixins: {
        observable:  Ext.util.Observable 
    },

    

    statics: {
        strings: {
            model: {
                getter: 'getModel',
                upper: 'Model'
            },

            view: {
                getter: 'getView',
                upper: 'View'
            },

            controller: {
                getter: 'getController',
                upper: 'Controller'
            },

            store: {
                getter: 'getStore',
                upper: 'Store'
            }
        },

        controllerRegex: /^(.*)\.controller\./,

        createGetter: function(baseGetter, name) {
            return function () {
                return this[baseGetter](name);
            };
        },

        getGetterName: function(name, kindUpper) {
            var fn       = 'get',
                parts    = name.split('.'),
                numParts = parts.length,
                index;

            
            for (index = 0; index < numParts; index++) {
                fn += Ext.String.capitalize(parts[index]);
            }

            fn += kindUpper;
            
            return fn;
        },

        
        processDependencies: function(cls, requires, namespace, kind, names) {
            if (!names || !names.length) {
                return;
            }

            var me = this,
                strings = me.strings[kind],
                o, absoluteName, shortName, name, j, subLn, getterName, getter;
                
             if (!Ext.isArray(names)) {
                 names = [names];
             }

            for (j = 0, subLn = names.length; j < subLn; j++) {
                name = names[j];
                o = me.getFullName(name, kind, namespace);
                absoluteName = o.absoluteName;
                shortName = o.shortName;

                requires.push(absoluteName);
                getterName = me.getGetterName(shortName, strings.upper);
                cls[getterName] = getter = me.createGetter(strings.getter, name);

                
                if (kind !== 'controller') {
                    
                    
                    
                    
                    getter['Ext.app.getter'] = true;
                }
            }
        },

        getFullName: function(name, kind, namespace) {
            var shortName = name,
                sep, absoluteName;

            if ((sep = name.indexOf('@')) > 0) {
                
                
                
                
                shortName    = name.substring(0, sep); 
                absoluteName = name.substring(sep + 1) + '.' + shortName; 
            }
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            else if (name.indexOf('.') > 0 && (Ext.ClassManager.isCreated(name) ||
                     Ext.Loader.isAClassNameWithAKnownPrefix(name))) {
                absoluteName = name;
            }
            else {

                if (namespace) {
                    absoluteName = namespace + '.' + kind + '.' + name;
                    shortName    = name;
                }
                else {
                    absoluteName = name;
                }
            }

            return {
                absoluteName: absoluteName,
                shortName:    shortName
            };
        }
    },
    
    
    application: null,

    

    

    

    

    onClassExtended: function(cls, data, hooks) {
        var onBeforeClassCreated = hooks.onBeforeCreated;

        hooks.onBeforeCreated = function(cls, data) {
            var Controller = Ext.app.Controller,
                ctrlRegex  = Controller.controllerRegex,
                requires   = [],
                className, namespace, requires, proto, match;

            proto = cls.prototype;
            
            
            className = Ext.getClassName(cls);
            namespace = data.$namespace                 ||
                        Ext.app.getNamespace(className) ||
                        ((match = ctrlRegex.exec(className)) && match[1]);

            if (namespace) {
                proto.$namespace = namespace;
            }

            Controller.processDependencies(proto, requires, namespace, 'model',      data.models);
            Controller.processDependencies(proto, requires, namespace, 'view',       data.views);
            Controller.processDependencies(proto, requires, namespace, 'store',      data.stores);
            Controller.processDependencies(proto, requires, namespace, 'controller', data.controllers);

            Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));
        };
    },

    
    constructor: function (config) {
        var me = this;

        me.mixins.observable.constructor.call(me, config);

        if (me.refs) {
            me.ref(me.refs);
        }

        me.eventbus = Ext.app.EventBus;
        
        me.initAutoGetters();
    },
    
    initAutoGetters: function() {
        var proto = this.self.prototype,
            prop, fn;

        for (prop in proto) {
            fn = proto[prop];

            
            
            if (fn && fn['Ext.app.getter']) {
                fn.call(this);
            }
        }
    },

    doInit: function(app) {
        var me = this;

        if (!me._initialized) {
            me.init(app);
            me._initialized = true;
        }
    },
    
    finishInit: function(app) {
        var me = this,
            controllers = me.controllers,
            controller, i, l;
        
        if (me._initialized && controllers && controllers.length) {
            for (i = 0, l = controllers.length; i < l; i++) {
                controller = me.getController(controllers[i]);
                controller.finishInit(app);
            }
        }
    },

    
    init: Ext.emptyFn,

    
    onLaunch: Ext.emptyFn,

    ref: function(refs) {
        var me = this,
            i = 0,
            length = refs.length,
            info, ref, fn;

        refs = Ext.Array.from(refs);

        me.references = me.references || [];

        for (; i < length; i++) {
            info = refs[i];
            ref  = info.ref;
            fn   = 'get' + Ext.String.capitalize(ref);

            if (!me[fn]) {
                me[fn] = Ext.Function.pass(me.getRef, [ref, info], me);
            }
            me.references.push(ref.toLowerCase());
        }
    },

    
    addRef: function(refs) {
        this.ref(refs);
    },

    getRef: function(ref, info, config) {
        var me = this,
            refCache = me.refCache || (me.refCache = {}),
            cached = refCache[ref];

        info = info || {};
        config = config || {};

        Ext.apply(info, config);

        if (info.forceCreate) {
            return Ext.ComponentManager.create(info, 'component');
        }

        if (!cached) {
            if (info.selector) {
                refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];
            }
            
            if (!cached && info.autoCreate) {
                refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
            }
            
            if (cached) {
                cached.on('beforedestroy', function() {
                    refCache[ref] = null;
                });
            }
        }

        return cached;
    },

    
    hasRef: function(ref) {
        var references = this.references;
        return references && Ext.Array.indexOf(references, ref.toLowerCase()) !== -1;
    },

    
    control: function(selectors, listeners, controller) {
        var me = this,
            ctrl = controller,
            obj;

        if (Ext.isString(selectors)) {
            obj = {};
            obj[selectors] = listeners;
        }
        else {
            obj = selectors;
            ctrl = listeners;
        }

        me.eventbus.control(obj, ctrl || me);
    },

    
    listen: function (to, controller) {
        this.eventbus.listen(to, controller || this);
    },

    
    getController: function(id) {
        var me = this,
            app = me.application;

        if (id === me.id) {
            return me;
        }

        return app && app.getController(id);
    },

    
    getStore: function(name) {
        var storeId, store;

        storeId = (name.indexOf('@') == -1) ? name : name.split('@')[0];
        store   = Ext.StoreManager.get(storeId);

        if (!store) {
            name = Ext.app.Controller.getFullName(name, 'store', this.$namespace);

            if (name) {
                store = Ext.create(name.absoluteName, {
                    storeId: storeId
                });
            }
        }

        return store;
    },

    
    getModel: function(model) {
        var name = Ext.app.Controller.getFullName(model, 'model', this.$namespace);

        return name && Ext.ModelManager.getModel(name.absoluteName);
    },

    
    getView: function(view) {
        var name = Ext.app.Controller.getFullName(view, 'view', this.$namespace);

        return name && Ext.ClassManager.get(name.absoluteName);
    },

    
    getApplication: function() {
        return this.application;
    }
});


Ext.define('Ext.container.DockingContainer', {

    

                                                           

    

    isDockingContainer: true,
    
    
    
    

    
    defaultDockWeights: {
        top:    { render: 1, visual: 1 },
        left:   { render: 3, visual: 5 },
        right:  { render: 5, visual: 7 },
        bottom: { render: 7, visual: 3 }
    },

    
    
    
    
    dockOrder: {
        top: -1,
        left: -1,
        right: 1,
        bottom: 1
    },

    
    horizontalDocks: 0,

    
    addDocked : function(items, pos) {
        var me = this,
            i = 0,
            item, length;

        items = me.prepareItems(items);
        length = items.length;

        for (; i < length; i++) {
            item = items[i];
            item.dock = item.dock || 'top';
            if (item.dock === 'left' || item.dock === 'right') {
                me.horizontalDocks++;
            }

            if (pos !== undefined) {
                i += pos;
                me.dockedItems.insert(i, item);
            } else {
                me.dockedItems.add(item);
            }
            
            item.onAdded(me, i);
            if (me.hasListeners.dockedadd) {
                me.fireEvent('dockedadd', me, item, i);
            }
            if (me.onDockedAdd !== Ext.emptyFn) {
                me.onDockedAdd(item);
            }
        }

        if (me.rendered && !me.suspendLayout) {
            me.updateLayout();
        }
        return items;
    },

    destroyDockedItems: function(){
        var dockedItems = this.dockedItems,
            c;

        if (dockedItems) {
            while ((c = dockedItems.first())) {
                this.removeDocked(c, true);
            }
        }
    },

    doRenderDockedItems: function (out, renderData, after) {
        
        
        

        
        
        
        
        var me = renderData.$comp,
            layout = me.componentLayout,
            items,
            tree;

        if (layout.getDockedItems && !renderData.$skipDockedItems) {
            items = layout.getDockedItems('render', !after);
            tree = items && layout.getItemsRenderTree(items);

            if (tree) {
                Ext.DomHelper.generateMarkup(tree, out);
            }
        }
    },

    
    getDockedComponent: function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }
        return this.dockedItems.get(comp);
    },

    
    getDockedItems : function(selector, beforeBody) {
        var dockedItems = this.getComponentLayout().getDockedItems('render', beforeBody);

        if (selector && dockedItems.length) {
            dockedItems = Ext.ComponentQuery.query(selector, dockedItems);
        }

        return dockedItems;
    },

    getDockingRefItems: function(deep, containerItems) {
        
        var selector = deep && '*,* *',
            
            dockedItems = this.getDockedItems(selector, true),
            items;

        
        dockedItems.push.apply(dockedItems, containerItems);

        
        items = this.getDockedItems(selector, false);
        dockedItems.push.apply(dockedItems, items);

        return dockedItems;
    },

    initDockingItems: function() {
        var me = this,
            items = me.dockedItems;

        me.dockedItems = new Ext.util.AbstractMixedCollection(false, me.getComponentId);
        if (items) {
            me.addDocked(items);
        }
    },

    
    insertDocked : function(pos, items) {
        this.addDocked(items, pos);
    },

    
    
    onDockedAdd : Ext.emptyFn,
    
    onDockedRemove : Ext.emptyFn,

    
    removeDocked : function(item, autoDestroy) {
        var me = this,
            layout,
            hasLayout;

        autoDestroy = autoDestroy === true || (autoDestroy !== false && me.autoDestroy);
        if (!me.dockedItems.contains(item)) {
            return item;
        }
        if (item.dock === 'left' || item.dock === 'right') {
            me.horizontalDocks--;
        }

        layout = me.componentLayout;
        hasLayout = layout && me.rendered;

        if (hasLayout) {
            layout.onRemove(item);
        }

        me.dockedItems.remove(item);
        
        item.onRemoved(item.destroying || autoDestroy);
        me.onDockedRemove(item);

        if (autoDestroy) {
            item.destroy();
        } else if (hasLayout) {
            
            layout.afterRemove(item);
        }
        
        if (me.hasListeners.dockedremove) {
            me.fireEvent('dockedremove', me, item);
        }

        if (!me.destroying && !me.suspendLayout) {
            me.updateLayout();
        }

        return item;
    },

    setupDockingRenderTpl: function (renderTpl) {
        renderTpl.renderDockedItems = this.doRenderDockedItems;
    }
});


Ext.define('Ext.toolbar.Fill', {
    extend:  Ext.Component ,
    alias: 'widget.tbfill',
    alternateClassName: 'Ext.Toolbar.Fill',
    
    isFill : true,
    flex: 1
});


Ext.define('Ext.layout.container.boxOverflow.None', {
    alternateClassName: 'Ext.layout.boxOverflow.None',
    
    constructor: function(layout, config) {
        this.layout = layout;
        Ext.apply(this, config);
    },

    handleOverflow: Ext.emptyFn,

    clearOverflow: Ext.emptyFn,

    beginLayout: Ext.emptyFn,
    beginLayoutCycle: Ext.emptyFn,

    calculate: function(ownerContext) {
        var me = this,
            plan = ownerContext.state.boxPlan,
            overflow;

        if (plan && plan.tooNarrow) {
            overflow = me.handleOverflow(ownerContext);

            if (overflow) {
                if (overflow.reservedSpace) {
                    me.layout.publishInnerCtSize(ownerContext, overflow.reservedSpace);
                }

                
                
                
                
                
                
                
                
                
                
            }
        } else {
            me.clearOverflow();
        }
    },

    completeLayout: Ext.emptyFn,

    finishedLayout: function (ownerContext) {
        var me = this,
            owner = me.layout.owner,
            hiddens,
            hiddenCount;

        
        if (owner.hasListeners.overflowchange) {
            hiddens = owner.query('>[hidden]');
            hiddenCount = hiddens.length;
            if (hiddenCount !== me.lastHiddenCount) {
                owner.fireEvent('overflowchange', me.lastHiddenCount, hiddenCount, hiddens);
                me.lastHiddenCount = hiddenCount;
            }
        }
    },

    onRemove: Ext.emptyFn,

    
    getItem: function(item) {
        return this.layout.owner.getComponent(item);
    },
    
    getOwnerType: function(owner){
        var type;
        if (owner.isToolbar) {
            type = 'toolbar';
        } else if (owner.isTabBar) {
            type = 'tabbar';
        } else if (owner.isMenu) {
            type = 'menu';
        } else {
            type = owner.getXType();
        }
        
        return type;
    },

    getPrefixConfig: Ext.emptyFn,
    getSuffixConfig: Ext.emptyFn,
    getOverflowCls: function() {
        return '';
    }
});


Ext.define('Ext.toolbar.Item', {
    extend:  Ext.Component ,
    alias: 'widget.tbitem',
    alternateClassName: 'Ext.Toolbar.Item',
    enable:Ext.emptyFn,
    disable:Ext.emptyFn,
    focus:Ext.emptyFn
    
});


Ext.define('Ext.toolbar.Separator', {
    extend:  Ext.toolbar.Item ,
    alias: 'widget.tbseparator',
    alternateClassName: 'Ext.Toolbar.Separator',
    baseCls: Ext.baseCSSPrefix + 'toolbar-separator',
    focusable: false
});


Ext.define('Ext.button.Manager', {
    singleton: true,

    alternateClassName: 'Ext.ButtonToggleManager',

    groups: {},

    pressedButton: null,

    buttonSelector: '.' + Ext.baseCSSPrefix + 'btn',

    init: function() {
        var me = this;
        if (!me.initialized) {
            Ext.getDoc().on({
                keydown: me.onDocumentKeyDown,
                mouseup: me.onDocumentMouseUp,
                scope: me
            });
            me.initialized = true;
        }
    },

    
    
    onDocumentKeyDown: function(e) {
        var k = e.getKey(),
            btn;

        
        if (k === e.SPACE || k === e.ENTER) {

            
            btn = e.getTarget(this.buttonSelector);

            
            if (btn) {
                Ext.getCmp(btn.id).onClick(e);
            }
        }
    },

    
    
    
    onButtonMousedown: function(button, e) {
        var pressed = this.pressedButton;
        if (pressed) {
            pressed.onMouseUp(e);
        }
        this.pressedButton = button;
    },

    onDocumentMouseUp: function(e) {
        var pressed = this.pressedButton;
        
        if (pressed) {
            pressed.onMouseUp(e);
            this.pressedButton = null;
        }
    },

    toggleGroup: function(btn, state) {
        if (state) {
            var g = this.groups[btn.toggleGroup],
                length = g.length,
                i;

            for (i = 0; i < length; i++) {
                if (g[i] !== btn) {
                    g[i].toggle(false);
                }
            }
        }
    },

    register: function(btn) {
        var me = this,
            groups = this.groups,
            group = groups[btn.toggleGroup];

        me.init();
        if (!btn.toggleGroup) {
            return;
        }

        if (!group) {
            group = groups[btn.toggleGroup] = [];
        }
        group.push(btn);
        btn.on('toggle', me.toggleGroup, me);
    },

    unregister: function(btn) {
        if (!btn.toggleGroup) {
            return;
        }
        var me = this,
            group = me.groups[btn.toggleGroup];

        if (group) {
            Ext.Array.remove(group, btn);
            btn.un('toggle', me.toggleGroup, me);
        }
    },

    
    
    
    getPressed: function(group) {
        var g = this.groups[group],
            i = 0,
            len;

        if (g) {
            for (len = g.length; i < len; i++) {
                if (g[i].pressed === true) {
                    return g[i];
                }
            }
        }
        return null;
    } 
});


Ext.define('Ext.menu.Manager', {
    singleton: true,
               
                                   
                         
      
    alternateClassName: 'Ext.menu.MenuMgr',

                            
    
    menuSelector: '.' + Ext.baseCSSPrefix + 'menu',

    menus: {},
    groups: {},
    attached: false,
    lastShow: new Date(),

    init: function() {
        var me = this;
        
        me.active = new Ext.util.MixedCollection();
        Ext.getDoc().addKeyListener(27, function() {
            if (me.active.length > 0) {
                me.hideAll();
            }
        }, me);
    },

    
    hideAll: function() {
        var active = this.active,
            menus, m, mLen;

        if (active && active.length > 0) {
            menus = Ext.Array.slice(active.items);
            mLen  = menus.length;

            for (m = 0; m < mLen; m++) {
                menus[m].hide();
            }

            return true;
        }
        return false;
    },

    onHide: function(m) {
        var me = this,
            active = me.active;
        active.remove(m);
        if (active.length < 1) {
            Ext.getDoc().un('mousedown', me.onMouseDown, me);
            me.attached = false;
        }
    },

    onShow: function(m) {
        var me = this,
            active   = me.active,
            attached = me.attached;

        me.lastShow = new Date();
        active.add(m);
        if (!attached) {
            Ext.getDoc().on('mousedown', me.onMouseDown, me, {
                
                
                buffer: Ext.isIE9m ? 10 : undefined
            });
            me.attached = true;
        }
        m.toFront();
    },

    onBeforeHide: function(m) {
        if (m.activeChild) {
            m.activeChild.hide();
        }
        if (m.autoHideTimer) {
            clearTimeout(m.autoHideTimer);
            delete m.autoHideTimer;
        }
    },

    onBeforeShow: function(m) {
        var active = this.active,
            parentMenu = m.parentMenu;
            
        active.remove(m);
        if (!parentMenu && !m.allowOtherMenus) {
            this.hideAll();
        }
        else if (parentMenu && parentMenu.activeChild && m != parentMenu.activeChild) {
            parentMenu.activeChild.hide();
        }
    },

    
    onMouseDown: function(e) {
        var me = this,
            active = me.active,
            lastShow = me.lastShow,
            doHide = true;

        if (Ext.Date.getElapsed(lastShow) > 50 && active.length > 0 && !e.getTarget(me.menuSelector)) {
            
            
            
            if (Ext.isIE9m && !Ext.getDoc().contains(e.target)) {
                doHide = false;
            }
            if (doHide) {
                me.hideAll();
            }
        }
    },

    
    register: function(menu) {
        var me = this;

        if (!me.active) {
            me.init();
        }

        if (menu.floating) {
            me.menus[menu.id] = menu;
            menu.on({
                beforehide: me.onBeforeHide,
                hide: me.onHide,
                beforeshow: me.onBeforeShow,
                show: me.onShow,
                scope: me
            });
        }
    },

    
    get: function(menu) {
        var menus = this.menus;
        
        if (typeof menu == 'string') { 
            if (!menus) {  
                return null;
            }
            return menus[menu];
        } else if (menu.isMenu) {  
            return menu;
        } else if (Ext.isArray(menu)) { 
            return new Ext.menu.Menu({items:menu});
        } else { 
            return Ext.ComponentManager.create(menu, 'menu');
        }
    },

    
    unregister: function(menu) {
        var me = this,
            menus = me.menus,
            active = me.active;

        delete menus[menu.id];
        active.remove(menu);
        menu.un({
            beforehide: me.onBeforeHide,
            hide: me.onHide,
            beforeshow: me.onBeforeShow,
            show: me.onShow,
            scope: me
        });
    },

    
    registerCheckable: function(menuItem) {
        var groups  = this.groups,
            groupId = menuItem.group;

        if (groupId) {
            if (!groups[groupId]) {
                groups[groupId] = [];
            }

            groups[groupId].push(menuItem);
        }
    },

    
    unregisterCheckable: function(menuItem) {
        var groups  = this.groups,
            groupId = menuItem.group;

        if (groupId) {
            Ext.Array.remove(groups[groupId], menuItem);
        }
    },

    onCheckChange: function(menuItem, state) {
        var groups  = this.groups,
            groupId = menuItem.group,
            i       = 0,
            group, ln, curr;

        if (groupId && state) {
            group = groups[groupId];
            ln = group.length;
            for (; i < ln; i++) {
                curr = group[i];
                if (curr != menuItem) {
                    curr.setChecked(false);
                }
            }
        }
    }
});


Ext.define('Ext.util.ClickRepeater', {
    extend:  Ext.util.Observable ,

    
    constructor : function(el, config){
        var me = this;

        me.el = Ext.get(el);
        me.el.unselectable();

        Ext.apply(me, config);

        me.callParent();

        me.addEvents(
        
        "mousedown",
        
        "click",
        
        "mouseup"
        );

        if(!me.disabled){
            me.disabled = true;
            me.enable();
        }

        
        if(me.handler){
            me.on("click", me.handler,  me.scope || me);
        }
    },

    

    

    

    
    interval : 20,

    
    delay: 250,

    
    preventDefault : true,

    
    stopDefault : false,

    timer : 0,

    
    enable: function(){
        if(this.disabled){
            this.el.on('mousedown', this.handleMouseDown, this);
            
            
            if (Ext.isIE && !(Ext.isIE10p || (Ext.isStrict && Ext.isIE9))){
                this.el.on('dblclick', this.handleDblClick, this);
            }
            if(this.preventDefault || this.stopDefault){
                this.el.on('click', this.eventOptions, this);
            }
        }
        this.disabled = false;
    },

    
    disable: function( force){
        if(force || !this.disabled){
            clearTimeout(this.timer);
            if(this.pressedCls){
                this.el.removeCls(this.pressedCls);
            }
            Ext.getDoc().un('mouseup', this.handleMouseUp, this);
            this.el.removeAllListeners();
        }
        this.disabled = true;
    },

    
    setDisabled: function(disabled){
        this[disabled ? 'disable' : 'enable']();
    },

    eventOptions: function(e){
        if(this.preventDefault){
            e.preventDefault();
        }
        if(this.stopDefault){
            e.stopEvent();
        }
    },

    
    destroy : function() {
        this.disable(true);
        Ext.destroy(this.el);
        this.clearListeners();
    },

    handleDblClick : function(e){
        clearTimeout(this.timer);
        this.el.blur();

        this.fireEvent("mousedown", this, e);
        this.fireEvent("click", this, e);
    },

    
    handleMouseDown : function(e){
        clearTimeout(this.timer);
        this.el.blur();
        if(this.pressedCls){
            this.el.addCls(this.pressedCls);
        }
        this.mousedownTime = new Date();

        Ext.getDoc().on("mouseup", this.handleMouseUp, this);
        this.el.on("mouseout", this.handleMouseOut, this);

        this.fireEvent("mousedown", this, e);
        this.fireEvent("click", this, e);

        
        if (this.accelerate) {
            this.delay = 400;
        }

        
        
        e = new Ext.EventObjectImpl(e);

        this.timer =  Ext.defer(this.click, this.delay || this.interval, this, [e]);
    },

    
    click : function(e){
        this.fireEvent("click", this, e);
        this.timer =  Ext.defer(this.click, this.accelerate ?
            this.easeOutExpo(Ext.Date.getElapsed(this.mousedownTime),
                400,
                -390,
                12000) :
            this.interval, this, [e]);
    },

    easeOutExpo : function (t, b, c, d) {
        return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
    },

    
    handleMouseOut : function(){
        clearTimeout(this.timer);
        if(this.pressedCls){
            this.el.removeCls(this.pressedCls);
        }
        this.el.on("mouseover", this.handleMouseReturn, this);
    },

    
    handleMouseReturn : function(){
        this.el.un("mouseover", this.handleMouseReturn, this);
        if(this.pressedCls){
            this.el.addCls(this.pressedCls);
        }
        this.click();
    },

    
    handleMouseUp : function(e){
        clearTimeout(this.timer);
        this.el.un("mouseover", this.handleMouseReturn, this);
        this.el.un("mouseout", this.handleMouseOut, this);
        Ext.getDoc().un("mouseup", this.handleMouseUp, this);
        if(this.pressedCls){
            this.el.removeCls(this.pressedCls);
        }
        this.fireEvent("mouseup", this, e);
    }
});


Ext.define('Ext.layout.component.Button', {

    

    alias: ['layout.button'],

    extend:  Ext.layout.component.Auto ,

    

    type: 'button',

    htmlRE: /<.*>/,

    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            text = owner.text;

        me.callParent(arguments);
        ownerContext.btnWrapContext = ownerContext.getEl('btnWrap');
        ownerContext.btnElContext = ownerContext.getEl('btnEl');
        ownerContext.btnInnerElContext = ownerContext.getEl('btnInnerEl');
        ownerContext.btnIconElContext = ownerContext.getEl('btnIconEl');

        if (text && me.htmlRE.test(text)) {
            ownerContext.isHtmlText = true;
            
            
            
            
            owner.btnInnerEl.setStyle('line-height', 'normal');
            owner.btnInnerEl.setStyle('padding-top', '');
        }
    },

    beginLayoutCycle: function(ownerContext) {
        var owner = this.owner,
            lastWidthModel = this.lastWidthModel;

        this.callParent(arguments);

        if (lastWidthModel && !this.lastWidthModel.shrinkWrap &&
            ownerContext.widthModel.shrinkWrap) {
            
            owner.btnWrap.setStyle('height', '');
            owner.btnEl.setStyle('height', '');
            owner.btnInnerEl.setStyle('line-height', '');
        }
    },

    calculate: function(ownerContext) {
        var me = this,
            owner = me.owner,
            btnElContext = ownerContext.btnElContext,
            btnInnerElContext = ownerContext.btnInnerElContext,
            btnWrapContext = ownerContext.btnWrapContext,
            mmax = Math.max,
            ownerHeight, contentHeight, btnElHeight, innerElHeight;

        me.callParent(arguments);

        if (ownerContext.heightModel.shrinkWrap) {
            
            
            
            

            
            
            btnElHeight = owner.btnEl.getHeight();
            if (ownerContext.isHtmlText) {
                me.centerInnerEl(
                    ownerContext,
                    btnElHeight
                );
                me.ieCenterIcon(ownerContext, btnElHeight);
            }
        } else {
            
            
            ownerHeight = ownerContext.getProp('height');

            
            if (ownerHeight) {
                
                
                contentHeight = ownerHeight - ownerContext.getFrameInfo().height - ownerContext.getPaddingInfo().height;

                
                
                
                btnElHeight = contentHeight;
                if ((owner.menu || owner.split) && owner.arrowAlign === 'bottom') {
                    
                    
                    btnElHeight -= btnWrapContext.getPaddingInfo().bottom;
                }

                
                
                
                
                innerElHeight = btnElHeight;
                if ((owner.icon || owner.iconCls || owner.glyph) &&
                    (owner.iconAlign === 'top' || owner.iconAlign === 'bottom')) {
                    innerElHeight -= btnInnerElContext.getPaddingInfo().height;
                }

                btnWrapContext.setProp('height', mmax(0, contentHeight));
                btnElContext.setProp('height', mmax(0, btnElHeight));
                
                if (ownerContext.isHtmlText) {
                    
                    
                    me.centerInnerEl(ownerContext, btnElHeight);
                } else {
                    
                    
                    
                    btnInnerElContext.setProp('line-height', mmax(0, innerElHeight) + 'px');
                }
                me.ieCenterIcon(ownerContext, btnElHeight);
            } else if (ownerHeight !== 0) {
                
                me.done = false;
            }
        }
    },

    centerInnerEl: function(ownerContext, btnElHeight) {
        var me = this,
            btnInnerElContext = ownerContext.btnInnerElContext,
            innerElHeight = me.owner.btnInnerEl.getHeight();

        if (ownerContext.heightModel.shrinkWrap && (btnElHeight < innerElHeight)) {
            
            
            
            ownerContext.btnElContext.setHeight(innerElHeight);
        } else if (btnElHeight > innerElHeight) {
            
            
            
            btnInnerElContext.setProp(
                'padding-top', 
                Math.round((btnElHeight - innerElHeight) / 2) +
                    
                    
                    
                    btnInnerElContext.getPaddingInfo().top 
            );
        }
    },

    ieCenterIcon: function(ownerContext, btnElHeight) {
        var iconAlign = this.owner.iconAlign;

        if ((Ext.isIEQuirks || Ext.isIE6) &&
            (iconAlign === 'left' || iconAlign === 'right')) {
            
            
            
            
            
            
            ownerContext.btnIconElContext.setHeight(btnElHeight);
        }
    },

    publishInnerWidth: function(ownerContext, width) {
        if (this.owner.getFrameInfo().table) {
            
            
            
            ownerContext.btnInnerElContext.setWidth(
                width -
                
                ownerContext.getFrameInfo().width - ownerContext.getPaddingInfo().width -
                
                
                ownerContext.btnWrapContext.getPaddingInfo().width
            );
        }
    }

});


Ext.define('Ext.util.TextMetrics', {
    statics: {
        shared: null,
        
        measure: function(el, text, fixedWidth){
            var me = this,
                shared = me.shared;
            
            if(!shared){
                shared = me.shared = new me(el, fixedWidth);
            }
            shared.bind(el);
            shared.setFixedWidth(fixedWidth || 'auto');
            return shared.getSize(text);
        },
        
        
        destroy: function(){
            var me = this;
            Ext.destroy(me.shared);
            me.shared = null;
        }
    },
    
    
    constructor: function(bindTo, fixedWidth){
        var me = this,
            measure = Ext.getBody().createChild({
                cls: Ext.baseCSSPrefix + 'textmetrics'
            });
            
        me.measure = measure; 
        if (bindTo) {
            me.bind(bindTo);
        }
        
        measure.position('absolute');
        measure.setLocalXY(-1000, -1000);
        measure.hide();

        if (fixedWidth) {
           measure.setWidth(fixedWidth);
        }
    },
    
    
    getSize: function(text){
        var measure = this.measure,
            size;
        
        measure.update(text);
        size = measure.getSize();
        measure.update('');
        return size;
    },
    
    
    bind: function(el){
        var me = this;
        
        me.el = Ext.get(el);
        me.measure.setStyle(
            me.el.getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
        );
    },
    
    
     setFixedWidth : function(width){
         this.measure.setWidth(width);
     },
     
     
     getWidth : function(text){
         this.measure.dom.style.width = 'auto';
         return this.getSize(text).width;
     },
     
     
     getHeight : function(text){
         return this.getSize(text).height;
     },
     
     
     destroy: function(){
         var me = this;
         me.measure.remove();
         delete me.el;
         delete me.measure;
     }
}, function(){
    Ext.Element.addMethods({
        
        getTextWidth : function(text, min, max){
            return Ext.Number.constrain(Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width, min || 0, max || 1000000);
        }
    });
});


Ext.define('Ext.button.Button', {

    
    alias: 'widget.button',
    extend:  Ext.Component ,

               
                             
                           
                                 
                                      
                               
                         
      
    
    mixins: {
        queryable:  Ext.Queryable 
    },

    alternateClassName: 'Ext.Button',
    

    
    isButton: true,
    componentLayout: 'button',

    
    hidden: false,

    
    disabled: false,

    
    pressed: false,

    

    

    

    

    

    

    

    

    

    

    
    tabIndex: 0,

    

    
    enableToggle: false,

    

    

    
    menuAlign: 'tl-bl?',

    
    showEmptyMenu: false,

    
    textAlign: 'center',

    

    

    

    
    clickEvent: 'click',

    
    preventDefault: true,

    
    handleMouseEvents: true,

    
    tooltipType: 'qtip',

    
    baseCls: Ext.baseCSSPrefix + 'btn',

    
    pressedCls: 'pressed',

    
    overCls: 'over',

    
    focusCls: 'focus',

    
    menuActiveCls: 'menu-active',

    

    
     hrefTarget: '_blank',

     

    

    

    childEls: [
        'btnEl', 'btnWrap', 'btnInnerEl', 'btnIconEl'
    ],

    
    
    
    renderTpl: [
        '<span id="{id}-btnWrap" class="{baseCls}-wrap',
            '<tpl if="splitCls"> {splitCls}</tpl>',
            '{childElCls}" unselectable="on">',
            '<span id="{id}-btnEl" class="{baseCls}-button">',
                '<span id="{id}-btnInnerEl" class="{baseCls}-inner {innerCls}',
                    '{childElCls}" unselectable="on">',
                    '{text}',
                '</span>',
                '<span role="img" id="{id}-btnIconEl" class="{baseCls}-icon-el {iconCls}',
                    '{childElCls} {glyphCls}" unselectable="on" style="',
                    '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>',
                    '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">',
                    '<tpl if="glyph">&#{glyph};</tpl><tpl if="iconCls || iconUrl">&#160;</tpl>',
                '</span>',
            '</span>',
        '</span>',
        
        '<tpl if="closable">',
            '<span id="{id}-closeEl" class="{baseCls}-close-btn" title="{closeText}" tabIndex="0"></span>',
        '</tpl>'
    ],

    
    scale: 'small',

    
    allowedScales: ['small', 'medium', 'large'],

    

    
    iconAlign: 'left',

    
    arrowAlign: 'right',

    
    arrowCls: 'arrow',

    

    

    

    maskOnDisable: false,

    shrinkWrap: 3,

    frame: true,

    
    _triggerRegion: {},

    
    initComponent: function() {
        var me = this;

        
        
        me.autoEl = {
            tag: 'a',
            role: 'button',
            hidefocus: 'on',
            unselectable: 'on'
        };

        
        me.addCls('x-unselectable');

        me.callParent(arguments);

        me.addEvents(
            
            'click',

            
            'toggle',

            
            'mouseover',

            
            'mouseout',

            
            'menushow',

            
            'menuhide',

            
            'menutriggerover',

            
            'menutriggerout',

            
            'textchange',

            
            'iconchange',

            
            'glyphchange'
        );

        if (me.menu) {
            
            me.split = true;

            
            me.menu = Ext.menu.Manager.get(me.menu);

            
            
            me.menu.ownerButton = me;
        }

        
        if (me.url) {
            me.href = me.url;
        }

        
        if (me.href && !me.hasOwnProperty('preventDefault')) {
            me.preventDefault = false;
        }

        if (Ext.isString(me.toggleGroup) && me.toggleGroup !== '') {
            me.enableToggle = true;
        }

        if (me.html && !me.text) {
            me.text = me.html;
            delete me.html;
        }

        me.glyphCls = me.baseCls + '-glyph';
    },

    
    getActionEl: function() {
        return this.el;
    },

    
    getFocusEl: function() {
        return this.el;
    },

    
    onDisable: function(){
        this.callParent(arguments);
    },

    
    setComponentCls: function() {
        var me = this,
            cls = me.getComponentCls();

        if (!Ext.isEmpty(me.oldCls)) {
            me.removeClsWithUI(me.oldCls);
            me.removeClsWithUI(me.pressedCls);
        }

        me.oldCls = cls;
        me.addClsWithUI(cls);
    },

    getComponentCls: function() {
        var me = this,
            cls;

        
        if (me.iconCls || me.icon || me.glyph) {
            cls = [me.text ? 'icon-text-' + me.iconAlign : 'icon'];
        } else if (me.text) {
            cls = ['noicon'];
        } else {
            cls = [];
        }

        if (me.pressed) {
            cls[cls.length] = me.pressedCls;
        }
        return cls;
    },

    beforeRender: function () {
        var me = this,
            autoEl = me.autoEl,
            href = me.getHref(),
            hrefTarget = me.hrefTarget;

        if (!me.disabled) {
            autoEl.tabIndex = me.tabIndex;
        }

        if (href) {
            autoEl.href = href;
            if (hrefTarget) {
                autoEl.target = hrefTarget;
            }
        }

        me.callParent();

        
        me.oldCls = me.getComponentCls();
        me.addClsWithUI(me.oldCls);

        
        Ext.applyIf(me.renderData, me.getTemplateArgs());
    },

    
    onRender: function() {
        var me = this,
            addOnclick,
            btn,
            btnListeners;

        me.doc = Ext.getDoc();
        me.callParent(arguments);

        
        btn = me.el;

        if (me.tooltip) {
            me.setTooltip(me.tooltip, true);
        }

        
        if (me.handleMouseEvents) {
            btnListeners = {
                scope: me,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousedown: me.onMouseDown
            };
            if (me.split) {
                btnListeners.mousemove = me.onMouseMove;
            }
        } else {
            btnListeners = {
                scope: me
            };
        }

        
        if (me.menu) {
            me.mon(me.menu, {
                scope: me,
                show: me.onMenuShow,
                hide: me.onMenuHide
            });

            me.keyMap = new Ext.util.KeyMap({
                target: me.el,
                key: Ext.EventObject.DOWN,
                handler: me.onDownKey,
                scope: me
            });
        }

        
        if (me.repeat) {
            me.mon(new Ext.util.ClickRepeater(btn, Ext.isObject(me.repeat) ? me.repeat: {}), 'click', me.onRepeatClick, me);
        } else {

            
            if (btnListeners[me.clickEvent]) {
                addOnclick = true;
            } else {
                btnListeners[me.clickEvent] = me.onClick;
            }
        }

        
        me.mon(btn, btnListeners);

        
        if (addOnclick) {
            me.mon(btn, me.clickEvent, me.onClick, me);
        }

        Ext.button.Manager.register(me);
    },

    
    getTemplateArgs: function() {
        var me = this,
            glyph = me.glyph,
            glyphFontFamily = Ext._glyphFontFamily,
            glyphParts;

        if (typeof glyph === 'string') {
            glyphParts = glyph.split('@');
            glyph = glyphParts[0];
            glyphFontFamily = glyphParts[1];
        }

        return {
            innerCls : me.getInnerCls(),
            splitCls : me.getSplitCls(),
            iconUrl  : me.icon,
            iconCls  : me.iconCls,
            glyph: glyph,
            glyphCls: glyph ? me.glyphCls : '', 
            glyphFontFamily: glyphFontFamily,
            text     : me.text || '&#160;'
        };
    },

    
    setHref: function(href) {
        this.href = href;
        this.el.dom.href = this.getHref();
    },

    
    getHref: function() {
        var me = this,
            href = me.href;

        return href ? Ext.urlAppend(href, Ext.Object.toQueryString(Ext.apply({}, me.params, me.baseParams))) : false;
    },

    
    setParams: function(params) {
        this.params = params;
        this.el.dom.href = this.getHref();
    },

    getSplitCls: function() {
        var me = this;
        return me.split ? (me.baseCls + '-' + me.arrowCls) + ' ' + (me.baseCls + '-' + me.arrowCls + '-' + me.arrowAlign) : '';
    },

    getInnerCls: function() {
        return this.textAlign ? this.baseCls + '-inner-' + this.textAlign : '';
    },

    
    setIcon: function(icon) {
        icon = icon || '';
        var me = this,
            btnIconEl = me.btnIconEl,
            oldIcon = me.icon || '';

        me.icon = icon;
        if (icon != oldIcon) {
            if (btnIconEl) {
                btnIconEl.setStyle('background-image', icon ? 'url(' + icon + ')': '');
                me.setComponentCls();
                if (me.didIconStateChange(oldIcon, icon)) {
                    me.updateLayout();
                }
            }
            me.fireEvent('iconchange', me, oldIcon, icon);
        }
        return me;
    },

    
    setIconCls: function(cls) {
        cls = cls || '';
        var me = this,
            btnIconEl = me.btnIconEl,
            oldCls = me.iconCls || '';

        me.iconCls = cls;
        if (oldCls != cls) {
            if (btnIconEl) {
                
                btnIconEl.removeCls(oldCls);
                btnIconEl.addCls(cls);
                me.setComponentCls();
                if (me.didIconStateChange(oldCls, cls)) {
                    me.updateLayout();
                }
            }
            me.fireEvent('iconchange', me, oldCls, cls);
        }
        return me;
    },

    
    setGlyph: function(glyph) {
        glyph = glyph || 0;
        var me = this,
            btnIconEl = me.btnIconEl,
            oldGlyph = me.glyph,
            fontFamily, glyphParts;

        me.glyph = glyph;

        if (btnIconEl) {
            if (typeof glyph === 'string') {
                glyphParts = glyph.split('@');
                glyph = glyphParts[0];
                fontFamily = glyphParts[1] || Ext._glyphFontFamily;
            }

            if (!glyph) {
                btnIconEl.dom.innerHTML = '';
            } else if (oldGlyph != glyph) {
                btnIconEl.dom.innerHTML = '&#' + glyph + ';';
            }

            if (fontFamily) {
                btnIconEl.setStyle('font-family', fontFamily);
            }
        }

        me.fireEvent('glyphchange', me, me.glyph, oldGlyph);

        return me;
    },

    
    setTooltip: function(tooltip, initial) {
        var me = this;

        if (me.rendered) {
            if (!initial || !tooltip) {
                me.clearTip();
            }
            if (tooltip) {
                if (Ext.quickTipsActive && Ext.isObject(tooltip)) {
                    Ext.tip.QuickTipManager.register(Ext.apply({
                        target: me.el.id
                    },
                    tooltip));
                    me.tooltip = tooltip;
                } else {
                    me.el.dom.setAttribute(me.getTipAttr(), tooltip);
                }
            }
        } else {
            me.tooltip = tooltip;
        }
        return me;
    },

    
    setTextAlign: function(align) {
        var me = this,
            btnEl = me.btnEl;

        if (btnEl) {
            btnEl.removeCls(me.baseCls + '-inner-' + me.textAlign);
            btnEl.addCls(me.baseCls + '-inner-' + align);
        }
        me.textAlign = align;
        return me;
    },

    getTipAttr: function(){
        return this.tooltipType == 'qtip' ? 'data-qtip' : 'title';
    },

    
    getRefItems: function(deep){
        var menu = this.menu,
            items;

        if (menu) {
            items = menu.getRefItems(deep);
            items.unshift(menu);
        }
        return items || [];
    },

    
    clearTip: function() {
        var me = this,
            el = me.el;

        if (Ext.quickTipsActive && Ext.isObject(me.tooltip)) {
            Ext.tip.QuickTipManager.unregister(el);
        } else {
            el.dom.removeAttribute(me.getTipAttr());
        }
    },

    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.clearTip();
        }
        if (me.menu && me.destroyMenu !== false) {
            Ext.destroy(me.menu);
        }
        Ext.destroy(me.btnInnerEl, me.repeater);
        me.callParent();
    },

    
    onDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.doc.un('mouseover', me.monitorMouseOver, me);
            delete me.doc;

            Ext.destroy(me.keyMap);
            delete me.keyMap;
        }
        Ext.button.Manager.unregister(me);
        me.callParent();
    },

    
    setHandler: function(handler, scope) {
        this.handler = handler;
        this.scope = scope;
        return this;
    },

    
    setText: function(text) {
        text = text || '';
        var me = this,
            oldText = me.text || '';

        if (text != oldText) {
            me.text = text;
            if (me.rendered) {
                me.btnInnerEl.update(text || '&#160;');
                me.setComponentCls();
                if (Ext.isStrict && Ext.isIE8) {
                    
                    me.el.repaint();
                }
                me.updateLayout();
            }
            me.fireEvent('textchange', me, oldText, text);
        }
        return me;
    },

    
    didIconStateChange: function(old, current) {
        var currentEmpty = Ext.isEmpty(current);
        return Ext.isEmpty(old) ? !currentEmpty : currentEmpty;
    },

    
    getText: function() {
        return this.text;
    },

    
    toggle: function(state, suppressEvent) {
        var me = this;
        state = state === undefined ? !me.pressed: !!state;
        if (state !== me.pressed) {
            if (me.rendered) {
                me[state ? 'addClsWithUI': 'removeClsWithUI'](me.pressedCls);
            }
            me.pressed = state;
            if (!suppressEvent) {
                me.fireEvent('toggle', me, state);
                Ext.callback(me.toggleHandler, me.scope || me, [me, state]);
            }
        }
        return me;
    },

    maybeShowMenu: function(){
        var me = this;
        if (me.menu && !me.hasVisibleMenu() && !me.ignoreNextClick) {
            me.showMenu(true);
        }
    },

    
    showMenu: function( fromEvent) {
        var me = this,
            menu = me.menu;

        if (me.rendered) {
            if (me.tooltip && Ext.quickTipsActive && me.getTipAttr() != 'title') {
                Ext.tip.QuickTipManager.getQuickTip().cancelShow(me.el);
            }
            if (menu.isVisible()) {
                menu.hide();
            }

            if (!fromEvent || me.showEmptyMenu || menu.items.getCount() > 0) {
                menu.showBy(me.el, me.menuAlign);
            }
        }
        return me;
    },

    
    hideMenu: function() {
        if (this.hasVisibleMenu()) {
            this.menu.hide();
        }
        return this;
    },

    
    hasVisibleMenu: function() {
        var menu = this.menu;
        return menu && menu.rendered && menu.isVisible();
    },

    
    onRepeatClick: function(repeat, e) {
        this.onClick(e);
    },

    
    onClick: function(e) {
        var me = this;
        if (me.preventDefault || (me.disabled && me.getHref()) && e) {
            e.preventDefault();
        }

        
        
        if (e.type !== 'keydown' && e.button !== 0) {
            return;
        }
        if (!me.disabled) {
            me.doToggle();
            me.maybeShowMenu();
            me.fireHandler(e);
        }
    },

    fireHandler: function(e) {
        var me = this,
            handler = me.handler;

        if (me.fireEvent('click', me, e) !== false) {
            if (handler) {
                handler.call(me.scope || me, me, e);
            }
        }
    },

    doToggle: function() {
        var me = this;    
        if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) {
            me.toggle();
        }
    },

    
    onMouseOver: function(e) {
        var me = this;
        if (!me.disabled && !e.within(me.el, true, true)) {
            me.onMouseEnter(e);
        }
    },

    
    onMouseOut: function(e) {
        var me = this;
        if (!e.within(me.el, true, true)) {
            if (me.overMenuTrigger) {
                me.onMenuTriggerOut(e);
            }
            me.onMouseLeave(e);
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            el = me.el,
            over = me.overMenuTrigger,
            overPosition, triggerRegion;

        if (me.split) {
            overPosition = (me.arrowAlign === 'right') ?
                e.getX() - me.getX() : e.getY() - el.getY();
            triggerRegion = me.getTriggerRegion();

            if (overPosition > triggerRegion.begin && overPosition < triggerRegion.end) {
                if (!over) {
                    me.onMenuTriggerOver(e);
                }
            } else {
                if (over) {
                    me.onMenuTriggerOut(e);
                }
            }
        }
    },

    
    getTriggerRegion: function() {
        var me = this,
            region = me._triggerRegion,
            triggerSize = me.getTriggerSize(),
            btnSize = me.arrowAlign === 'right' ? me.getWidth() : me.getHeight();

        region.begin = btnSize - triggerSize;
        region.end = btnSize;
        return region;
    },

    
    getTriggerSize: function() {
        var me = this,
            size = me.triggerSize,
            side, sideFirstLetter;

        if (size == null) { 
            side = me.arrowAlign;
            sideFirstLetter = side.charAt(0);
            size = me.triggerSize = me.el.getFrameWidth(sideFirstLetter) + me.getBtnWrapFrameWidth(sideFirstLetter)
            if (me.frameSize) {
                size = me.triggerSize += me.frameSize[side];
            }
        }
        return size;
    },

    
    getBtnWrapFrameWidth: function(side) {
        return this.btnWrap.getFrameWidth(side);
    },

    addOverCls: function() {
        if (!this.disabled) {
            this.addClsWithUI(this.overCls);
        }
    },
    removeOverCls: function() {
        this.removeClsWithUI(this.overCls);
    },

    
    onMouseEnter: function(e) {
        
        this.fireEvent('mouseover', this, e);
    },

    
    onMouseLeave: function(e) {
        
        this.fireEvent('mouseout', this, e);
    },

    
    onMenuTriggerOver: function(e) {
        var me = this,
            arrowTip = me.arrowTooltip;

        me.overMenuTrigger = true;
        
        
        if (me.split && arrowTip) {
            me.btnWrap.dom.setAttribute(me.getTipAttr(), arrowTip);
        }
        me.fireEvent('menutriggerover', me, me.menu, e);
    },

    
    onMenuTriggerOut: function(e) {
        var me = this;
        delete me.overMenuTrigger;
        
        if (me.split && me.arrowTooltip) {
            me.btnWrap.dom.setAttribute(me.getTipAttr(), '');
        }
        me.fireEvent('menutriggerout', me, me.menu, e);
    },

    
    enable: function(silent) {
        var me = this;

        me.callParent(arguments);

        me.removeClsWithUI('disabled');
        if (me.rendered) {
            me.el.dom.setAttribute('tabIndex', me.tabIndex);
        }

        return me;
    },

    
    disable: function(silent) {
        var me = this;

        me.callParent(arguments);

        me.addClsWithUI('disabled');
        me.removeClsWithUI(me.overCls);
        if (me.rendered) {
            me.el.dom.removeAttribute('tabIndex');
        }

        
        
        if (me.btnInnerEl && Ext.isIE7m) {
            me.btnInnerEl.repaint();
        }

        return me;
    },

    
    setScale: function(scale) {
        var me = this,
            ui = me.ui.replace('-' + me.scale, '');

        
        if (!Ext.Array.contains(me.allowedScales, scale)) {
            throw('#setScale: scale must be an allowed scale (' + me.allowedScales.join(', ') + ')');
        }

        me.scale = scale;
        me.setUI(ui);
    },

    
    setUI: function(ui) {
        var me = this;

        
        if (me.scale && !ui.match(me.scale)) {
            ui = ui + '-' + me.scale;
        }

        me.callParent([ui]);

        
        
    },


    
    onMouseDown: function(e) {
        var me = this;

        if (Ext.isIE) {
            
            
            me.getFocusEl().focus();
        }

        if (!me.disabled && e.button === 0) {
            Ext.button.Manager.onButtonMousedown(me, e);
            me.addClsWithUI(me.pressedCls);
        }
    },
    
    onMouseUp: function(e) {
        var me = this;
        if (e.button === 0) {
            if (!me.pressed) {
                me.removeClsWithUI(me.pressedCls);
            }
        }
    },
    
    onMenuShow: function(e) {
        var me = this;
        me.ignoreNextClick = 0;
        me.addClsWithUI(me.menuActiveCls);
        me.fireEvent('menushow', me, me.menu);
    },

    
    onMenuHide: function(e) {
        var me = this;
        me.removeClsWithUI(me.menuActiveCls);
        me.ignoreNextClick = Ext.defer(me.restoreClick, 250, me);
        me.fireEvent('menuhide', me, me.menu);
        me.focus();
    },

    
    restoreClick: function() {
        this.ignoreNextClick = 0;
    },

    
    onDownKey: function(k, e) {
        var me = this;

        if (me.menu && !me.disabled) {
            me.showMenu();
            e.stopEvent();
            return false;
        }
    }
});


Ext.define('Ext.layout.container.boxOverflow.Menu', {

    

    extend:  Ext.layout.container.boxOverflow.None ,
                                                             
    alternateClassName: 'Ext.layout.boxOverflow.Menu',
    
    

    

    
    noItemsMenuText : '<div class="' + Ext.baseCSSPrefix + 'toolbar-no-items">(None)</div>',

    constructor: function(layout) {
        var me = this;

        me.callParent(arguments);

        me.triggerButtonCls = me.triggerButtonCls || Ext.baseCSSPrefix + 'box-menu-after';
        
        me.menuItems = [];
    },

    beginLayout: function (ownerContext) {
        this.callParent(arguments);

        
        
        this.clearOverflow(ownerContext);
    },

    beginLayoutCycle: function (ownerContext, firstCycle) {
        this.callParent(arguments);

        if (!firstCycle) {
            
            
            this.clearOverflow(ownerContext);

            this.layout.cacheChildItems(ownerContext);
        }
    },

    onRemove: function(comp){
        Ext.Array.remove(this.menuItems, comp);
    },

    
    getSuffixConfig: function() {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            oid = owner.id;

        
        me.menu = new Ext.menu.Menu({
            listeners: {
                scope: me,
                beforeshow: me.beforeMenuShow
            }
        });

        
        me.menuTrigger = new Ext.button.Button({
            id: oid + '-menu-trigger',
            cls: Ext.layout.container.Box.prototype.innerCls + ' ' + me.triggerButtonCls + ' ' + Ext.baseCSSPrefix + 'toolbar-item',
            plain: owner.usePlainButtons,
            ownerCt: owner, 
            ownerLayout: layout,
            iconCls: Ext.baseCSSPrefix + me.getOwnerType(owner) + '-more-icon',
            ui: owner instanceof Ext.toolbar.Toolbar ? 'default-toolbar' : 'default',
            menu: me.menu,
            
            showEmptyMenu: true,
            getSplitCls: function() { return '';}
        });

        return me.menuTrigger.getRenderTree();
    },
    
    getOverflowCls: function() {
        return Ext.baseCSSPrefix + this.layout.direction + '-box-overflow-body';
    },

    handleOverflow: function(ownerContext) {
        var me = this,
            layout = me.layout,
            names = layout.names,
            plan = ownerContext.state.boxPlan,
            posArgs = [null, null];

        me.showTrigger(ownerContext);

        
        
        if (me.layout.direction !== 'vertical') {
            posArgs[names.heightIndex] = (plan.maxSize - me.menuTrigger[names.getHeight]()) / 2;
            me.menuTrigger.setPosition.apply(me.menuTrigger, posArgs);
        }

        return {
            reservedSpace: me.triggerTotalWidth
        };
    },

    
    captureChildElements: function() {
        var me = this,
            menuTrigger = me.menuTrigger,
            names = me.layout.names;

        
        if (menuTrigger.rendering) {
            menuTrigger.finishRender();
            me.triggerTotalWidth = menuTrigger[names.getWidth]() + menuTrigger.el.getMargin(names.parallelMargins);
        }
    },

    _asLayoutRoot: { isRoot: true },

    
    clearOverflow: function(ownerContext) {
        var me = this,
            items = me.menuItems,
            item,
            i = 0,
            length = items.length,
            owner = me.layout.owner,
            asLayoutRoot = me._asLayoutRoot;

        owner.suspendLayouts();
        me.captureChildElements();
        me.hideTrigger();
        owner.resumeLayouts();

        for (; i < length; i++) {
            item = items[i];

            
            
            item.suspendLayouts();
            item.show();
            item.resumeLayouts(asLayoutRoot);
        }

        items.length = 0;
    },

    
    showTrigger: function(ownerContext) {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            names = layout.names,
            startProp = names.x,
            sizeProp = names.width,
            plan = ownerContext.state.boxPlan,
            available = plan.targetSize[sizeProp],
            childItems = ownerContext.childItems,
            len = childItems.length,
            menuTrigger = me.menuTrigger,
            childContext,
            comp, i, props;

        
        
        menuTrigger.suspendLayouts();
        menuTrigger.show();
        menuTrigger.resumeLayouts(me._asLayoutRoot);

        available -= me.triggerTotalWidth;

        owner.suspendLayouts();

        
        
        me.menuItems.length = 0;
        for (i = 0; i < len; i++) {
            childContext = childItems[i];
            props = childContext.props;
            if (props[startProp] + props[sizeProp] > available) {
                comp = childContext.target;
                me.menuItems.push(comp);
                comp.hide();
            }
        }

        owner.resumeLayouts();
    },

    
    hideTrigger: function() {
        var menuTrigger = this.menuTrigger;
        if (menuTrigger) {
            menuTrigger.hide();
        }
    },

    
    beforeMenuShow: function(menu) {
        var me = this,
            items = me.menuItems,
            i = 0,
            len   = items.length,
            item,
            prev,
            needsSep = function(group, prev){
                return group.isXType('buttongroup') && !(prev instanceof Ext.toolbar.Separator);
            };

        menu.suspendLayouts();
        me.clearMenu();
        menu.removeAll();

        for (; i < len; i++) {
            item = items[i];

            
            if (!i && (item instanceof Ext.toolbar.Separator)) {
                continue;
            }
            if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
                menu.add('-');
            }

            me.addComponentToMenu(menu, item);
            prev = item;
        }

        
        if (menu.items.length < 1) {
            menu.add(me.noItemsMenuText);
        }
        menu.resumeLayouts();
    },
    
    
    createMenuConfig : function(component, hideOnClick) {
        var me = this,
            config = Ext.apply({}, component.initialConfig),
            group  = component.toggleGroup;

        Ext.copyTo(config, component, [
            'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu', 'tabIndex'
        ]);

        Ext.apply(config, {
            text       : component.overflowText || component.text,
            hideOnClick: hideOnClick,
            destroyMenu: false,
            listeners  : {}
        });

        
        if (component.isFormField) {
            config.value = component.getValue();

            
            
            
            
            
            config.listeners.change = function(c, newVal, oldVal) {                            
                component.setValue(newVal);
            }
        }

        
        else if (group || component.enableToggle) {
            Ext.apply(config, {
                hideOnClick: false,
                group  : group,
                checked: component.pressed,
                handler: function(item, e) {
                    component.onClick(e);
                }
            });
        }

        
        if (component.isButton && !component.changeListenersAdded) {
            component.on({
                textchange: me.onButtonAttrChange,
                iconchange: me.onButtonAttrChange,
                toggle:     me.onButtonToggle
            });
            component.changeListenersAdded = true;
        }

        
        
        
        delete config.margin;
        delete config.ownerCt;
        delete config.xtype;
        delete config.id;
        delete config.itemId;
        return config;
    },

    onButtonAttrChange: function(btn) {
        var clone = btn.overflowClone;
        clone.suspendLayouts();
        clone.setText(btn.text);
        clone.setIcon(btn.icon);
        clone.setIconCls(btn.iconCls);
        clone.resumeLayouts(true);
    },

    onButtonToggle: function(btn, state) {
        
        if (btn.overflowClone.checked !== state) {
            btn.overflowClone.setChecked(state);
        }
    },

    
    addComponentToMenu : function(menu, component) {
        var me = this,
        i, items, iLen;

        if (component instanceof Ext.toolbar.Separator) {
            menu.add('-');
        } else if (component.isComponent) {
            if (component.isXType('splitbutton')) {
                component.overflowClone = menu.add(me.createMenuConfig(component, true));

            } else if (component.isXType('button')) {
                component.overflowClone = menu.add(me.createMenuConfig(component, !component.menu));

            } else if (component.isXType('buttongroup')) {
                items = component.items.items;
                iLen  = items.length;

                for (i = 0; i < iLen; i++) {
                    me.addComponentToMenu(menu, items[i]);
                }
            } else {
                component.overflowClone = menu.add(Ext.create(Ext.getClassName(component), me.createMenuConfig(component)));
            }
        }
    },

    
    clearMenu : function() {
        var menu = this.menu,
            items, i, iLen, item;
        
        if (menu && menu.items) {
            items = menu.items.items;
            iLen  = items.length;
            
            for (i = 0; i < iLen; i++) {
                item = items[i];
                if (item.setMenu) {
                    item.setMenu(null);
                }
            }
        }
    },

    
    destroy: function() {
        var trigger = this.menuTrigger;
            
        if (trigger && !this.layout.owner.items.contains(trigger)) {
            
            
            delete trigger.ownerCt;
        }
        Ext.destroy(this.menu, trigger);
    }
});


Ext.define('Ext.layout.container.boxOverflow.Scroller', {

    

    extend:  Ext.layout.container.boxOverflow.None ,
                                                        
    alternateClassName: 'Ext.layout.boxOverflow.Scroller',
    mixins: {
        observable:  Ext.util.Observable 
    },
    
    

    
    animateScroll: false,

    
    scrollIncrement: 20,

    
    wheelIncrement: 10,

    
    scrollRepeatInterval: 60,

    
    scrollDuration: 400,

    

    

    
    scrollerCls: Ext.baseCSSPrefix + 'box-scroller',

    

    

    constructor: function(layout, config) {
        var me = this;

        me.layout = layout;
        Ext.apply(me, config || {});

        
        me.mixins.observable.constructor.call(me);

        me.addEvents(
            
            'scroll'
        );
        me.scrollPosition = 0;
        me.scrollSize = 0;
    },

    getPrefixConfig: function() {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            cls;

        me.initCSSClasses();
        cls = Ext.layout.container.Box.prototype.innerCls + ' ' + me.beforeCtCls;
        if (owner.plain) {
            
            cls += ' ' + me.scrollerCls + '-plain';
        }
        return {
            cls: cls,
            cn : {
                id : owner.id + layout.names.beforeScrollerSuffix,
                cls: me.scrollerCls + ' ' + me.beforeScrollerCls,
                style: 'display:none'
            }
        };
    },

    getSuffixConfig: function() {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            cls = Ext.layout.container.Box.prototype.innerCls + ' ' + me.afterCtCls;

        if (owner.plain) {
            
            cls += ' ' + me.scrollerCls + '-plain';
        }
        return {
            cls: cls,
            cn : {
                id : owner.id + layout.names.afterScrollerSuffix,
                cls: me.scrollerCls + ' ' + me.afterScrollerCls,
                style: 'display:none'
            }
        };
    },

    getOverflowCls: function() {
        return Ext.baseCSSPrefix + this.layout.direction + '-box-overflow-body';
    },

    initCSSClasses: function() {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            layout = me.layout,
            names = layout.names,
            beforeXName = names.beforeX,
            afterXName = names.afterX,
            type = me.getOwnerType(layout.owner);

        me.beforeCtCls = me.beforeCtCls || prefix + 'box-scroller-' + beforeXName;
        me.afterCtCls  = me.afterCtCls  || prefix + 'box-scroller-' + afterXName;
        
        me.beforeScrollerCls = me.beforeScrollerCls || prefix + type + '-scroll-' + beforeXName;
        me.afterScrollerCls  = me.afterScrollerCls  || prefix + type + '-scroll-' + afterXName;
    },

    beginLayout: function (ownerContext) {
        var layout = this.layout;

        ownerContext.innerCtScrollPos = this.getScrollPosition();

        this.callParent(arguments);
    },

    completeLayout: function(ownerContext) {
        var me = this,
            plan = ownerContext.state.boxPlan,
            names = me.layout.names,
            last;

        
        if (plan && plan.tooNarrow) {
            last = ownerContext.childItems[ownerContext.childItems.length - 1];

            
            me.scrollSize = last.props[names.x] + last.props[names.width];
            me.updateScrollButtons();
        }
        this.callParent(arguments);
    },

    finishedLayout: function(ownerContext) {
        var me = this,
            layout = me.layout,
            scrollPos = Math.min(me.getMaxScrollPosition(), ownerContext.innerCtScrollPos);

        layout.innerCt[layout.names.setScrollLeft](scrollPos);
    },

    handleOverflow: function(ownerContext) {
        var me = this,
            methodName = me.layout.names.getWidth;

        me.showScrollers();
        return {
            reservedSpace: me.beforeCt[methodName]() + me.afterCt[methodName]()
        };
    },

    
    captureChildElements: function() {
        var me = this,
            el = me.layout.owner.el,
            before, after, hoverCls, pressedSuffix, pressedCls, hoverSuffix;

        
        if (!me.beforeCt) {
            hoverSuffix = '-hover';
            pressedSuffix = '-pressed';
            hoverCls = me.scrollerCls + hoverSuffix;
            pressedCls = me.scrollerCls + pressedSuffix;
            before = me.beforeScroller = el.getById(me.layout.owner.id + '-before-scroller');
            after = me.afterScroller = el.getById(me.layout.owner.id + '-after-scroller');
            me.beforeCt = before.up('');
            me.afterCt = after.up('');
            me.createWheelListener();

            before.addClsOnOver(hoverCls);
            before.addClsOnOver(me.beforeScrollerCls + hoverSuffix);
            before.addClsOnClick(pressedCls);
            before.addClsOnClick(me.beforeScrollerCls + pressedSuffix);
            after.addClsOnOver(hoverCls);
            after.addClsOnOver(me.afterScrollerCls + hoverSuffix);
            after.addClsOnClick(pressedCls);
            after.addClsOnClick(me.afterScrollerCls + pressedSuffix);

            before.setVisibilityMode(Ext.Element.DISPLAY);
            after.setVisibilityMode(Ext.Element.DISPLAY);

            me.beforeRepeater = new Ext.util.ClickRepeater(before, {
                interval: me.scrollRepeatInterval,
                handler : me.scrollLeft,
                scope   : me
            });

            me.afterRepeater = new Ext.util.ClickRepeater(after, {
                interval: me.scrollRepeatInterval,
                handler : me.scrollRight,
                scope   : me
            });
        }
    },

    
    createWheelListener: function() {
        var me = this;
        me.layout.innerCt.on({
            mousewheel: function(e) {
                me.scrollBy(me.getWheelDelta(e) * me.wheelIncrement * -1, false);
            },
            stopEvent: true
        });
    },

    getWheelDelta: function (e) {
        return e.getWheelDelta();
    },

    
    clearOverflow: function () {
        this.hideScrollers();
    },

    
    showScrollers: function() {
        var me = this;

        me.captureChildElements();
        me.beforeScroller.show();
        me.afterScroller.show();
        me.layout.owner.addClsWithUI(me.layout.direction === 'vertical' ? 'vertical-scroller' : 'scroller');
        
    },

    
    hideScrollers: function() {
        var me = this;

        if (me.beforeScroller !== undefined) {
            me.beforeScroller.hide();
            me.afterScroller.hide();
            me.layout.owner.removeClsWithUI(me.layout.direction === 'vertical' ? 'vertical-scroller' : 'scroller');
            
        }
    },

    
    destroy: function() {
        var me = this;

        Ext.destroy(me.beforeRepeater, me.afterRepeater, me.beforeScroller, me.afterScroller, me.beforeCt, me.afterCt);
    },

    
    scrollBy: function(delta, animate) {
        this.scrollTo(this.getScrollPosition() + delta, animate);
    },

    
    getScrollAnim: function() {
        return {
            duration: this.scrollDuration, 
            callback: this.updateScrollButtons, 
            scope   : this
        };
    },

    
    updateScrollButtons: function() {
        var me = this,
            beforeMeth,
            afterMeth,
            beforeCls,
            afterCls,
            disabledCls,
            suffix = '-disabled';
            
        if (me.beforeScroller == null || me.afterScroller == null) {
            return;
        }

        beforeMeth = me.atExtremeBefore()  ? 'addCls' : 'removeCls';
        afterMeth  = me.atExtremeAfter() ? 'addCls' : 'removeCls';
        disabledCls = me.scrollerCls + suffix;
        beforeCls = [disabledCls, me.beforeScrollerCls + suffix];
        afterCls = [disabledCls, me.afterScrollerCls  + suffix];

        me.beforeScroller[beforeMeth](beforeCls);
        me.afterScroller[afterMeth](afterCls);
        me.scrolling = false;
    },

    
    scrollLeft: function() {
        this.scrollBy(-this.scrollIncrement, false);
    },

    
    scrollRight: function() {
        this.scrollBy(this.scrollIncrement, false);
    },

    
    getScrollPosition: function(){
        var me = this,
            layout = me.layout,
            result;

        
        
        if (isNaN(me.scrollPosition)) {
            result = layout.innerCt[layout.names.getScrollLeft]();
        } else {
            result = me.scrollPosition;
        }
        return result;
    },

    
    getMaxScrollPosition: function() {
        var me = this,
            layout = me.layout,
            maxScrollPos = me.scrollSize - layout.innerCt[layout.names.getWidth]();

        return (maxScrollPos < 0) ? 0 : maxScrollPos;
    },

    
    atExtremeBefore: function() {
        return !this.getScrollPosition();
    },

    
    atExtremeAfter: function() {
        return this.getScrollPosition() >= this.getMaxScrollPosition();
    },

    
    scrollTo: function(position, animate) {
        var me = this,
            layout = me.layout,
            names = layout.names,
            oldPosition = me.getScrollPosition(),
            newPosition = Ext.Number.constrain(position, 0, me.getMaxScrollPosition());

        if (newPosition != oldPosition && !me.scrolling) {
            me.scrollPosition = NaN;
            if (animate === undefined) {
                animate = me.animateScroll;
            }

            layout.innerCt[names.scrollTo](names.beforeScrollX, newPosition, animate ? me.getScrollAnim() : false);
            if (animate) {
                me.scrolling = true;
            } else {
                me.updateScrollButtons();
            }
            me.fireEvent('scroll', me, newPosition, animate ? me.getScrollAnim() : false);
        }
    },

    
    scrollToItem: function(item, animate) {
        var me = this,
            layout = me.layout,
            owner = layout.owner,
            names = layout.names,
            visibility,
            box,
            newPos;

        item = me.getItem(item);
        if (item !== undefined) {
            if (item == owner.items.first()) {
                newPos = 0
            } else if (item === owner.items.last()) {
                newPos = me.getMaxScrollPosition();
            } else {
                visibility = me.getItemVisibility(item);
                if (!visibility.fullyVisible) {
                    box = item.getBox(false, true);
                    newPos = box[names.x];
                    if (visibility.hiddenEnd) {
                        newPos -= (me.layout.innerCt[names.getWidth]() - box[names.width]);
                    }
                }
            }
            if (newPos !== undefined) {
                me.scrollTo(newPos, animate);
            }
        }
    },

    
    getItemVisibility: function(item) {
        var me          = this,
            box         = me.getItem(item).getBox(true, true),
            layout      = me.layout,
            names       = layout.names,
            itemStart   = box[names.x],
            itemEnd     = itemStart + box[names.width],
            scrollStart = me.getScrollPosition(),
            scrollEnd   = scrollStart + layout.innerCt[names.getWidth]();

        return {
            hiddenStart : itemStart < scrollStart,
            hiddenEnd   : itemEnd > scrollEnd,
            fullyVisible: itemStart > scrollStart && itemEnd < scrollEnd
        };
    }
});



Ext.define('Ext.util.Offset', {

    

    statics: {
        fromObject: function(obj) {
            return new this(obj.x, obj.y);
        }
    },

    

    constructor: function(x, y) {
        this.x = (x != null && !isNaN(x)) ? x : 0;
        this.y = (y != null && !isNaN(y)) ? y : 0;

        return this;
    },

    copy: function() {
        return new Ext.util.Offset(this.x, this.y);
    },

    copyFrom: function(p) {
        this.x = p.x;
        this.y = p.y;
    },

    toString: function() {
        return "Offset[" + this.x + "," + this.y + "]";
    },

    equals: function(offset) {

        return (this.x == offset.x && this.y == offset.y);
    },

    round: function(to) {
        if (!isNaN(to)) {
            var factor = Math.pow(10, to);
            this.x = Math.round(this.x * factor) / factor;
            this.y = Math.round(this.y * factor) / factor;
        } else {
            this.x = Math.round(this.x);
            this.y = Math.round(this.y);
        }
    },

    isZero: function() {
        return this.x == 0 && this.y == 0;
    }
});


Ext.define('Ext.util.Region', {

    

                                  

    statics: {
        
        getRegion: function(el) {
            return Ext.fly(el).getRegion();
        },

        
        from: function(o) {
            return new this(o.top, o.right, o.bottom, o.left);
        }
    },

    

    
    constructor : function(t, r, b, l) {
        var me = this;
        me.y = me.top = me[1] = t;
        me.right = r;
        me.bottom = b;
        me.x = me.left = me[0] = l;
    },

    
    contains : function(region) {
        var me = this;
        return (region.x >= me.x &&
                region.right <= me.right &&
                region.y >= me.y &&
                region.bottom <= me.bottom);

    },

    
    intersect : function(region) {
        var me = this,
            t = Math.max(me.y, region.y),
            r = Math.min(me.right, region.right),
            b = Math.min(me.bottom, region.bottom),
            l = Math.max(me.x, region.x);

        if (b > t && r > l) {
            return new this.self(t, r, b, l);
        }
        else {
            return false;
        }
    },

    
    union : function(region) {
        var me = this,
            t = Math.min(me.y, region.y),
            r = Math.max(me.right, region.right),
            b = Math.max(me.bottom, region.bottom),
            l = Math.min(me.x, region.x);

        return new this.self(t, r, b, l);
    },

    
    constrainTo : function(r) {
        var me = this,
            constrain = Ext.Number.constrain;
        me.top = me.y = constrain(me.top, r.y, r.bottom);
        me.bottom = constrain(me.bottom, r.y, r.bottom);
        me.left = me.x = constrain(me.left, r.x, r.right);
        me.right = constrain(me.right, r.x, r.right);
        return me;
    },

    
    adjust : function(t, r, b, l) {
        var me = this;
        me.top = me.y += t;
        me.left = me.x += l;
        me.right += r;
        me.bottom += b;
        return me;
    },

    
    getOutOfBoundOffset: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.getOutOfBoundOffsetX(p);
            } else {
                return this.getOutOfBoundOffsetY(p);
            }
        } else {
            p = axis;
            var d = new Ext.util.Offset();
            d.x = this.getOutOfBoundOffsetX(p.x);
            d.y = this.getOutOfBoundOffsetY(p.y);
            return d;
        }

    },

    
    getOutOfBoundOffsetX: function(p) {
        if (p <= this.x) {
            return this.x - p;
        } else if (p >= this.right) {
            return this.right - p;
        }

        return 0;
    },

    
    getOutOfBoundOffsetY: function(p) {
        if (p <= this.y) {
            return this.y - p;
        } else if (p >= this.bottom) {
            return this.bottom - p;
        }

        return 0;
    },

    
    isOutOfBound: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.isOutOfBoundX(p);
            } else {
                return this.isOutOfBoundY(p);
            }
        } else {
            p = axis;
            return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y));
        }
    },

    
    isOutOfBoundX: function(p) {
        return (p < this.x || p > this.right);
    },

    
    isOutOfBoundY: function(p) {
        return (p < this.y || p > this.bottom);
    },

    
    restrict: function(axis, p, factor) {
        if (Ext.isObject(axis)) {
            var newP;

            factor = p;
            p = axis;

            if (p.copy) {
                newP = p.copy();
            }
            else {
                newP = {
                    x: p.x,
                    y: p.y
                };
            }

            newP.x = this.restrictX(p.x, factor);
            newP.y = this.restrictY(p.y, factor);
            return newP;
        } else {
            if (axis == 'x') {
                return this.restrictX(p, factor);
            } else {
                return this.restrictY(p, factor);
            }
        }
    },

    
    restrictX : function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.x) {
            p -= (p - this.x) * factor;
        }
        else if (p >= this.right) {
            p -= (p - this.right) * factor;
        }
        return p;
    },

    
    restrictY : function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.y) {
            p -= (p - this.y) * factor;
        }
        else if (p >= this.bottom) {
            p -= (p - this.bottom) * factor;
        }
        return p;
    },

    
    getSize: function() {
        return {
            width: this.right - this.x,
            height: this.bottom - this.y
        };
    },

    
    copy: function() {
        return new this.self(this.y, this.right, this.bottom, this.x);
    },

    
    copyFrom: function(p) {
        var me = this;
        me.top = me.y = me[1] = p.y;
        me.right = p.right;
        me.bottom = p.bottom;
        me.left = me.x = me[0] = p.x;

        return this;
    },

    
    toString: function() {
        return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
    },

    
    translateBy: function(x, y) {
        if (arguments.length == 1) {
            y = x.y;
            x = x.x;
        }
        var me = this;
        me.top = me.y += y;
        me.right += x;
        me.bottom += y;
        me.left = me.x += x;

        return me;
    },

    
    round: function() {
        var me = this;
        me.top = me.y = Math.round(me.y);
        me.right = Math.round(me.right);
        me.bottom = Math.round(me.bottom);
        me.left = me.x = Math.round(me.x);

        return me;
    },

    
    equals: function(region) {
        return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left);
    }
});





Ext.define('Ext.dd.DragDropManager', {
    singleton: true,

                                  

                                      

    
    alternateClassName: ['Ext.dd.DragDropMgr', 'Ext.dd.DDM'],

    
    ids: {},

    
    handleIds: {},

    
    dragCurrent: null,

    
    dragOvers: {},

    
    deltaX: 0,

    
    deltaY: 0,

    
    preventDefault: true,

    
    stopPropagation: true,

    
    initialized: false,

    
    locked: false,

    
    init: function() {
        this.initialized = true;
    },

    
    POINT: 0,

    
    INTERSECT: 1,

    
    mode: 0,

    
    notifyOccluded: false,

    
    dragCls: Ext.baseCSSPrefix + 'dd-drag-current',

    
    _execOnAll: function(sMethod, args) {
        var i, j, oDD;
        for (i in this.ids) {
            for (j in this.ids[i]) {
                oDD = this.ids[i][j];
                if (! this.isTypeOfDD(oDD)) {
                    continue;
                }
                oDD[sMethod].apply(oDD, args);
            }
        }
    },

    
    _onLoad: function() {

        this.init();

        var Event = Ext.EventManager;
        Event.on(document, "mouseup",   this.handleMouseUp, this, true);
        Event.on(document, "mousemove", this.handleMouseMove, this, true);
        Event.on(window,   "unload",    this._onUnload, this, true);
        Event.on(window,   "resize",    this._onResize, this, true);
        

    },

    
    _onResize: function(e) {
        this._execOnAll("resetConstraints", []);
    },

    
    lock: function() { this.locked = true; },

    
    unlock: function() { this.locked = false; },

    
    isLocked: function() { return this.locked; },

    
    locationCache: {},

    
    useCache: true,

    
    clickPixelThresh: 3,

    
    clickTimeThresh: 350,

    
    dragThreshMet: false,

    
    clickTimeout: null,

    
    startX: 0,

    
    startY: 0,

    
    regDragDrop: function(oDD, sGroup) {
        if (!this.initialized) { this.init(); }

        if (!this.ids[sGroup]) {
            this.ids[sGroup] = {};
        }
        this.ids[sGroup][oDD.id] = oDD;
    },

    
    removeDDFromGroup: function(oDD, sGroup) {
        if (!this.ids[sGroup]) {
            this.ids[sGroup] = {};
        }

        var obj = this.ids[sGroup];
        if (obj && obj[oDD.id]) {
            delete obj[oDD.id];
        }
    },

    
    _remove: function(oDD) {
        for (var g in oDD.groups) {
            if (g && this.ids[g] && this.ids[g][oDD.id]) {
                delete this.ids[g][oDD.id];
            }
        }
        delete this.handleIds[oDD.id];
    },

    
    regHandle: function(sDDId, sHandleId) {
        if (!this.handleIds[sDDId]) {
            this.handleIds[sDDId] = {};
        }
        this.handleIds[sDDId][sHandleId] = sHandleId;
    },

    
    isDragDrop: function(id) {
        return ( this.getDDById(id) ) ? true : false;
    },

    
    getRelated: function(p_oDD, bTargetsOnly) {
        var oDDs = [],
            i, j, dd;
        for (i in p_oDD.groups) {
            for (j in this.ids[i]) {
                dd = this.ids[i][j];
                if (! this.isTypeOfDD(dd)) {
                    continue;
                }
                if (!bTargetsOnly || dd.isTarget) {
                    oDDs[oDDs.length] = dd;
                }
            }
        }

        return oDDs;
    },

    
    isLegalTarget: function (oDD, oTargetDD) {
        var targets = this.getRelated(oDD, true),
            i, len;
        for (i=0, len=targets.length;i<len;++i) {
            if (targets[i].id == oTargetDD.id) {
                return true;
            }
        }

        return false;
    },

    
    isTypeOfDD: function (oDD) {
        return (oDD && oDD.__ygDragDrop);
    },

    
    isHandle: function(sDDId, sHandleId) {
        return ( this.handleIds[sDDId] &&
                        this.handleIds[sDDId][sHandleId] );
    },

    
    getDDById: function(id) {
        var i, dd;
        for (i in this.ids) {
            dd = this.ids[i][id];
            if (dd instanceof Ext.dd.DDTarget) {
                return dd;
            }
        }
        return null;
    },

    
    handleMouseDown: function(e, oDD) {
        var me = this,
            el;

        if (Ext.quickTipsActive){
            Ext.tip.QuickTipManager.ddDisable();
        }
        if (me.dragCurrent){
            
            
            me.handleMouseUp(e);
        }

        me.currentTarget = e.getTarget();
        me.dragCurrent = oDD;

        el = oDD.getEl();

        
        
        
        
        if (Ext.isIE9m && el.setCapture) {
            el.setCapture();
        }

        
        me.startX = e.getPageX();
        me.startY = e.getPageY();

        me.deltaX = me.startX - el.offsetLeft;
        me.deltaY = me.startY - el.offsetTop;

        me.dragThreshMet = false;

        me.clickTimeout = setTimeout(
            function() {
                me.startDrag(me.startX, me.startY);
            },
            me.clickTimeThresh
        );
    },

    
    startDrag: function(x, y) {
        var me = this,
            current = me.dragCurrent,
            dragEl;

        clearTimeout(me.clickTimeout);
        if (current) {
            current.b4StartDrag(x, y);
            current.startDrag(x, y);
            dragEl = current.getDragEl();

            
            if (dragEl) {
                Ext.fly(dragEl).addCls(me.dragCls);
            }
        }
        me.dragThreshMet = true;
    },

    
    handleMouseUp: function(e) {
        var me = this;

        if (Ext.quickTipsActive){
            Ext.tip.QuickTipManager.ddEnable();
        }
        if (!me.dragCurrent) {
            return;
        }

        
        if (Ext.isIE && document.releaseCapture) {
            document.releaseCapture();
        }

        clearTimeout(me.clickTimeout);

        if (me.dragThreshMet) {
            me.fireEvents(e, true);
        }

        me.stopDrag(e);

        me.stopEvent(e);
    },

    
    stopEvent: function(e) {
        if (this.stopPropagation) {
            e.stopPropagation();
        }

        if (this.preventDefault) {
            e.preventDefault();
        }
    },

    
    stopDrag: function(e) {
        var me = this,
            current = me.dragCurrent,
            dragEl;

        
        if (current) {
            if (me.dragThreshMet) {

                
                dragEl = current.getDragEl();
                if (dragEl) {
                    Ext.fly(dragEl).removeCls(me.dragCls);
                }

                current.b4EndDrag(e);
                current.endDrag(e);
            }

            me.dragCurrent.onMouseUp(e);
        }

        me.dragCurrent = null;
        me.dragOvers = {};
    },

    
    handleMouseMove: function(e) {
        var me = this,
            current = me.dragCurrent,
            diffX,
            diffY;

        if (!current) {
            return true;
        }

        if (!me.dragThreshMet) {
            diffX = Math.abs(me.startX - e.getPageX());
            diffY = Math.abs(me.startY - e.getPageY());
            if (diffX > me.clickPixelThresh || diffY > me.clickPixelThresh) {
                me.startDrag(me.startX, me.startY);
            }
        }

        if (me.dragThreshMet) {
            current.b4Drag(e);
            current.onDrag(e);
            if (!current.moveOnly) {
                me.fireEvents(e, false);
            }
        }

        me.stopEvent(e);

        return true;
    },

    
    fireEvents: function(e, isDrop) {
        var me = this,
            dragCurrent = me.dragCurrent,
            dragEl,
            oldDragElTop,
            mousePoint = e.getPoint(),
            overTarget,
            overTargetEl,
            allTargets = [],
            oldOvers  = [],  
            outEvts   = [],
            overEvts  = [],
            dropEvts  = [],
            enterEvts = [],
            xy,
            needsSort,
            i,
            len,
            sGroup;

        
        
        if (!dragCurrent || dragCurrent.isLocked()) {
            return;
        }

        
        
        
        
        
        
        if (!me.notifyOccluded && (!Ext.supports.PointerEvents || Ext.isIE10m || Ext.isOpera) && !(dragCurrent.deltaX < 0 || dragCurrent.deltaY < 0)) {
            dragEl = dragCurrent.getDragEl();
            oldDragElTop = dragEl.style.top;
            dragEl.style.top = '-10000px';
            xy = e.getXY();
            e.target = document.elementFromPoint(xy[0], xy[1]);
            dragEl.style.top = oldDragElTop;
        }

        
        
        for (i in me.dragOvers) {

            overTarget = me.dragOvers[i];

            if (!me.isTypeOfDD(overTarget)) {
                continue;
            }

            
            if (me.notifyOccluded) {
                if (!this.isOverTarget(mousePoint, overTarget, me.mode)) {
                    outEvts.push(overTarget);
                }
            }
            
            else {
                if (!e.within(overTarget.getEl())) {
                    outEvts.push(overTarget);
                }
            }

            oldOvers[i] = true;
            delete me.dragOvers[i];
        }

        
        
        
        for (sGroup in dragCurrent.groups) {

            if ("string" != typeof sGroup) {
                continue;
            }

            
            for (i in me.ids[sGroup]) {
                overTarget = me.ids[sGroup][i];

                
                
                
                
                
                
                if (me.isTypeOfDD(overTarget) &&
                    (overTargetEl = overTarget.getEl()) &&
                    (overTarget.isTarget) &&
                    (!overTarget.isLocked()) &&
                    (Ext.fly(overTargetEl).isVisible(true)) &&
                    ((overTarget != dragCurrent) || (dragCurrent.ignoreSelf === false))) {

                    
                    if (me.notifyOccluded) {

                        
                        if ((overTarget.zIndex = me.getZIndex(overTargetEl)) !== -1) {
                            needsSort = true;
                        }
                        allTargets.push(overTarget);
                    }
                    
                    else {
                        if (e.within(overTarget.getEl())) {
                            allTargets.push(overTarget);
                            break;
                        }
                    }
                }
            }
        }

        
        if (needsSort) {
            Ext.Array.sort(allTargets, me.byZIndex);
        }

        
        
        for (i = 0, len = allTargets.length; i < len; i++) {
            overTarget = allTargets[i];

            
            if (me.isOverTarget(mousePoint, overTarget, me.mode)) {
                
                if (isDrop) {
                    dropEvts.push( overTarget );
                
                } else {

                    
                    if (!oldOvers[overTarget.id]) {
                        enterEvts.push( overTarget );
                    
                    } else {
                        overEvts.push( overTarget );
                    }
                    me.dragOvers[overTarget.id] = overTarget;
                }

                
                if (!me.notifyOccluded) {
                    break;
                }
            }
        }

        if (me.mode) {
            if (outEvts.length) {
                dragCurrent.b4DragOut(e, outEvts);
                dragCurrent.onDragOut(e, outEvts);
            }

            if (enterEvts.length) {
                dragCurrent.onDragEnter(e, enterEvts);
            }

            if (overEvts.length) {
                dragCurrent.b4DragOver(e, overEvts);
                dragCurrent.onDragOver(e, overEvts);
            }

            if (dropEvts.length) {
                dragCurrent.b4DragDrop(e, dropEvts);
                dragCurrent.onDragDrop(e, dropEvts);
            }

        } else {
            
            for (i=0, len=outEvts.length; i<len; ++i) {
                dragCurrent.b4DragOut(e, outEvts[i].id);
                dragCurrent.onDragOut(e, outEvts[i].id);
            }

            
            for (i=0,len=enterEvts.length; i<len; ++i) {
                
                dragCurrent.onDragEnter(e, enterEvts[i].id);
            }

            
            for (i=0,len=overEvts.length; i<len; ++i) {
                dragCurrent.b4DragOver(e, overEvts[i].id);
                dragCurrent.onDragOver(e, overEvts[i].id);
            }

            
            for (i=0, len=dropEvts.length; i<len; ++i) {
                dragCurrent.b4DragDrop(e, dropEvts[i].id);
                dragCurrent.onDragDrop(e, dropEvts[i].id);
            }

        }

        
        if (isDrop && !dropEvts.length) {
            dragCurrent.onInvalidDrop(e);
        }

    },

    
    getZIndex: function(element) {
        var body = document.body,
            z,
            zIndex = -1;

        element = Ext.getDom(element);
        while (element !== body) {
            if (!isNaN(z = Number(Ext.fly(element).getStyle('zIndex')))) {
                zIndex = z;
            }
            element = element.parentNode;
        }
        return zIndex;
    },

    
    byZIndex: function(d1, d2) {
        return d1.zIndex < d2.zIndex;
    },

    
    getBestMatch: function(dds) {
        var winner = null,
            len = dds.length,
            i, dd;
        
        
           
        
        


        if (len == 1) {
            winner = dds[0];
        } else {
            
            for (i=0; i<len; ++i) {
                dd = dds[i];
                
                
                
                if (dd.cursorIsOver) {
                    winner = dd;
                    break;
                
                } else {
                    if (!winner ||
                        winner.overlap.getArea() < dd.overlap.getArea()) {
                        winner = dd;
                    }
                }
            }
        }

        return winner;
    },

    
    refreshCache: function(groups) {
        var sGroup, i, oDD, loc;
        for (sGroup in groups) {
            if ("string" != typeof sGroup) {
                continue;
            }
            for (i in this.ids[sGroup]) {
                oDD = this.ids[sGroup][i];

                if (this.isTypeOfDD(oDD)) {
                
                    loc = this.getLocation(oDD);
                    if (loc) {
                        this.locationCache[oDD.id] = loc;
                    } else {
                        delete this.locationCache[oDD.id];
                        
                        
                        
                    }
                }
            }
        }
    },

    
    verifyEl: function(el) {
        if (el) {
            var parent;
            if(Ext.isIE){
                try{
                    parent = el.offsetParent;
                }catch(e){}
            }else{
                parent = el.offsetParent;
            }
            if (parent) {
                return true;
            }
        }

        return false;
    },

    
    getLocation: function(oDD) {
        if (! this.isTypeOfDD(oDD)) {
            return null;
        }

        
        
        if (oDD.getRegion) {
            return oDD.getRegion();
        }

        var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;

        try {
            pos= Ext.Element.getXY(el);
        } catch (e) { }

        if (!pos) {
            return null;
        }

        x1 = pos[0];
        x2 = x1 + el.offsetWidth;
        y1 = pos[1];
        y2 = y1 + el.offsetHeight;

        t = y1 - oDD.padding[0];
        r = x2 + oDD.padding[1];
        b = y2 + oDD.padding[2];
        l = x1 - oDD.padding[3];

        return new Ext.util.Region(t, r, b, l);
    },

    
    isOverTarget: function(pt, oTarget, intersect) {
        
        var loc = this.locationCache[oTarget.id],
            dc,
            pos,
            el,
            curRegion,
            overlap;

        if (!loc || !this.useCache) {
            loc = this.getLocation(oTarget);
            this.locationCache[oTarget.id] = loc;
        }

        if (!loc) {
            return false;
        }

        oTarget.cursorIsOver = loc.contains( pt );

        
        
        
        
        
        dc = this.dragCurrent;
        if (!dc || !dc.getTargetCoord ||
                (!intersect && !dc.constrainX && !dc.constrainY)) {
            return oTarget.cursorIsOver;
        }

        oTarget.overlap = null;

        
        
        
        
        pos = dc.getTargetCoord(pt.x, pt.y);

        el = dc.getDragEl();
        curRegion = new Ext.util.Region(pos.y,
            pos.x + el.offsetWidth,
            pos.y + el.offsetHeight,
            pos.x
        );

        overlap = curRegion.intersect(loc);

        if (overlap) {
            oTarget.overlap = overlap;
            return (intersect) ? true : oTarget.cursorIsOver;
        } else {
            return false;
        }
    },

    
    _onUnload: function(e, me) {
        Ext.dd.DragDropManager.unregAll();
    },

    
    unregAll: function() {

        if (this.dragCurrent) {
            this.stopDrag();
            this.dragCurrent = null;
        }

        this._execOnAll("unreg", []);

        for (var i in this.elementCache) {
            delete this.elementCache[i];
        }

        this.elementCache = {};
        this.ids = {};
    },

    
    elementCache: {},

    
    getElWrapper: function(id) {
        var oWrapper = this.elementCache[id];
        if (!oWrapper || !oWrapper.el) {
            oWrapper = this.elementCache[id] =
                new this.ElementWrapper(Ext.getDom(id));
        }
        return oWrapper;
    },

    
    getElement: function(id) {
        return Ext.getDom(id);
    },

    
    getCss: function(id) {
        var el = Ext.getDom(id);
        return (el) ? el.style : null;
    },

    
    ElementWrapper: function(el) {
        
        this.el = el || null;
        
        this.id = this.el && el.id;
        
        this.css = this.el && el.style;
    },

    
    

    
    getPosX: function(el) {
        return Ext.Element.getX(el);
    },

    
    getPosY: function(el) {
        return Ext.Element.getY(el);
    },

    
    swapNode: function(n1, n2) {
        if (n1.swapNode) {
            n1.swapNode(n2);
        } else {
            var p = n2.parentNode,
                s = n2.nextSibling;

            if (s == n1) {
                p.insertBefore(n1, n2);
            } else if (n2 == n1.nextSibling) {
                p.insertBefore(n2, n1);
            } else {
                n1.parentNode.replaceChild(n2, n1);
                p.insertBefore(n1, s);
            }
        }
    },

    
    getScroll: function () {
        var doc   = window.document,
            docEl = doc.documentElement,
            body  = doc.body,
            top   = 0,
            left  = 0;

        if (Ext.isGecko4) {
            top  = window.scrollYOffset;
            left = window.scrollXOffset;
        } else {
            if (docEl && (docEl.scrollTop || docEl.scrollLeft)) {
                top  = docEl.scrollTop;
                left = docEl.scrollLeft;
            } else if (body) {
                top  = body.scrollTop;
                left = body.scrollLeft;
            }
        }
        return {
            top: top,
            left: left
        };
    },

    
    getStyle: function(el, styleProp) {
        return Ext.fly(el).getStyle(styleProp);
    },

    
    getScrollTop: function () {
        return this.getScroll().top;
    },

    
    getScrollLeft: function () {
        return this.getScroll().left;
    },

    
    moveToEl: function (moveEl, targetEl) {
        var aCoord = Ext.Element.getXY(targetEl);
        Ext.Element.setXY(moveEl, aCoord);
    },

    
    numericSort: function(a, b) {
        return (a - b);
    },

    
    _timeoutCount: 0,

    
    _addListeners: function() {
        if ( document ) {
            this._onLoad();
        } else {
            if (this._timeoutCount <= 2000) {
                setTimeout(this._addListeners, 10);
                if (document && document.body) {
                    this._timeoutCount += 1;
                }
            }
        }
    },

    
    handleWasClicked: function(node, id) {
        if (this.isHandle(id, node.id)) {
            return true;
        } else {
            
            var p = node.parentNode;

            while (p) {
                if (this.isHandle(id, p.id)) {
                    return true;
                } else {
                    p = p.parentNode;
                }
            }
        }

        return false;
    }
}, function() {
    this._addListeners();
});


Ext.define('Ext.layout.container.Box', {

    

    alias: ['layout.box'],
    extend:  Ext.layout.container.Container ,
    alternateClassName: 'Ext.layout.BoxLayout',

               
                                                
                                                
                                                    
                          
                                
      

    

    
    defaultMargins: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    },

    
    padding: 0,

    
    pack: 'start',

    
    flex: undefined,

    
    stretchMaxPartner: undefined,

    alignRoundingMethod: 'round',

    type: 'box',
    scrollOffset: 0,
    itemCls: Ext.baseCSSPrefix + 'box-item',
    targetCls: Ext.baseCSSPrefix + 'box-layout-ct',
    targetElCls: Ext.baseCSSPrefix + 'box-target',
    innerCls: Ext.baseCSSPrefix + 'box-inner',

    
    
    availableSpaceOffset: 0,

    
    reserveOffset: true,

    manageMargins: true,
    
    createsInnerCt: true,

    childEls: [
        'innerCt',
        'targetEl'
    ],

    renderTpl: [
        '{%var oc,l=values.$comp.layout,oh=l.overflowHandler;',
        'if (oh.getPrefixConfig!==Ext.emptyFn) {',
            'if(oc=oh.getPrefixConfig())dh.generateMarkup(oc, out)',
        '}%}',
        '<div id="{ownerId}-innerCt" class="{[l.innerCls]} {[oh.getOverflowCls()]}" role="presentation">',
            '<div id="{ownerId}-targetEl" class="{targetElCls}">',
                '{%this.renderBody(out, values)%}',
            '</div>',
        '</div>',
        '{%if (oh.getSuffixConfig!==Ext.emptyFn) {',
            'if(oc=oh.getSuffixConfig())dh.generateMarkup(oc, out)',
        '}%}',
        {
            disableFormats: true,
            definitions: 'var dh=Ext.DomHelper;'
        }
    ],

    constructor: function(config) {
        var me = this,
            type;

        me.callParent(arguments);

        
        me.flexSortFn = Ext.Function.bind(me.flexSort, me);

        me.initOverflowHandler();

        type = typeof me.padding;
        if (type == 'string' || type == 'number') {
            me.padding = Ext.util.Format.parseBox(me.padding);
            me.padding.height = me.padding.top  + me.padding.bottom;
            me.padding.width  = me.padding.left + me.padding.right;
        }
    },

    
    
    _percentageRe: /^\s*(\d+(?:\.\d*)?)\s*[%]\s*$/,

    getItemSizePolicy: function (item, ownerSizeModel) {
        var me = this,
            policy = me.sizePolicy,
            align = me.align,
            flex = item.flex,
            key = align,
            names = me.names,
            width = item[names.width],
            height = item[names.height],
            percentageRe = me._percentageRe,
            percentageWidth = percentageRe.test(width),
            isStretch = (align == 'stretch'),
            isStretchMax = (align == 'stretchmax'),
            constrain = me.constrainAlign;
            
        
        if (!ownerSizeModel && (isStretch || flex || percentageWidth || (constrain && !isStretchMax))) {
            ownerSizeModel = me.owner.getSizeModel();
        }

        if (isStretch) {
            
            
            if (!percentageRe.test(height) && ownerSizeModel[names.height].shrinkWrap) {
                key = 'stretchmax';
                
                
                
            }
        } else if (!isStretchMax) {
            if (percentageRe.test(height)) {
                
                
                key = 'stretch';
            } else if (constrain && !ownerSizeModel[names.height].shrinkWrap) {
                
                
                key = 'stretchmax';
            } else {
                key = '';
            }
        }

        if (flex || percentageWidth) {
            
            
            if (!ownerSizeModel[names.width].shrinkWrap) {
                policy = policy.flex; 
            }
        }

        return policy[key];
    },

    flexSort: function (a, b) {
        
        
        
        
        
        var maxWidthName = this.names.maxWidth,
            minWidthName = this.names.minWidth,
            infiniteValue = Infinity,
            aTarget = a.target,
            bTarget = b.target,
            result = 0,
            aMin, bMin, aMax, bMax,
            hasMin, hasMax;

        aMax = aTarget[maxWidthName] || infiniteValue;
        bMax = bTarget[maxWidthName] || infiniteValue;
        aMin = aTarget[minWidthName] || 0;
        bMin = bTarget[minWidthName] || 0;
        
        hasMin = isFinite(aMin) || isFinite(bMin);
        hasMax = isFinite(aMax) || isFinite(bMax);

        if (hasMin || hasMax) {
            if (hasMax) {
                result = aMax - bMax;
            }
            
            
            
            
            if (result === 0 && hasMin) {
                result = bMin - aMin;
            }
        }
        return result;
    },

    isItemBoxParent: function (itemContext) {
        return true;
    },

    isItemShrinkWrap: function (item) {
        return true;
    },

    roundFlex: function(width) {
        return Math.ceil(width);
    },

    
    beginCollapse: function(child) {
        var me = this;

        if (me.direction === 'vertical' && child.collapsedVertical()) {
            child.collapseMemento.capture(['flex']);
            delete child.flex;
        } else if (me.direction === 'horizontal' && child.collapsedHorizontal()) {
            child.collapseMemento.capture(['flex']);
            delete child.flex;
        }
    },

    
    beginExpand: function(child) {

        
        child.collapseMemento.restore(['flex']);
    },

    beginLayout: function (ownerContext) {
        var me = this,
            owner = me.owner,
            smp = owner.stretchMaxPartner,
            style = me.innerCt.dom.style,
            names = me.names;

        ownerContext.boxNames = names;

        
        
        me.overflowHandler.beginLayout(ownerContext);

        
        if (typeof smp === 'string') {
            smp = Ext.getCmp(smp) || owner.query(smp)[0];
        }

        ownerContext.stretchMaxPartner = smp && ownerContext.context.getCmp(smp);

        me.callParent(arguments);

        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);

        
        me.scrollParallel = owner.scrollFlags[names.x];

        
        me.scrollPerpendicular = owner.scrollFlags[names.y];

        
        if (me.scrollParallel) {
            me.scrollPos = owner.getTargetEl().dom[names.scrollLeft];
        }

        
        style.width = '';
        style.height = '';
    },

    beginLayoutCycle: function (ownerContext, firstCycle) {
        var me = this,
            align = me.align,
            names = ownerContext.boxNames,
            pack = me.pack,
            heightModelName = names.heightModel;

        
        
        me.overflowHandler.beginLayoutCycle(ownerContext, firstCycle);

        me.callParent(arguments);

        
        

        ownerContext.parallelSizeModel      = ownerContext[names.widthModel];
        ownerContext.perpendicularSizeModel = ownerContext[heightModelName];

        ownerContext.boxOptions = {
            align: align = {
                stretch:    align == 'stretch',
                stretchmax: align == 'stretchmax',
                center:     align == names.center,
                bottom:     align == names.afterY
            },
            pack: pack = {
                center: pack == 'center',
                end:    pack == 'end'
            }
        };

        
        
        
        
        

        if (align.stretch && ownerContext.perpendicularSizeModel.shrinkWrap) {
            align.stretchmax = true;
            align.stretch = false;
        }

        
        align.nostretch = !(align.stretch || align.stretchmax);

        
        
        

        if (ownerContext.parallelSizeModel.shrinkWrap) {
            pack.center = pack.end = false;
        }

        me.cacheFlexes(ownerContext);

        
        
        
        
        
        
        me.targetEl.setWidth(20000);
    },

    
    cacheFlexes: function (ownerContext) {
        var me = this,
            names = ownerContext.boxNames,
            widthModelName = names.widthModel,
            heightModelName = names.heightModel,
            nostretch = ownerContext.boxOptions.align.nostretch,
            totalFlex = 0,
            childItems = ownerContext.childItems,
            i = childItems.length,
            flexedItems = [],
            minWidth = 0,
            minWidthName = names.minWidth,
            percentageRe = me._percentageRe,
            percentageWidths = 0,
            percentageHeights = 0,
            child, childContext, flex, match;

        while (i--) {
            childContext = childItems[i];
            child = childContext.target;

            
            
            
            if (childContext[widthModelName].calculated) {
                childContext.flex = flex = child.flex;
                if (flex) {
                    totalFlex += flex;
                    flexedItems.push(childContext);
                    minWidth += child[minWidthName] || 0;
                } else { 
                    match = percentageRe.exec(child[names.width]);
                    childContext.percentageParallel = parseFloat(match[1]) / 100;
                    ++percentageWidths;
                }
            }
            
            

            if (nostretch && childContext[heightModelName].calculated) {
                
                
                match = percentageRe.exec(child[names.height]);
                childContext.percentagePerpendicular = parseFloat(match[1]) / 100;
                ++percentageHeights;
            }
        }

        ownerContext.flexedItems = flexedItems;
        ownerContext.flexedMinSize = minWidth;
        ownerContext.totalFlex = totalFlex;
        ownerContext.percentageWidths = percentageWidths;
        ownerContext.percentageHeights = percentageHeights;

        
        
        
        Ext.Array.sort(flexedItems, me.flexSortFn);
    },

    calculate: function(ownerContext) {
        var me = this,
            targetSize = me.getContainerSize(ownerContext),
            names = ownerContext.boxNames,
            state = ownerContext.state,
            plan = state.boxPlan || (state.boxPlan = {}),
            targetContext = ownerContext.targetContext;

        plan.targetSize = targetSize;

        
        if (!ownerContext.parallelSizeModel.shrinkWrap && !targetSize[names.gotWidth]) {
            me.done = false;
            return;
        }

        if (!state.parallelDone) {
            state.parallelDone = me.calculateParallel(ownerContext, names, plan);
        }

        if (!state.perpendicularDone) {
            state.perpendicularDone = me.calculatePerpendicular(ownerContext, names, plan);
        }

        if (state.parallelDone && state.perpendicularDone) {
            
            
            
            
            if (me.owner.dock && (Ext.isIE7m || Ext.isIEQuirks) && !me.owner.width && !me.horizontal) {
                plan.isIEVerticalDock = true;
                plan.calculatedWidth = plan.maxSize + ownerContext.getPaddingInfo().width + ownerContext.getFrameInfo().width;
                if (targetContext !== ownerContext) {
                    
                    
                    
                    plan.calculatedWidth += targetContext.getPaddingInfo().width;
                }
            }

            me.publishInnerCtSize(ownerContext, me.reserveOffset ? me.availableSpaceOffset : 0);

            
            if (me.done && (ownerContext.childItems.length > 1 || ownerContext.stretchMaxPartner) && ownerContext.boxOptions.align.stretchmax && !state.stretchMaxDone) {
                me.calculateStretchMax(ownerContext, names, plan);
                state.stretchMaxDone = true;
            }
            me.overflowHandler.calculate(ownerContext);
        } else {
            me.done = false;
        }
    },

    calculateParallel: function(ownerContext, names, plan) {
        var me = this,
            widthName = names.width,
            childItems = ownerContext.childItems,
            beforeXName = names.beforeX,
            afterXName = names.afterX,
            setWidthName = names.setWidth,
            childItemsLength = childItems.length,
            flexedItems = ownerContext.flexedItems,
            flexedItemsLength = flexedItems.length,
            pack = ownerContext.boxOptions.pack,
            padding = me.padding,
            containerWidth = plan.targetSize[widthName],
            totalMargin = 0,
            left = padding[beforeXName],
            nonFlexWidth = left + padding[afterXName] + me.scrollOffset +
                                    (me.reserveOffset ? me.availableSpaceOffset : 0),
            scrollbarWidth = Ext.getScrollbarSize()[names.width],
            i, childMargins, remainingWidth, remainingFlex, childContext, flex, flexedWidth,
            contentWidth, mayNeedScrollbarAdjust, childWidth, percentageSpace;

        
        
        
        
        
        
        if (scrollbarWidth &&
            me.scrollPerpendicular &&
            ownerContext.parallelSizeModel.shrinkWrap &&
            !ownerContext.boxOptions.align.stretch &&
            !ownerContext.perpendicularSizeModel.shrinkWrap) {

            
            
            
            if (!ownerContext.state.perpendicularDone) {
                return false;
            }
            mayNeedScrollbarAdjust = true;
        }

        
        for (i = 0; i < childItemsLength; ++i) {
            childContext = childItems[i];
            childMargins = childContext.marginInfo || childContext.getMarginInfo();

            totalMargin += childMargins[widthName];

            if (!childContext[names.widthModel].calculated) {
                childWidth = childContext.getProp(widthName);
                nonFlexWidth += childWidth; 
                if (isNaN(nonFlexWidth)) {
                    return false;
                }
            }
        }

        nonFlexWidth += totalMargin;
        if (ownerContext.percentageWidths) {
            percentageSpace = containerWidth - totalMargin;
            if (isNaN(percentageSpace)) {
                return false;
            }

            for (i = 0; i < childItemsLength; ++i) {
                childContext = childItems[i];
                if (childContext.percentageParallel) {
                    childWidth = Math.ceil(percentageSpace * childContext.percentageParallel);
                    childWidth = childContext.setWidth(childWidth);
                    nonFlexWidth += childWidth;
                }
            }
        }

        

        if (ownerContext.parallelSizeModel.shrinkWrap) {
            plan.availableSpace = 0;
            plan.tooNarrow = false;
        } else {
            plan.availableSpace = containerWidth - nonFlexWidth;

            
            plan.tooNarrow = plan.availableSpace < ownerContext.flexedMinSize;
            if (plan.tooNarrow && Ext.getScrollbarSize()[names.height] && me.scrollParallel && ownerContext.state.perpendicularDone) {
                ownerContext.state.perpendicularDone = false;
                for (i = 0; i < childItemsLength; ++i) {
                    childItems[i].invalidate();
                }
            }
        }

        contentWidth = nonFlexWidth;
        remainingWidth = plan.availableSpace;
        remainingFlex = ownerContext.totalFlex;

        
        for (i = 0; i < flexedItemsLength; i++) {
            childContext = flexedItems[i];
            flex         = childContext.flex;
            flexedWidth  = me.roundFlex((flex / remainingFlex) * remainingWidth);
            flexedWidth  = childContext[setWidthName](flexedWidth); 

            

            contentWidth   += flexedWidth;
            
            remainingWidth  = Math.max(0, remainingWidth - flexedWidth); 
            remainingFlex  -= flex;
        }

        if (pack.center) {
            left += remainingWidth / 2;

            
            if (left < 0) {
                left = 0;
            }
        } else if (pack.end) {
            left += remainingWidth;
        }

        
        for (i = 0; i < childItemsLength; ++i) {
            childContext = childItems[i];
            childMargins = childContext.marginInfo; 

            left += childMargins[beforeXName];

            childContext.setProp(names.x, left);

            
            
            
            
            left += childMargins[afterXName] + childContext.props[widthName];
        }

        contentWidth += ownerContext.targetContext.getPaddingInfo()[widthName];

        
        ownerContext.state.contentWidth = contentWidth; 

        
        
        if (mayNeedScrollbarAdjust &&
            (ownerContext.peek(names.contentHeight) > plan.targetSize[names.height])) {
            contentWidth += scrollbarWidth;
            ownerContext[names.hasOverflowY] = true;

            
            ownerContext.target.componentLayout[names.setWidthInDom] = true;

            
            
            
            
            ownerContext[names.invalidateScrollY] = Ext.isStrict && Ext.isIE8;
        }
        ownerContext[names.setContentWidth](contentWidth);

        return true;
    },

    calculatePerpendicular: function(ownerContext, names, plan) {
        var me = this,
            heightShrinkWrap = ownerContext.perpendicularSizeModel.shrinkWrap,
            targetSize = plan.targetSize,
            childItems = ownerContext.childItems,
            childItemsLength = childItems.length,
            mmax = Math.max,
            heightName = names.height,
            setHeightName = names.setHeight,
            beforeYName = names.beforeY,
            topPositionName = names.y,
            padding = me.padding,
            top = padding[beforeYName],
            availHeight = targetSize[heightName] - top - padding[names.afterY],
            align = ownerContext.boxOptions.align,
            isStretch    = align.stretch, 
            isStretchMax = align.stretchmax,
            isCenter     = align.center,
            isBottom     = align.bottom,
            constrain    = me.constrainAlign,
            maxHeight = 0,
            hasPercentageSizes = 0,
            onBeforeInvalidateChild = me.onBeforeConstrainInvalidateChild,
            onAfterInvalidateChild = me.onAfterConstrainInvalidateChild,
            scrollbarHeight = Ext.getScrollbarSize().height,
            childTop, i, childHeight, childMargins, diff, height, childContext,
            stretchMaxPartner, stretchMaxChildren, shrinkWrapParallelOverflow, 
            percentagePerpendicular;

        if (isStretch || ((isCenter || isBottom) && !heightShrinkWrap)) {
            if (isNaN(availHeight)) {
                return false;
            }
        }

        
        
        
        
        
        
        if (me.scrollParallel && plan.tooNarrow) {
            if (heightShrinkWrap) {
                shrinkWrapParallelOverflow = true;
            } else {
                availHeight -= scrollbarHeight;
                plan.targetSize[heightName] -= scrollbarHeight;
            }
        }

        if (isStretch) {
            height = availHeight; 
        } else {
            for (i = 0; i < childItemsLength; i++) {
                childContext = childItems[i];
                childMargins = (childContext.marginInfo || childContext.getMarginInfo())[heightName];

                if (!(percentagePerpendicular = childContext.percentagePerpendicular)) {
                    childHeight = childContext.getProp(heightName);
                } else {
                    ++hasPercentageSizes;
                    if (heightShrinkWrap) {
                        
                        
                        continue;
                    } else {
                        childHeight = percentagePerpendicular * availHeight - childMargins;
                        childHeight = childContext[names.setHeight](childHeight);
                    }
                }
                
                
                
                
                
                
                if (!heightShrinkWrap && constrain && childContext[names.heightModel].shrinkWrap && childHeight > availHeight) {
                    childContext.invalidate({
                        before: onBeforeInvalidateChild,
                        after: onAfterInvalidateChild,
                        layout: me,
                        childHeight: availHeight,
                        names: names
                    });
                    
                    
                    
                    ownerContext.state.parallelDone = false; 
                }

                
                if (isNaN(maxHeight = mmax(maxHeight, childHeight + childMargins,
                                           childContext.target[names.minHeight] || 0))) {
                    return false; 
                }
            }

            
            
            if (shrinkWrapParallelOverflow) {
                maxHeight += scrollbarHeight;
                ownerContext[names.hasOverflowX] = true;

                
                ownerContext.target.componentLayout[names.setHeightInDom] = true;

                
                
                
                
                ownerContext[names.invalidateScrollX] = Ext.isStrict && Ext.isIE8;
            }

            
            
            stretchMaxPartner = ownerContext.stretchMaxPartner;
            if (stretchMaxPartner) {
                
                ownerContext.setProp('maxChildHeight', maxHeight);
                stretchMaxChildren = stretchMaxPartner.childItems;
                
                if (stretchMaxChildren && stretchMaxChildren.length) {
                    maxHeight = mmax(maxHeight, stretchMaxPartner.getProp('maxChildHeight'));
                    if (isNaN(maxHeight)) {
                        return false;
                    }
                }
            }

            ownerContext[names.setContentHeight](maxHeight + me.padding[heightName] +
                    ownerContext.targetContext.getPaddingInfo()[heightName]);

            
            
            
            if (shrinkWrapParallelOverflow) {
                maxHeight -= scrollbarHeight;
            }
            plan.maxSize = maxHeight;

            if (isStretchMax) {
                height = maxHeight;
            } else if (isCenter || isBottom || hasPercentageSizes) {
                if (constrain) {
                    height = heightShrinkWrap ? maxHeight : availHeight;
                } else {
                    height = heightShrinkWrap ? maxHeight : mmax(availHeight, maxHeight);
                }

                
                
                
                
                height -= ownerContext.innerCtContext.getBorderInfo()[heightName];
            }
        }

        for (i = 0; i < childItemsLength; i++) {
            childContext = childItems[i];
            childMargins = childContext.marginInfo || childContext.getMarginInfo();

            childTop = top + childMargins[beforeYName];

            if (isStretch) {
                childContext[setHeightName](height - childMargins[heightName]);
            } else {
                percentagePerpendicular = childContext.percentagePerpendicular;
                if (heightShrinkWrap && percentagePerpendicular) {
                    childMargins = childContext.marginInfo || childContext.getMarginInfo();
                    childHeight = percentagePerpendicular * height - childMargins[heightName];
                    childHeight = childContext.setHeight(childHeight);
                }

                if (isCenter) {
                    diff = height - childContext.props[heightName];
                    if (diff > 0) {
                        childTop = top + Math[me.alignRoundingMethod](diff / 2);
                    }
                } else if (isBottom) {
                    childTop = mmax(0, height - childTop - childContext.props[heightName]);
                }
            }

            childContext.setProp(topPositionName, childTop);
        }

        return true;
    },
    
    onBeforeConstrainInvalidateChild: function(childContext, options){
        
        var heightModelName = options.names.heightModel;
        if (!childContext[heightModelName].constrainedMin) {
            
            
            childContext[heightModelName] = Ext.layout.SizeModel.calculated;
        }
    },
    
    onAfterConstrainInvalidateChild: function(childContext, options){
         
        var names = options.names;

        
        
        
        
        
        childContext.setProp(names.beforeY, 0);
        if (childContext[names.heightModel].calculated) {
            childContext[names.setHeight](options.childHeight);
        }
    },

    calculateStretchMax: function (ownerContext, names, plan) {
        var me = this,
            heightName = names.height,
            widthName = names.width,
            childItems = ownerContext.childItems,
            length = childItems.length,
            height = plan.maxSize,
            onBeforeStretchMaxInvalidateChild = me.onBeforeStretchMaxInvalidateChild,
            onAfterStretchMaxInvalidateChild = me.onAfterStretchMaxInvalidateChild,
            childContext, props, i, childHeight;

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];

            props = childContext.props;
            childHeight = height - childContext.getMarginInfo()[heightName];

            if (childHeight != props[heightName] ||   
                childContext[names.heightModel].constrained) { 
                
                
                
                
                
                
                
                childContext.invalidate({
                    before: onBeforeStretchMaxInvalidateChild,
                    after: onAfterStretchMaxInvalidateChild,
                    layout: me,
                    
                    childWidth: props[widthName],
                    
                    childHeight: childHeight,
                    childX: props.x,
                    childY: props.y,
                    names: names
                });
            }
        }
    },
    
    onBeforeStretchMaxInvalidateChild: function (childContext, options) {
        
        var heightModelName = options.names.heightModel;

        
        
        
        
        if (!childContext[heightModelName].constrainedMax) {
            
            
            childContext[heightModelName] = Ext.layout.SizeModel.calculated;
        }
    },

    onAfterStretchMaxInvalidateChild: function (childContext, options) {
        
        var names = options.names,
            childHeight = options.childHeight,
            childWidth = options.childWidth;

        childContext.setProp('x', options.childX);
        childContext.setProp('y', options.childY);

        if (childContext[names.heightModel].calculated) {
            
            
            childContext[names.setHeight](childHeight);
        }

        if (childContext[names.widthModel].calculated) {
            childContext[names.setWidth](childWidth);
        }
    },

    completeLayout: function(ownerContext) {
        var me = this,
            names = ownerContext.boxNames,
            invalidateScrollX = ownerContext.invalidateScrollX,
            invalidateScrollY = ownerContext.invalidateScrollY,
            dom, el, overflowX, overflowY, styles;

        me.overflowHandler.completeLayout(ownerContext);

        if (invalidateScrollX || invalidateScrollY) {
            el = me.getTarget();
            dom = el.dom;
            styles = dom.style;

            if (invalidateScrollX) {
                
                overflowX = el.getStyle('overflowX');
                if (overflowX == 'auto') {
                    
                    overflowX = styles.overflowX;
                    styles.overflowX = 'scroll'; 
                } else {
                    invalidateScrollX = false; 
                }
            }

            if (invalidateScrollY) {
                
                overflowY = el.getStyle('overflowY');
                if (overflowY == 'auto') {
                    
                    overflowY = styles.overflowY;
                    styles.overflowY = 'scroll'; 
                } else {
                    invalidateScrollY = false; 
                }
            }

            if (invalidateScrollX || invalidateScrollY) { 
                
                dom.scrollWidth;

                if (invalidateScrollX) {
                    styles.overflowX = overflowX; 
                }
                if (invalidateScrollY) {
                    styles.overflowY = overflowY; 
                }
            }
        }

        
        if (me.scrollParallel) {
            me.owner.getTargetEl().dom[names.scrollLeft] = me.scrollPos;
        }
    },

    finishedLayout: function(ownerContext) {
        this.overflowHandler.finishedLayout(ownerContext);
        this.callParent(arguments);

        
        
        
        
        
        
        
        
        
        this.targetEl.setWidth(ownerContext.innerCtContext.props.width);
    },

    publishInnerCtSize: function(ownerContext, reservedSpace) {
        var me = this,
            names = ownerContext.boxNames,
            heightName = names.height,
            widthName = names.width,
            align = ownerContext.boxOptions.align,
            dock = me.owner.dock,
            padding = me.padding,
            plan = ownerContext.state.boxPlan,
            targetSize = plan.targetSize,
            height = targetSize[heightName],
            innerCtContext = ownerContext.innerCtContext,
            innerCtWidth = (ownerContext.parallelSizeModel.shrinkWrap || (plan.tooNarrow && me.scrollParallel)
                    ? ownerContext.state.contentWidth - ownerContext.targetContext.getPaddingInfo()[widthName]
                    : targetSize[widthName]) - (reservedSpace || 0),
            innerCtHeight;

        if (align.stretch) {
            innerCtHeight = height;
        } else {
            innerCtHeight = plan.maxSize + padding[names.beforeY] + padding[names.afterY] + innerCtContext.getBorderInfo()[heightName];

            if (!ownerContext.perpendicularSizeModel.shrinkWrap && (align.center || align.bottom)) {
                innerCtHeight = Math.max(height, innerCtHeight);
            }
        }

        innerCtContext[names.setWidth](innerCtWidth);
        innerCtContext[names.setHeight](innerCtHeight);

        
        if (isNaN(innerCtWidth + innerCtHeight)) {
            me.done = false;
        }

        
        
        
        
        
        
        if (plan.calculatedWidth && (dock == 'left' || dock == 'right')) {
            
            ownerContext.setWidth(plan.calculatedWidth, true, true);
        }
    },

    onRemove: function(comp){
        var me = this;
        me.callParent(arguments);
        if (me.overflowHandler) {
            me.overflowHandler.onRemove(comp);
        }
        if (comp.layoutMarginCap == me.id) {
            delete comp.layoutMarginCap;
        }
    },

    
    initOverflowHandler: function() {
        var me = this,
            handler = me.overflowHandler,
            handlerType,
            constructor;

        if (typeof handler == 'string') {
            handler = {
                type: handler
            };
        }

        handlerType = 'None';
        if (handler && handler.type !== undefined) {
            handlerType = handler.type;
        }

        constructor = Ext.layout.container.boxOverflow[handlerType];
        if (constructor[me.type]) {
            constructor = constructor[me.type];
        }

        me.overflowHandler = Ext.create('Ext.layout.container.boxOverflow.' + handlerType, me, handler);
    },

    
    
    getRenderTarget: function() {
        return this.targetEl;
    },

    
    
    getElementTarget: function() {
        return this.innerCt;
    },


    
    destroy: function() {
        Ext.destroy(this.innerCt, this.overflowHandler);
        this.callParent(arguments);
    },

    getRenderData: function() {
        var data = this.callParent();

        data.targetElCls = this.targetElCls;

        return data;
    }
});


Ext.define('Ext.layout.container.HBox', {

    

    alias: ['layout.hbox'],
    extend:  Ext.layout.container.Box ,
    alternateClassName: 'Ext.layout.HBoxLayout',

    

    
    align: 'top', 

    
    
    
    constrainAlign: false,

    type : 'hbox',

    direction: 'horizontal',

    horizontal: true,

    names: {
        
        beforeX: 'left',
        beforeScrollX: 'left',
        beforeScrollerSuffix: '-before-scroller',
        afterScrollerSuffix: '-after-scroller',
        leftCap: 'Left',
        afterX: 'right',
        width: 'width',
        contentWidth: 'contentWidth',
        minWidth: 'minWidth',
        maxWidth: 'maxWidth',
        widthCap: 'Width',
        widthModel: 'widthModel',
        widthIndex: 0,
        x: 'x',
        scrollLeft: 'scrollLeft',
        overflowX: 'overflowX',
        hasOverflowX: 'hasOverflowX',
        invalidateScrollX: 'invalidateScrollX',
        parallelMargins: 'lr',

        
        center: 'middle',
        beforeY: 'top',
        afterY: 'bottom',
        height: 'height',
        contentHeight: 'contentHeight',
        minHeight: 'minHeight',
        maxHeight: 'maxHeight',
        heightCap: 'Height',
        heightModel: 'heightModel',
        heightIndex: 1,
        y: 'y',
        overflowY: 'overflowY',
        hasOverflowY: 'hasOverflowY',
        invalidateScrollY: 'invalidateScrollY',
        perpendicularMargins: 'tb',

        
        getWidth: 'getWidth',
        getHeight: 'getHeight',
        setWidth: 'setWidth',
        setHeight: 'setHeight',
        gotWidth: 'gotWidth',
        gotHeight: 'gotHeight',
        setContentWidth: 'setContentWidth',
        setContentHeight: 'setContentHeight',
        setWidthInDom: 'setWidthInDom',
        setHeightInDom: 'setHeightInDom',
        getScrollLeft: 'getScrollLeft',
        setScrollLeft: 'setScrollLeft',
        scrollTo: 'scrollTo'
    },

    sizePolicy: {
        flex: {
            '': {
                readsWidth : 0,
                readsHeight: 1,
                setsWidth  : 1,
                setsHeight : 0
            },
            stretch: {
                readsWidth : 0,
                readsHeight: 0,
                setsWidth  : 1,
                setsHeight : 1
            },
            stretchmax: {
                readsWidth : 0,
                readsHeight: 1,
                setsWidth  : 1,
                setsHeight : 1
            }
        },
        '': {
            readsWidth : 1,
            readsHeight: 1,
            setsWidth  : 0,
            setsHeight : 0
        },
        stretch: {
            readsWidth : 1,
            readsHeight: 0,
            setsWidth  : 0,
            setsHeight : 1
        },
        stretchmax: {
            readsWidth : 1,
            readsHeight: 1,
            setsWidth  : 0,
            setsHeight : 1
        }
    }            
});


Ext.define('Ext.layout.container.VBox', {

    

    alias: ['layout.vbox'],
    extend:  Ext.layout.container.Box ,
    alternateClassName: 'Ext.layout.VBoxLayout',

    

    
    align : 'left', 

    
    
    
    constrainAlign: false,

    type: 'vbox',

    direction: 'vertical',

    horizontal: false,

    names: {
        
        beforeX: 'top',
        beforeScrollX: 'top',
        beforeScrollerSuffix: '-before-scroller',
        afterScrollerSuffix: '-after-scroller',
        leftCap: 'Top',
        afterX: 'bottom',
        width: 'height',
        contentWidth: 'contentHeight',
        minWidth: 'minHeight',
        maxWidth: 'maxHeight',
        widthCap: 'Height',
        widthModel: 'heightModel',
        widthIndex: 1,
        x: 'y',
        scrollLeft: 'scrollTop',
        overflowX: 'overflowY',
        hasOverflowX: 'hasOverflowY',
        invalidateScrollX: 'invalidateScrollY',
        parallelMargins: 'tb',

        
        center: 'center',
        beforeY: 'left',
        afterY: 'right',
        height: 'width',
        contentHeight: 'contentWidth',
        minHeight: 'minWidth',
        maxHeight: 'maxWidth',
        heightCap: 'Width',
        heightModel: 'widthModel',
        heightIndex: 0,
        y: 'x',
        overflowY: 'overflowX',
        hasOverflowY: 'hasOverflowX',
        invalidateScrollY: 'invalidateScrollX',
        perpendicularMargins: 'lr',

        
        getWidth: 'getHeight',
        getHeight: 'getWidth',
        setWidth: 'setHeight',
        setHeight: 'setWidth',
        gotWidth: 'gotHeight',
        gotHeight: 'gotWidth',
        setContentWidth: 'setContentHeight',
        setContentHeight: 'setContentWidth',
        setWidthInDom: 'setHeightInDom',
        setHeightInDom: 'setWidthInDom',
        getScrollLeft: 'getScrollTop',
        setScrollLeft: 'setScrollTop',
        scrollTo: 'scrollTo'
    },

    sizePolicy: {
        flex: {
            '': {
                readsWidth : 1,
                readsHeight: 0,
                setsWidth  : 0,
                setsHeight : 1
            },
            stretch: {
                readsWidth : 0,
                readsHeight: 0,
                setsWidth  : 1,
                setsHeight : 1
            },
            stretchmax: {
                readsWidth : 1,
                readsHeight: 0,
                setsWidth  : 1,
                setsHeight : 1
            }
        },
        '': {
            readsWidth : 1,
            readsHeight: 1,
            setsWidth  : 0,
            setsHeight : 0
        },
        stretch: {
            readsWidth : 0,
            readsHeight: 1,
            setsWidth  : 1,
            setsHeight : 0
        },
        stretchmax: {
            readsWidth : 1,
            readsHeight: 1,
            setsWidth  : 1,
            setsHeight : 0
        }
    }
});


Ext.define('Ext.toolbar.Toolbar', {
    extend:  Ext.container.Container ,
               
                           
                                    
                                   
      
           
                               
      
    alias: 'widget.toolbar',
    alternateClassName: 'Ext.Toolbar',

    
    isToolbar: true,
    baseCls  : Ext.baseCSSPrefix + 'toolbar',
    ariaRole : 'toolbar',

    defaultType: 'button',

    
    vertical: false,

    

    
    enableOverflow: false,

    
    menuTriggerCls: Ext.baseCSSPrefix + 'toolbar-more-icon',

    
    
    
    trackMenus: true,

    itemCls: Ext.baseCSSPrefix + 'toolbar-item',

    statics: {
        shortcuts: {
            '-' : 'tbseparator',
            ' ' : 'tbspacer'
        },

        shortcutsHV: {
            
            0: {
                '->': { xtype: 'tbfill', height: 0 }
            },
            
            1: {
                '->': { xtype: 'tbfill', width: 0 }
            }
        }
    },

    initComponent: function() {
        var me = this;

        
        if (!me.layout && me.enableOverflow) {
            me.layout = { overflowHandler: 'Menu' };
        }

        if (me.dock === 'right' || me.dock === 'left') {
            me.vertical = true;
        }

        me.layout = Ext.applyIf(Ext.isString(me.layout) ? {
            type: me.layout
        } : me.layout || {}, {
            type: me.vertical ? 'vbox' : 'hbox',
            align: me.vertical ? 'stretchmax' : 'middle'
        });

        if (me.vertical) {
            me.addClsWithUI('vertical');
        }

        
        if (me.ui === 'footer') {
            me.ignoreBorderManagement = true;
        }

        me.callParent();

        
        me.addEvents('overflowchange');
    },

    getRefItems: function(deep) {
        var me = this,
            items = me.callParent(arguments),
            layout = me.layout,
            handler;

        if (deep && me.enableOverflow) {
            handler = layout.overflowHandler;
            if (handler && handler.menu) {
                items = items.concat(handler.menu.getRefItems(deep));
            }
        }
        return items;
    },

    
    
    

    
    lookupComponent: function(c) {
        var args = arguments;
        if (typeof c == 'string') {
            var T = Ext.toolbar.Toolbar,
                shortcut = T.shortcutsHV[this.vertical ? 1 : 0][c] || T.shortcuts[c];

            if (typeof shortcut == 'string') {
                c = {
                    xtype: shortcut
                };
            } else if (shortcut) {
                c = Ext.apply({}, shortcut);
            } else {
                c = {
                    xtype: 'tbtext',
                    text: c
                };
            }

            this.applyDefaults(c);
            
            
            args = [c];
        }

        return this.callParent(args);
    },

    
    applyDefaults: function(c) {
        if (!Ext.isString(c)) {
            c = this.callParent(arguments);
        }
        return c;
    },

    
    trackMenu: function(item, remove) {
        if (this.trackMenus && item.menu) {
            var method = remove ? 'mun' : 'mon',
                me = this;

            me[method](item, 'mouseover', me.onButtonOver, me);
            me[method](item, 'menushow', me.onButtonMenuShow, me);
            me[method](item, 'menuhide', me.onButtonMenuHide, me);
        }
    },

    
    onBeforeAdd: function(component) {
        var me = this,
            isButton = component.isButton;

        if (isButton && me.defaultButtonUI && component.ui === 'default' &&
            !component.hasOwnProperty('ui')) {
            component.ui = me.defaultButtonUI;
        } else if ((isButton || component.isFormField) && me.ui !== 'footer') {
            component.ui = component.ui + '-toolbar';
            component.addCls(component.baseCls + '-toolbar');
        }

        
        if (component instanceof Ext.toolbar.Separator) {
            component.setUI((me.vertical) ? 'vertical' : 'horizontal');
        }

        me.callParent(arguments);
    },

    
    onAdd: function(component) {
        this.callParent(arguments);
        this.trackMenu(component);
    },
    
    
    onRemove: function(c) {
        this.callParent(arguments);
        this.trackMenu(c, true);
    },
    
    getChildItemsToDisable: function() {
        return this.items.getRange();   
    },

    
    onButtonOver: function(btn){
        if (this.activeMenuBtn && this.activeMenuBtn != btn) {
            this.activeMenuBtn.hideMenu();
            btn.showMenu();
            this.activeMenuBtn = btn;
        }
    },

    
    onButtonMenuShow: function(btn) {
        this.activeMenuBtn = btn;
    },

    
    onButtonMenuHide: function(btn) {
        delete this.activeMenuBtn;
    }
});


Ext.define('Ext.layout.component.Dock', {

    

    extend:  Ext.layout.component.Component ,

    alias: 'layout.dock',

    alternateClassName: 'Ext.layout.component.AbstractDock',

    

    type: 'dock',
    
    horzAxisProps: {
        name: 'horz',
        oppositeName: 'vert',
        dockBegin: 'left',
        dockEnd: 'right',
        horizontal: true,
        marginBegin: 'margin-left',
        maxSize: 'maxWidth',
        minSize: 'minWidth',
        pos: 'x',
        setSize: 'setWidth',
        shrinkWrapDock: 'shrinkWrapDockWidth',
        size: 'width',
        sizeModel: 'widthModel'
    },

    vertAxisProps: {
        name: 'vert',
        oppositeName: 'horz',
        dockBegin: 'top',
        dockEnd: 'bottom',
        horizontal: false,
        marginBegin: 'margin-top',
        maxSize: 'maxHeight',
        minSize: 'minHeight',
        pos: 'y',
        setSize: 'setHeight',
        shrinkWrapDock: 'shrinkWrapDockHeight',
        size: 'height',
        sizeModel: 'heightModel'
    },

    initializedBorders: -1,

    horizontalCollapsePolicy: { width: true, x: true },

    verticalCollapsePolicy: { height: true, y: true },

    finishRender: function () {
        var me = this,
            target, items;

        me.callParent();

        target = me.getRenderTarget();
        items = me.getDockedItems();

        me.finishRenderItems(target, items);
    },

    isItemBoxParent: function (itemContext) {
        return true;
    },

    isItemShrinkWrap: function (item) {
        return true;
    },

    noBorderClasses: [
        Ext.baseCSSPrefix + 'docked-noborder-top',
        Ext.baseCSSPrefix + 'docked-noborder-right',
        Ext.baseCSSPrefix + 'docked-noborder-bottom',
        Ext.baseCSSPrefix + 'docked-noborder-left'
    ],

    noBorderClassesSides: {
        top: Ext.baseCSSPrefix + 'docked-noborder-top',
        right: Ext.baseCSSPrefix + 'docked-noborder-right',
        bottom: Ext.baseCSSPrefix + 'docked-noborder-bottom',
        left: Ext.baseCSSPrefix + 'docked-noborder-left'
    },

    borderWidthProps: {
        top: 'border-top-width',
        right: 'border-right-width',
        bottom: 'border-bottom-width',
        left: 'border-left-width'
    },

    handleItemBorders: function() {
        var me = this,
            owner = me.owner,
            borders, docked,
            lastItems = me.lastDockedItems,
            oldBorders = me.borders,
            currentGeneration = owner.dockedItems.generation,
            noBorderClassesSides = me.noBorderClassesSides,
            borderWidthProps = me.borderWidthProps,
            i, ln, item, dock, side,
            collapsed = me.collapsed;

        if (me.initializedBorders == currentGeneration || (owner.border && !owner.manageBodyBorders)) {
            return;
        }

        me.initializedBorders = currentGeneration;

        
        me.collapsed = false;
        me.lastDockedItems = docked = me.getLayoutItems();
        me.collapsed = collapsed;

        borders = { top: [], right: [], bottom: [], left: [] };

        for (i = 0, ln = docked.length; i < ln; i++) {
            item = docked[i];
            dock = item.dock;

            if (item.ignoreBorderManagement) {
                continue;
            }

            if (!borders[dock].satisfied) {
                borders[dock].push(item);
                borders[dock].satisfied = true;
            }

            if (!borders.top.satisfied && dock !== 'bottom') {
                borders.top.push(item);
            }
            if (!borders.right.satisfied && dock !== 'left') {
                borders.right.push(item);
            }
            if (!borders.bottom.satisfied && dock !== 'top') {
                borders.bottom.push(item);
            }
            if (!borders.left.satisfied && dock !== 'right') {
                borders.left.push(item);
            }
        }

        if (lastItems) {
            for (i = 0, ln = lastItems.length; i < ln; i++) {
                item = lastItems[i];
                if (!item.isDestroyed && !item.ignoreBorderManagement && !owner.manageBodyBorders) {
                    item.removeCls(me.noBorderClasses);
                }
            }
        }

        if (oldBorders) {
            for (side in oldBorders) {
                if (owner.manageBodyBorders && oldBorders[side].satisfied) {
                    owner.setBodyStyle(borderWidthProps[side], '');
                }
            }
        }

        for (side in borders) {
            ln = borders[side].length;
            if (!owner.manageBodyBorders) {
                for (i = 0; i < ln; i++) {
                    borders[side][i].addCls(noBorderClassesSides[side]);
                }
                if ((!borders[side].satisfied && !owner.bodyBorder) || owner.bodyBorder === false) {
                    owner.addBodyCls(noBorderClassesSides[side]);
                }
            }
            else if (borders[side].satisfied) {
                owner.setBodyStyle(borderWidthProps[side], '1px');
            }
        }

        me.borders = borders;
    },

    beforeLayoutCycle: function (ownerContext) {
        var me = this,
            owner = me.owner,
            shrinkWrap = me.sizeModels.shrinkWrap,
            shrinkWrapDock = owner.shrinkWrapDock,
            collapsedHorz, collapsedVert;

        if (owner.collapsed) {
            if (owner.collapsedVertical()) {
                collapsedVert = true;
                ownerContext.measureDimensions = 1;
            } else {
                collapsedHorz = true;
                ownerContext.measureDimensions = 2;
            }
        }

        ownerContext.collapsedVert = collapsedVert;
        ownerContext.collapsedHorz = collapsedHorz;

        
        
        
        
        if (collapsedVert) {
            ownerContext.heightModel = shrinkWrap;
        } else if (collapsedHorz) {
            ownerContext.widthModel = shrinkWrap;
        }
        
        shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0);
        ownerContext.shrinkWrapDockHeight = (shrinkWrapDock & 1) && ownerContext.heightModel.shrinkWrap;
        ownerContext.shrinkWrapDockWidth = (shrinkWrapDock & 2) && ownerContext.widthModel.shrinkWrap;
    },

    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            docked = me.getLayoutItems(),
            layoutContext = ownerContext.context,
            dockedItemCount = docked.length,
            dockedItems, i, item, itemContext, offsets,
            collapsed, dock;

        me.callParent(arguments);

        
        
        collapsed = owner.getCollapsed();
        if (collapsed !== me.lastCollapsedState && Ext.isDefined(me.lastCollapsedState)) {
            
            if (me.owner.collapsed) {
                ownerContext.isCollapsingOrExpanding = 1;
                
                owner.addClsWithUI(owner.collapsedCls);
            } else {
                ownerContext.isCollapsingOrExpanding = 2;
                
                owner.removeClsWithUI(owner.collapsedCls);
                ownerContext.lastCollapsedState = me.lastCollapsedState;
            }
        }
        me.lastCollapsedState = collapsed;

        ownerContext.dockedItems = dockedItems = [];

        for (i = 0; i < dockedItemCount; i++) {
            item = docked[i];
            if (item.rendered) {
                dock = item.dock;
                itemContext = layoutContext.getCmp(item);
                itemContext.dockedAt = { x: 0, y: 0 };
                itemContext.offsets = offsets = Ext.Element.parseBox(item.offsets || 0);
                itemContext.horizontal = dock == 'top' || dock == 'bottom';
                offsets.width = offsets.left + offsets.right;
                offsets.height = offsets.top + offsets.bottom;
                dockedItems.push(itemContext);
            }
        }

        ownerContext.bodyContext = ownerContext.getEl('body');
    },

    beginLayoutCycle: function(ownerContext) {
        var me = this,
            docked = ownerContext.dockedItems,
            len = docked.length,
            owner = me.owner,
            frameBody = owner.frameBody,
            lastHeightModel = me.lastHeightModel,
            i, item, dock;

        me.callParent(arguments);

        if (me.owner.manageHeight) {
            
            
            if (me.lastBodyDisplay) {
                owner.body.dom.style.display = me.lastBodyDisplay = '';
            }
        } else {
            
            
            
            if (me.lastBodyDisplay !== 'inline-block') {
                owner.body.dom.style.display = me.lastBodyDisplay = 'inline-block';
            }

            if (lastHeightModel && lastHeightModel.shrinkWrap &&
                        !ownerContext.heightModel.shrinkWrap) {
                owner.body.dom.style.marginBottom = '';
            }
        }

        if (ownerContext.widthModel.auto) {
            if (ownerContext.widthModel.shrinkWrap) {
                owner.el.setWidth(null);
            }
            owner.body.setWidth(null);
            if (frameBody) {
                frameBody.setWidth(null);
            }
        }
        if (ownerContext.heightModel.auto) {
            owner.body.setHeight(null);
            
            if (frameBody) {
                frameBody.setHeight(null);
            }
        }

        
        
        if (ownerContext.collapsedVert) {
            ownerContext.setContentHeight(0);
        } else if (ownerContext.collapsedHorz) {
            ownerContext.setContentWidth(0);
        }

        
        
        for (i = 0; i < len; i++) {
            item = docked[i].target;
            dock = item.dock;

            if (dock == 'right') {
                item.setLocalX(0);
            } else if (dock != 'left') {
                continue;
            }

            
        }
    },

    calculate: function (ownerContext) {
        var me = this,
            measure = me.measureAutoDimensions(ownerContext, ownerContext.measureDimensions),
            state = ownerContext.state,
            horzDone = state.horzDone,
            vertDone = state.vertDone,
            bodyContext = ownerContext.bodyContext,
            framing, horz, vert, forward, backward;

        
        ownerContext.borderInfo  || ownerContext.getBorderInfo();
        ownerContext.paddingInfo || ownerContext.getPaddingInfo();
        ownerContext.frameInfo   || ownerContext.getFrameInfo();
        bodyContext.borderInfo   || bodyContext.getBorderInfo();
        bodyContext.paddingInfo  || bodyContext.getPaddingInfo();

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        if (!ownerContext.frameBorder) {
            if (!(framing = ownerContext.framing)) {
                ownerContext.frameBorder = ownerContext.borderInfo;
                ownerContext.framePadding = ownerContext.paddingInfo;
            } else {
                
                ownerContext.frameBorder = framing.border;
                ownerContext.framePadding = framing.padding;
            }
        }

        
        
        horz = !horzDone &&
               me.createAxis(ownerContext, measure.contentWidth, ownerContext.widthModel,
                             me.horzAxisProps, ownerContext.collapsedHorz);
        vert = !vertDone &&
               me.createAxis(ownerContext, measure.contentHeight, ownerContext.heightModel,
                             me.vertAxisProps, ownerContext.collapsedVert);

        
        
        
        
        
        
        
        
        for (forward = 0, backward = ownerContext.dockedItems.length; backward--; ++forward) {
            if (horz) {
                me.dockChild(ownerContext, horz, backward, forward);
            }
            if (vert) {
                me.dockChild(ownerContext, vert, backward, forward);
            }
        }
        
        if (horz && me.finishAxis(ownerContext, horz)) {
            state.horzDone = horzDone = horz;
        }
        
        if (vert && me.finishAxis(ownerContext, vert)) {
            state.vertDone = vertDone = vert;
        }

        
        
        if (horzDone && vertDone && me.finishConstraints(ownerContext, horzDone, vertDone)) {
            
            
            
            me.finishPositions(ownerContext, horzDone, vertDone);
        } else {
            me.done = false;
        }
    },

    
    createAxis: function (ownerContext, contentSize, sizeModel, axisProps, collapsedAxis) {
        var me = this,
            begin = 0,
            owner = me.owner,
            maxSize = owner[axisProps.maxSize],
            minSize = owner[axisProps.minSize] || 0,
            dockBegin = axisProps.dockBegin,
            dockEnd = axisProps.dockEnd,
            posProp = axisProps.pos,
            sizeProp = axisProps.size,
            hasMaxSize = maxSize != null, 
            shrinkWrap = sizeModel.shrinkWrap,
            bodyContext, framing, padding, end;

        if (shrinkWrap) {
            
            
            if (collapsedAxis) {
                end = 0;
            } else {
                bodyContext = ownerContext.bodyContext;
                end = contentSize + bodyContext.borderInfo[sizeProp];
            }
        } else {
            framing = ownerContext.frameBorder;
            padding = ownerContext.framePadding;

            begin = framing[dockBegin] + padding[dockBegin];
            end = ownerContext.getProp(sizeProp) - (framing[dockEnd] + padding[dockEnd]);
        }

        return {
            shrinkWrap: sizeModel.shrinkWrap,
            sizeModel: sizeModel,
            
            initialBegin: begin,
            begin: begin,
            end: end,
            collapsed: collapsedAxis,
            horizontal: axisProps.horizontal,
            ignoreFrameBegin: null,
            ignoreFrameEnd: null,
            initialSize: end - begin,
            maxChildSize: 0,
            hasMinMaxConstraints: (minSize || hasMaxSize) && sizeModel.shrinkWrap,
            minSize: minSize,
            maxSize: hasMaxSize ? maxSize : 1e9,
            bodyPosProp: me.owner.manageHeight ? posProp : axisProps.marginBegin,
            dockBegin: dockBegin,    
            dockEnd: dockEnd,        
            posProp: posProp,        
            sizeProp: sizeProp,      
            setSize: axisProps.setSize,
            shrinkWrapDock: ownerContext[axisProps.shrinkWrapDock],
            sizeModelName: axisProps.sizeModel,
            dockedPixelsEnd: 0
        };
    },

    
    dockChild: function (ownerContext, axis, backward, forward) {
        var me = this,
            itemContext = ownerContext.dockedItems[axis.shrinkWrap ? backward : forward],
            item = itemContext.target,
            dock = item.dock, 
            sizeProp = axis.sizeProp,
            pos, size;

        if (item.ignoreParentFrame && ownerContext.isCollapsingOrExpanding) {
            
            
            itemContext.clearMarginCache();
        }

        itemContext.marginInfo || itemContext.getMarginInfo(); 

        if (dock == axis.dockBegin) {
            if (axis.shrinkWrap) {
                pos = me.dockOutwardBegin(ownerContext, itemContext, item, axis);
            } else {
                pos = me.dockInwardBegin(ownerContext, itemContext, item, axis);
            }
        } else if (dock == axis.dockEnd) {
            if (axis.shrinkWrap) {
                pos = me.dockOutwardEnd(ownerContext, itemContext, item, axis);
            } else {
                pos = me.dockInwardEnd(ownerContext, itemContext, item, axis);
            }
        } else {
            if (axis.shrinkWrapDock) {
                
                
                size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
                axis.maxChildSize = Math.max(axis.maxChildSize, size);
                pos = 0;
            } else {
                pos = me.dockStretch(ownerContext, itemContext, item, axis);
            }
        }

        itemContext.dockedAt[axis.posProp] = pos;
    },

    
    dockInwardBegin: function (ownerContext, itemContext, item, axis) {
        var pos = axis.begin,
            sizeProp = axis.sizeProp,
            ignoreParentFrame = item.ignoreParentFrame,
            delta,
            size, 
            dock;

        if (ignoreParentFrame) {
            axis.ignoreFrameBegin = itemContext;
            dock = item.dock;

            
            delta = ownerContext.frameBorder[dock];

            
            pos -= delta + ownerContext.framePadding[dock];
        }

        if (!item.overlay) {
            size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
            axis.begin += size;
            if (ignoreParentFrame) {
                axis.begin -= delta;
            }
        }

        return pos;
    },

    
    dockInwardEnd: function (ownerContext, itemContext, item, axis) {
        var sizeProp = axis.sizeProp,
            size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp],
            pos = axis.end - size,
            frameEnd;

        if (!item.overlay) {
            axis.end = pos;
        }

        if (item.ignoreParentFrame) {
            axis.ignoreFrameEnd = itemContext;
            frameEnd = ownerContext.frameBorder[item.dock];
            pos += frameEnd + ownerContext.framePadding[item.dock];
            axis.end += frameEnd;
        }

        return pos;
    },

    
    dockOutwardBegin: function (ownerContext, itemContext, item, axis) {
        var pos = axis.begin,
            sizeProp = axis.sizeProp,
            size;

        if (axis.collapsed) {
            axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext;
        } else if (item.ignoreParentFrame) {
            axis.ignoreFrameBegin = itemContext;
        }
        
        

        if (!item.overlay) {
            size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];
            pos -= size;
            axis.begin = pos;
        }

        return pos;
    },

    
    dockOutwardEnd: function (ownerContext, itemContext, item, axis) {
        var pos = axis.end,
            sizeProp = axis.sizeProp,
            size;

        size = itemContext.getProp(sizeProp) + itemContext.marginInfo[sizeProp];

        if (axis.collapsed) {
            axis.ignoreFrameBegin = axis.ignoreFrameEnd = itemContext;
        } else if (item.ignoreParentFrame) {
            axis.ignoreFrameEnd = itemContext;
        }
        
        

        if (!item.overlay) {
            axis.end = pos + size;
            axis.dockedPixelsEnd += size;
        }

        return pos;
    },

    
    dockStretch: function (ownerContext, itemContext, item, axis) {
        var dock = item.dock, 
            sizeProp = axis.sizeProp, 
            horizontal = dock == 'top' || dock == 'bottom',
            border = ownerContext.frameBorder,
            offsets = itemContext.offsets,
            padding = ownerContext.framePadding,
            endProp = horizontal ? 'right' : 'bottom',
            startProp = horizontal ? 'left' : 'top',
            pos = axis.begin + offsets[startProp],
            margin, size;

        if (item.stretch !== false) {
            size = axis.end - pos - offsets[endProp];

            if (item.ignoreParentFrame) {
                
                
                
                
                pos -= padding[startProp] + border[startProp];
                size += padding[sizeProp] + border[sizeProp];
            }

            margin = itemContext.marginInfo;
            size -= margin[sizeProp];

            itemContext[axis.setSize](size);
        }

        return pos;
    },

    
    finishAxis: function (ownerContext, axis) {
        
        
        
        if (isNaN(axis.maxChildSize)) {
            return false;
        }
        
        var axisBegin = axis.begin,
            size = axis.end - axisBegin,
            collapsed = axis.collapsed,
            setSizeMethod = axis.setSize,
            beginName = axis.dockBegin, 
            endName = axis.dockEnd, 
            padding = ownerContext.framePadding,
            border = ownerContext.frameBorder,
            borderBegin = border[beginName],
            framing = ownerContext.framing,
            framingBegin = framing && framing[beginName],
            
            paddingBegin = collapsed ? 0 : padding[beginName],
            sizeProp = axis.sizeProp,
            ignoreFrameBegin = axis.ignoreFrameBegin,
            ignoreFrameEnd = axis.ignoreFrameEnd,
            bodyContext = ownerContext.bodyContext,
            extraPaddingBegin = Math.max(borderBegin + paddingBegin - framingBegin, 0),
            bodyPos, bodySize, delta, dirty;

        if (axis.shrinkWrap) {
            
            
            

            bodySize = axis.initialSize;

            if (framing) {
                
                
                
                
                
                
                

                delta = -axisBegin + borderBegin + paddingBegin;
                bodyPos = delta - framingBegin - extraPaddingBegin;
            } else {
                bodyPos = -axisBegin;
                delta = bodyPos + paddingBegin;
            }

            if (!collapsed) {
                size += padding[sizeProp];
            }

            if (ignoreFrameBegin) {
                
                
                
                delta -= borderBegin;
                bodyPos -= borderBegin;

                
                
                
                
                
                
                ignoreFrameBegin.dockedAt[axis.posProp] -= paddingBegin;
            } else {
                size += borderBegin;
            }

            if (collapsed) {
                
                
            } else if (ignoreFrameEnd) {
                
                
                
                ignoreFrameEnd.dockedAt[axis.posProp] += padding[endName];
            } else {
                size += border[endName];
            }

            axis.size = size; 

            if (!axis.horizontal && !this.owner.manageHeight) {
                
                
                dirty = false;
            }
        } else {
            
            
            if (framing) {
                
                
                delta = 0;
                bodyPos = axisBegin - framingBegin - extraPaddingBegin;
            } else {
                delta = -borderBegin;
                bodyPos = axisBegin - paddingBegin - borderBegin;
            }

            
            bodySize = size;
        }

        axis.delta = delta;
        bodyContext[setSizeMethod](bodySize, dirty);
        bodyContext.setProp(axis.bodyPosProp, bodyPos);

        return !isNaN(size);
    },
    
    beforeInvalidateShrinkWrapDock: function(itemContext, options){
        var sizeModelName = options.axis.sizeModelName;
        if (!itemContext[sizeModelName].constrainedMin) {
            
            
            itemContext[sizeModelName] = Ext.layout.SizeModel.calculated;
        }
    },
    
    afterInvalidateShrinkWrapDock: function(itemContext, options){
        var axis = options.axis,
            me = options.layout,
            pos;

        if (itemContext[axis.sizeModelName].calculated) {
            pos = me.dockStretch(options.ownerContext, itemContext, itemContext.target, axis);
            itemContext.setProp(axis.posProp, axis.delta + pos);
        }
    },
    
    
    finishConstraints: function (ownerContext, horz, vert) {
        var me = this,
            sizeModels = me.sizeModels,
            publishWidth = horz.shrinkWrap,
            publishHeight = vert.shrinkWrap,
            owner = me.owner,
            dirty, height, width, heightModel, widthModel, size, 
            minSize, maxSize, maxChildSize, desiredSize;

        
        
        
        
        
        if (publishWidth) {
            size = horz.size;
            minSize = horz.collapsed ? 0 : horz.minSize;
            maxSize = horz.maxSize;
            maxChildSize = horz.maxChildSize;
            desiredSize = Math.max(size, maxChildSize);

            if (desiredSize > maxSize) {
                widthModel = sizeModels.constrainedMax;
                width = maxSize;
            } else if (desiredSize < minSize) {
                widthModel = sizeModels.constrainedMin;
                width = minSize;
            } else if (size < maxChildSize) {
                widthModel = sizeModels.constrainedDock;
                owner.dockConstrainedWidth = width = maxChildSize;
            } else {
                width = size;
            }
        }

        if (publishHeight) {
            size = vert.size;
            minSize = vert.collapsed ? 0 : vert.minSize;
            maxSize = vert.maxSize;
            maxChildSize = vert.maxChildSize;
            
            
            desiredSize = Math.max(size, maxChildSize + size - vert.initialSize);

            if (desiredSize > maxSize) {
                heightModel = sizeModels.constrainedMax;
                height = maxSize;
            } else if (desiredSize < minSize) {
                heightModel = sizeModels.constrainedMin;
                height = minSize;
            } else if (size < maxChildSize) {
                heightModel = sizeModels.constrainedDock;
                owner.dockConstrainedHeight = height = maxChildSize;
            } else {
                if (!ownerContext.collapsedVert && !owner.manageHeight) {
                    
                    
                    dirty = false;

                    
                    ownerContext.bodyContext.setProp('margin-bottom', vert.dockedPixelsEnd);
                }

                height = size;
            }
        }

        

        if (widthModel || heightModel) {
            
            
            if (widthModel && heightModel &&
                        widthModel.constrainedMax &&  heightModel.constrainedByMin) {
                ownerContext.invalidate({ widthModel: widthModel });
                return false;
            }

            
            
            
            if (!ownerContext.widthModel.calculatedFromShrinkWrap &&
                        !ownerContext.heightModel.calculatedFromShrinkWrap) {
                
                ownerContext.invalidate({ widthModel: widthModel, heightModel: heightModel });
                return false;
            }

            
            
            
        } else {
            
            
            me.invalidateAxes(ownerContext, horz, vert);
            
        }

        

        if (publishWidth) {
            ownerContext.setWidth(width);
            if (widthModel) {
                ownerContext.widthModel = widthModel; 
            }
        }
        if (publishHeight) {
            ownerContext.setHeight(height, dirty);
            if (heightModel) {
                ownerContext.heightModel = heightModel; 
            }
        }

        return true;
    },
    
     
    invalidateAxes: function(ownerContext, horz, vert){
        var before = this.beforeInvalidateShrinkWrapDock,
            after = this.afterInvalidateShrinkWrapDock,
            horzSize = horz.end - horz.begin,
            vertSize = vert.initialSize,
            invalidateHorz = horz.shrinkWrapDock && horz.maxChildSize < horzSize,
            invalidateVert = vert.shrinkWrapDock && vert.maxChildSize < vertSize,
            dockedItems, len, i, itemContext, itemSize, isHorz, axis, sizeProp;

        if (invalidateHorz || invalidateVert) {
            if (invalidateVert) {
                
                
                vert.begin = vert.initialBegin;
                vert.end = vert.begin + vert.initialSize;
            }
            dockedItems = ownerContext.dockedItems;
            for (i = 0, len = dockedItems.length; i < len; ++i) {
                itemContext = dockedItems[i];
                isHorz = itemContext.horizontal;
                axis = null;
                if (invalidateHorz && isHorz) {
                    sizeProp = horz.sizeProp;
                    itemSize = horzSize;
                    axis = horz;
                } else if (invalidateVert && !isHorz) {
                    sizeProp = vert.sizeProp;
                    itemSize = vertSize;
                    axis = vert;
                }
                
                if (axis) {
                    
                    itemSize -= itemContext.getMarginInfo()[sizeProp];
                    if (itemSize !== itemContext.props[sizeProp]) {
                        itemContext.invalidate({
                            before: before,
                            after: after,
                            axis: axis,
                            ownerContext: ownerContext,
                            layout: this
                        });
                    }
                }
            }
        }
    },

    
    finishPositions: function (ownerContext, horz, vert) {
        var dockedItems = ownerContext.dockedItems,
            length = dockedItems.length,
            deltaX = horz.delta,
            deltaY = vert.delta,
            index, itemContext;

        for (index = 0; index < length; ++index) {
            itemContext = dockedItems[index];

            itemContext.setProp('x', deltaX + itemContext.dockedAt.x);
            itemContext.setProp('y', deltaY + itemContext.dockedAt.y);
        }
    },

    finishedLayout: function(ownerContext) {
        var me = this,
            target = ownerContext.target;

        me.callParent(arguments);

        if (!ownerContext.animatePolicy) {
            if (ownerContext.isCollapsingOrExpanding === 1) {
                target.afterCollapse(false);
            } else if (ownerContext.isCollapsingOrExpanding === 2) {
                target.afterExpand(false);
            }
        }
    },

    getAnimatePolicy: function(ownerContext) {
        var me = this,
            lastCollapsedState, policy;

        if (ownerContext.isCollapsingOrExpanding == 1) {
            lastCollapsedState = me.lastCollapsedState;
        } else if (ownerContext.isCollapsingOrExpanding == 2) {
            lastCollapsedState = ownerContext.lastCollapsedState;
        }

        if (lastCollapsedState == 'left' || lastCollapsedState == 'right') {
            policy = me.horizontalCollapsePolicy;
        } else if (lastCollapsedState == 'top' || lastCollapsedState == 'bottom') {
            policy = me.verticalCollapsePolicy;
        }

        return policy;
    },

    
    getDockedItems: function(order, beforeBody) {
        var me = this,
            renderedOnly = (order === 'visual'),
            all = renderedOnly ? Ext.ComponentQuery.query('[rendered]', me.owner.dockedItems.items) : me.owner.dockedItems.items,
            sort = all && all.length && order !== false,
            renderOrder,
            dock, dockedItems, i, isBefore, length;

        if (beforeBody == null) {
            dockedItems = sort && !renderedOnly ? all.slice() : all;
        } else {
            dockedItems = [];

            for (i = 0, length = all.length; i < length; ++i) {
                dock = all[i].dock;
                isBefore = (dock == 'top' || dock == 'left');
                if (beforeBody ? isBefore : !isBefore) {
                    dockedItems.push(all[i]);
                }
            }

            sort = sort && dockedItems.length;
        }

        if (sort) {
            renderOrder = (order = order || 'render') == 'render';
            Ext.Array.sort(dockedItems, function(a, b) {
                var aw,
                    bw;

                
                
                if (renderOrder && ((aw = me.owner.dockOrder[a.dock]) !== (bw = me.owner.dockOrder[b.dock]))) {

                    
                    if (!(aw + bw)) {
                        return aw - bw;
                    }
                }

                aw = me.getItemWeight(a, order);
                bw = me.getItemWeight(b, order);
                if ((aw !== undefined) && (bw !== undefined)) {
                    return aw - bw;
                }
                return 0;
            });
        }

        return dockedItems || [];
    },

    getItemWeight: function (item, order) {
        var weight = item.weight || this.owner.defaultDockWeights[item.dock];
        return weight[order] || weight;
    },

    
    getLayoutItems : function() {
        var me = this,
            items,
            itemCount,
            item,
            i,
            result;

        if (me.owner.collapsed) {
            result = me.owner.getCollapsedDockedItems();
        } else {
            items = me.getDockedItems('visual');
            itemCount = items.length;
            result = [];
            for (i = 0; i < itemCount; i++) {
                item = items[i];
                if (!item.hidden) {
                    result.push(item);
                }
            }
        }
        return result;
    },

    
    measureContentWidth: function (ownerContext) {
        var bodyContext = ownerContext.bodyContext;
        return bodyContext.el.getWidth() - bodyContext.getBorderInfo().width;
    },

    measureContentHeight: function (ownerContext) {
        var bodyContext = ownerContext.bodyContext;
        return bodyContext.el.getHeight() - bodyContext.getBorderInfo().height;
    },
    
    redoLayout: function(ownerContext) {
        var me = this,
            owner = me.owner;
        
        
        if (ownerContext.isCollapsingOrExpanding == 1) {
            if (owner.reExpander) {
                owner.reExpander.el.show();
            }
            
            owner.addClsWithUI(owner.collapsedCls);
            ownerContext.redo(true);
        } else if (ownerContext.isCollapsingOrExpanding == 2) {
            
            owner.removeClsWithUI(owner.collapsedCls);
            ownerContext.bodyContext.redo();
        } 
    },

    
    
    renderChildren: function() {
        var me = this,
            items = me.getDockedItems(),
            target = me.getRenderTarget();

        me.handleItemBorders();

        me.renderItems(items, target);
    },

    
    renderItems: function(items, target) {
        var me = this,
            dockedItemCount = items.length,
            itemIndex = 0,
            correctPosition = 0,
            staticNodeCount = 0,
            targetNodes = me.getRenderTarget().dom.childNodes,
            targetChildCount = targetNodes.length,
            i, j, targetChildNode, item;

        
        for (i = 0, j = 0; i < targetChildCount; i++) {
            targetChildNode = targetNodes[i];
            if (Ext.fly(targetChildNode).hasCls(Ext.baseCSSPrefix + 'resizable-handle')) {
                break;
            }
            for (j = 0; j < dockedItemCount; j++) {
                item = items[j];
                if (item.rendered && item.el.dom === targetChildNode) {
                    break;
                }
            }
            
            
            if (j === dockedItemCount) {
                staticNodeCount++;
            }
        }

        
        for (; itemIndex < dockedItemCount; itemIndex++, correctPosition++) {
            item = items[itemIndex];

            
            
            
            
            
            
            
            if (itemIndex === correctPosition && (item.dock === 'right' || item.dock === 'bottom')) {
                correctPosition += staticNodeCount;
            }

            
            if (item && !item.rendered) {
                me.renderItem(item, target, correctPosition);
            }
            else if (!me.isValidParent(item, target, correctPosition)) {
                me.moveItem(item, target, correctPosition);
            }
        }
    },

    undoLayout: function(ownerContext) {
        var me = this,
            owner = me.owner;
        
        
        if (ownerContext.isCollapsingOrExpanding == 1) {

            
            if (owner.reExpander) {
                owner.reExpander.el.hide();
            }
            
            owner.removeClsWithUI(owner.collapsedCls);
            ownerContext.undo(true);
        } else if (ownerContext.isCollapsingOrExpanding == 2) {
            
            owner.addClsWithUI(owner.collapsedCls);
            ownerContext.bodyContext.undo();
        } 
    },

    sizePolicy: {
        nostretch: {
            setsWidth: 0,
            setsHeight: 0
        },

        horz: { 
            shrinkWrap: {
                
                
                setsWidth: 1,
                setsHeight: 0,
                readsWidth: 1
            },
            stretch: {
                setsWidth: 1,
                setsHeight: 0
            }
        },

        vert: { 
            shrinkWrap: {
                setsWidth: 0,
                setsHeight: 1,
                readsHeight: 1
            },
            stretch: {
                setsWidth: 0,
                setsHeight: 1
            }
        },

        stretchV: {
            setsWidth: 0,
            setsHeight: 1
        },

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        

        autoStretchH: {
            readsWidth: 1,
            setsWidth: 1,
            setsHeight: 0
        },
        autoStretchV: {
            readsHeight: 1,
            setsWidth: 0,
            setsHeight: 1
        }
    },

    getItemSizePolicy: function (item, ownerSizeModel) {
        var me = this,
            policy = me.sizePolicy,
            shrinkWrapDock = me.owner.shrinkWrapDock,
            dock, vertical;

        if (item.stretch === false) {
            return policy.nostretch;
        }

        dock = item.dock;
        vertical = (dock == 'left' || dock == 'right');

        shrinkWrapDock = shrinkWrapDock === true ? 3 : (shrinkWrapDock || 0);
        if (vertical) {
            policy = policy.vert;
            shrinkWrapDock = shrinkWrapDock & 1;
        } else {
            policy = policy.horz;
            shrinkWrapDock = shrinkWrapDock & 2;
        }

        if (shrinkWrapDock) {
            
            if (!ownerSizeModel) {
                ownerSizeModel = me.owner.getSizeModel();
            }
            if (ownerSizeModel[vertical ? 'height' : 'width'].shrinkWrap) {
                return policy.shrinkWrap;
            }
        }

        return policy.stretch;
    },

    
    configureItem : function(item, pos) {
        this.callParent(arguments);

        item.addCls(Ext.baseCSSPrefix + 'docked');
        item.addClsWithUI(this.getDockCls(item.dock));
    },

    
    getDockCls: function(dock) {
        return 'docked-' + dock;
    },

    afterRemove : function(item) {
        this.callParent(arguments);
        if (this.itemCls) {
            item.el.removeCls(this.itemCls + '-' + item.dock);
        }
        var dom = item.el.dom;

        if (!item.destroying && dom) {
            dom.parentNode.removeChild(dom);
        }
        this.childrenChanged = true;
    },

    
    borderCollapseMap: {
        
    },

    
    getBorderCollapseTable: function () {
        var me = this,
            map = me.borderCollapseMap,
            owner = me.owner,
            baseCls = owner.baseCls,
            ui = owner.ui,
            table;

        map = map[baseCls] || (map[baseCls] = {});
        table = map[ui];

        if (!table) {
            baseCls += '-' + ui + '-outer-border-';
            map[ui] = table = [
                0,                  
                baseCls + 'l',      
                baseCls + 'b',      
                baseCls + 'bl',     
                baseCls + 'r',      
                baseCls + 'rl',     
                baseCls + 'rb',     
                baseCls + 'rbl',    
                baseCls + 't',      
                baseCls + 'tl',     
                baseCls + 'tb',     
                baseCls + 'tbl',    
                baseCls + 'tr',     
                baseCls + 'trl',    
                baseCls + 'trb',    
                baseCls + 'trbl'    
            ];
        }

        return table;
    }
});


Ext.define('Ext.panel.AbstractPanel', {

    

    extend:  Ext.container.Container ,

    mixins: {
        docking:  Ext.container.DockingContainer 
    },

                                                                                 

    

    
    baseCls : Ext.baseCSSPrefix + 'panel',

    

    

    

    

    
    isPanel: true,
    
    

     
    contentPaddingProperty: 'bodyPadding',
    
    
    shrinkWrapDock: false,

    componentLayout: 'dock',

    childEls: [
        'body'
    ],

    renderTpl: [
        
        '{% this.renderDockedItems(out,values,0); %}',
        
        
        
        
        
        
        
        
        
        (Ext.isIE7m || Ext.isIEQuirks) ? '<div style="position:relative"></div>' : '',
        '<div id="{id}-body" class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl>',
            ' {baseCls}-body-{ui}<tpl if="uiCls">',
                '<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl>',
            '</tpl>{childElCls}"',
            '<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
            '{%this.renderContainer(out,values);%}',
        '</div>',
        '{% this.renderDockedItems(out,values,1); %}'
    ],

    bodyPosProps: {
        x: 'x',
        y: 'y'
    },

    
    

    
    border: true,

    
    emptyArray: [],

    initComponent : function() {
        this.initBorderProps();
        this.callParent();
    },

    initBorderProps: function() {
        var me = this;

        if (me.frame && me.border && me.bodyBorder === undefined) {
            me.bodyBorder = false;
        }
        if (me.frame && me.border && (me.bodyBorder === false || me.bodyBorder === 0)) {
            me.manageBodyBorders = true;
        }
    },

    beforeDestroy: function(){
        this.destroyDockedItems();
        this.callParent();
    },

    
    initItems : function() {
        this.callParent();
        this.initDockingItems();
    },

    
    initRenderData: function() {
        var me = this,
            data = me.callParent();

        me.initBodyStyles();
        me.protoBody.writeTo(data);
        delete me.protoBody;

        return data;
    },

    
    getComponent: function(comp) {
        var component = this.callParent(arguments);
        if (component === undefined && !Ext.isNumber(comp)) {
            
            component = this.getDockedComponent(comp);
        }
        return component;
    },

    getProtoBody: function () {
        var me = this,
            body = me.protoBody;

        if (!body) {
            me.protoBody = body = new Ext.util.ProtoElement({
                cls: me.bodyCls,
                style: me.bodyStyle,
                clsProp: 'bodyCls',
                styleProp: 'bodyStyle',
                styleIsText: true
            });
        }

        return body;
    },

    
    initBodyStyles: function() {
        var me = this,
            body = me.getProtoBody();

        if (me.bodyPadding !== undefined) {
            if (me.layout.managePadding) {
                
                
                
                
                
                body.setStyle('padding', 0);
            } else {
                body.setStyle('padding', this.unitizeBox((me.bodyPadding === true) ? 5 : me.bodyPadding));
            }
        }
        me.initBodyBorder();
    },

    initBodyBorder: function() {
        var me = this;

        if (me.frame && me.bodyBorder) {
            if (!Ext.isNumber(me.bodyBorder)) {
                me.bodyBorder = 1;
            }
            me.getProtoBody().setStyle('border-width', this.unitizeBox(me.bodyBorder));
        }
    },

    getCollapsedDockedItems: function () {
        var me = this;
        return me.header === false || me.collapseMode == 'placeholder' ? me.emptyArray : [ me.getReExpander() ];
    },

    
    setBodyStyle: function(style, value) {
        var me = this,
            body = me.rendered ? me.body : me.getProtoBody();

        if (Ext.isFunction(style)) {
            style = style();
        }
        if (arguments.length == 1) {
            if (Ext.isString(style)) {
                style = Ext.Element.parseStyles(style);     
            }
            body.setStyle(style);
        } else {
            body.setStyle(style, value);
        }
        return me;
    },

    
    addBodyCls: function(cls) {
        var me = this,
            body = me.rendered ? me.body : me.getProtoBody();

        body.addCls(cls);
        return me;
    },

    
    removeBodyCls: function(cls) {
        var me = this,
            body = me.rendered ? me.body : me.getProtoBody();

        body.removeCls(cls);
        return me;
    },

    
    addUIClsToElement: function(cls) {
        var me = this,
            result = me.callParent(arguments);

        me.addBodyCls([Ext.baseCSSPrefix + cls, me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls]);
        return result;
    },

    
    removeUIClsFromElement: function(cls) {
        var me = this,
            result = me.callParent(arguments);

        me.removeBodyCls([Ext.baseCSSPrefix + cls, me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls]);
        return result;
    },

    
    addUIToElement: function() {
        var me = this;

        me.callParent(arguments);
        me.addBodyCls(me.baseCls + '-body-' + me.ui);
    },

    
    removeUIFromElement: function() {
        var me = this;

        me.callParent(arguments);
        me.removeBodyCls(me.baseCls + '-body-' + me.ui);
    },

    
    getTargetEl : function() {
        return this.body;
    },

    applyTargetCls: function(targetCls) {
        this.getProtoBody().addCls(targetCls);
    },

    getRefItems: function(deep) {
        var items = this.callParent(arguments);

        return this.getDockingRefItems(deep, items);
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);
        this.setupDockingRenderTpl(renderTpl);
    }
});


Ext.define('Ext.panel.Header', {
    extend:  Ext.container.Container ,
                                                                                     
    alias: 'widget.header',

    
    isHeader       : true,
    defaultType    : 'tool',
    indicateDrag   : false,
    weight         : -1,
    componentLayout: 'body',

    

    childEls: [
        'body'
    ],

    renderTpl: [
        '<div id="{id}-body" class="{headerCls}-body {baseCls}-body {bodyCls} {bodyTargetCls}',
        '<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl>"',
        '<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
            '{%this.renderContainer(out,values)%}',
        '</div>'
    ],

    headingTpl: [
        
        '<span id="{id}-textEl" class="{headerCls}-text {cls}-text {cls}-text-{ui}" unselectable="on">{title}</span>'
    ],

    shrinkWrap: 3,

    

    
    titlePosition: 0,

    

    

    

    
    headerCls: Ext.baseCSSPrefix + 'header',

    initComponent: function() {
        var me = this,
            hasPosition = me.hasOwnProperty('titlePosition'),
            items = me.items,
            titlePosition = hasPosition ? me.titlePosition : (items ? items.length : 0),
            uiClasses = [me.orientation, me.getDockName()],
            ownerCt = me.ownerCt;

        me.addEvents(
            
            'click',

            
            'dblclick'
        );

        me.indicateDragCls = me.headerCls + '-draggable';
        me.title = me.title || '&#160;';
        me.tools = me.tools || [];
        items = me.items = (items ? Ext.Array.slice(items) : []);
        me.orientation = me.orientation || 'horizontal';
        me.dock = (me.dock) ? me.dock : (me.orientation == 'horizontal') ? 'top' : 'left';

        if (ownerCt ? (!ownerCt.border && !ownerCt.frame) : !me.border) {
            uiClasses.push(me.orientation + '-noborder');
        }
        me.addClsWithUI(uiClasses);
        me.addCls([me.headerCls, me.headerCls + '-' + me.orientation]);

        if (me.indicateDrag) {
            me.addCls(me.indicateDragCls);
        }

        
        if (me.iconCls || me.icon || me.glyph) {
            me.initIconCmp();
            
            
            if (!hasPosition && !items.length) {
                ++titlePosition;
            }
            items.push(me.iconCmp);
        }

        
        me.titleCmp = new Ext.Component({
            ariaRole  : 'heading',
            focusable : false,
            noWrap    : true,
            flex      : 1,
            rtl       : me.rtl,
            id        : me.id + '_hd',
            style     : me.titleAlign ? ('text-align:' + me.titleAlign) : '',
            cls       : me.headerCls + '-text-container ' +
                        me.baseCls + '-text-container ' +
                        me.baseCls + '-text-container-' + me.ui,
            renderTpl : me.getTpl('headingTpl'),
            renderData: {
                title: me.title,
                cls  : me.baseCls,
                headerCls: me.headerCls,
                ui   : me.ui
            },
            childEls  : ['textEl'],
            autoEl: {
                
                unselectable: 'on'
            },
            listeners: {
                render: me.onTitleRender,
                scope: me
            }
        });
        me.layout = (me.orientation == 'vertical') ? {
            type : 'vbox',
            align: 'center',
            alignRoundingMethod: 'ceil'
        } : {
            type : 'hbox',
            align: 'middle',
            alignRoundingMethod: 'floor'
        };

        
        Ext.Array.push(items, me.tools);
        
        
        me.tools.length = 0;
        me.callParent();

        if (items.length < titlePosition) {
            titlePosition = items.length;
        }
        me.titlePosition = titlePosition;

        
        me.insert(titlePosition, me.titleCmp);

        me.on({
            dblclick: me.onDblClick,
            click: me.onClick,
            element: 'el',
            scope: me
        });
    },

    initIconCmp: function() {
        var me = this,
            cls = [me.headerCls + '-icon', me.baseCls + '-icon', me.iconCls],
            cfg;
       
        if (me.glyph) {
            cls.push(me.baseCls + '-glyph');
        }

        cfg = {
            focusable: false,
            src: Ext.BLANK_IMAGE_URL,
            cls: cls,
            baseCls: me.baseCls + '-icon',
            id: me.id + '-iconEl',
            iconCls: me.iconCls,
            glyph:  me.glyph
        };

        if (!Ext.isEmpty(me.icon)) {
            delete cfg.iconCls;
            cfg.src = me.icon;
        }

        me.iconCmp = new Ext.Img(cfg);
    },

    beforeRender: function() {
        this.protoEl.unselectable();
        this.callParent();
    },

    afterLayout: function() {
        var me = this,
            frameBR, frameTR, frameTL, xPos;

        if (me.orientation === 'vertical') {
            me.adjustTitlePosition();
            frameTR = me.frameTR;
            if (frameTR) {
                
                
                
                frameBR = me.frameBR;
                frameTL = me.frameTL;
                xPos = (me.getWidth() - frameTR.getPadding('r') -
                    ((frameTL) ? frameTL.getPadding('l') : me.el.getBorderWidth('l'))) + 'px';
                frameBR.setStyle('background-position-x', xPos);
                frameTR.setStyle('background-position-x', xPos);
            }
            if (Ext.isIE7 && Ext.isStrict && me.frame) {
                
                
                me.el.repaint();
            }
        }
    },

    beforeLayout: function () {
        this.callParent();
        this.syncBeforeAfterTitleClasses();
    },

    adjustTitlePosition: function() {
        var titleCmp = this.titleCmp,
            titleEl;

        if (!Ext.isIE9m && titleCmp) { 
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            titleEl = titleCmp.el;
            titleEl.setStyle('left', titleEl.getWidth() + 'px');
        }
    },

    onTitleRender: function() {
        if (this.orientation === 'vertical') {
            this.titleCmp.el.setVertical(90);
        }
    },

    
    addUIClsToElement: function(cls) {
        var me = this,
            result = me.callParent(arguments),
            classes = [me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls],
            array, i;

        if (me.bodyCls) {
            array = me.bodyCls.split(' ');

            for (i = 0; i < classes.length; i++) {
                if (!Ext.Array.contains(array, classes[i])) {
                    array.push(classes[i]);
                }
            }

            me.bodyCls = array.join(' ');
        } else {
            me.bodyCls = classes.join(' ');
        }

        return result;
    },

    
    removeUIClsFromElement: function(cls) {
        var me = this,
            result = me.callParent(arguments),
            classes = [me.baseCls + '-body-' + cls, me.baseCls + '-body-' + me.ui + '-' + cls],
            array, i;

        if (me.bodyCls) {
            array = me.bodyCls.split(' ');

            for (i = 0; i < classes.length; i++) {
                Ext.Array.remove(array, classes[i]);
            }

            me.bodyCls = array.join(' ');
        }

        return result;
    },

    
    addUIToElement: function() {
        var me = this,
            array, cls;

        me.callParent(arguments);

        cls = me.baseCls + '-body-' + me.ui;
        if (me.rendered) {
            if (me.bodyCls) {
                me.body.addCls(me.bodyCls);
            } else {
                me.body.addCls(cls);
            }
        } else {
            if (me.bodyCls) {
                array = me.bodyCls.split(' ');

                if (!Ext.Array.contains(array, cls)) {
                    array.push(cls);
                }

                me.bodyCls = array.join(' ');
            } else {
                me.bodyCls = cls;
            }
        }

        if (me.titleCmp && me.titleCmp.rendered) {
            me.titleCmp.addCls(me.baseCls + '-text-container-' + me.ui);
        }
    },

    
    removeUIFromElement: function() {
        var me = this,
            array, cls;

        me.callParent(arguments);

        cls = me.baseCls + '-body-' + me.ui;
        if (me.rendered) {
            if (me.bodyCls) {
                me.body.removeCls(me.bodyCls);
            } else {
                me.body.removeCls(cls);
            }
        } else {
            if (me.bodyCls) {
                array = me.bodyCls.split(' ');
                Ext.Array.remove(array, cls);
                me.bodyCls = array.join(' ');
            } else {
                me.bodyCls = cls;
            }
        }

        if (me.titleCmp && me.titleCmp.rendered) {
            me.titleCmp.removeCls(me.baseCls + '-text-container-' + me.ui);
        }
    },

    onClick: function(e) {
        this.fireClickEvent('click', e);
    },

    onDblClick: function(e){
        this.fireClickEvent('dblclick', e);
    },

    fireClickEvent: function(type, e){
        var toolCls = '.' + Ext.panel.Tool.prototype.baseCls;
        if (!e.getTarget(toolCls)) {
            this.fireEvent(type, this, e);
        }
    },

    getFocusEl: function() {
        return this.el;
    },

    getTargetEl: function() {
        return this.body || this.frameBody || this.el;
    },

    applyTargetCls: function(targetCls) {
        this.bodyTargetCls = targetCls;
    },

    
    setTitle: function(title) {
        var me = this,
            titleCmp = me.titleCmp;

        me.title = title;
        if (titleCmp.rendered) {
            titleCmp.textEl.update(me.title || '&#160;');
            titleCmp.updateLayout();
        } else {
            me.titleCmp.on({
                render: function() {
                    me.setTitle(title);
                },
                single: true
            });
        }
    },

    
    getMinWidth: function() {
        var me = this,
            textEl = me.titleCmp.textEl.dom,
            result,
            tools = me.tools,
            l, i;

        
        textEl.style.display = 'inline';
        result = textEl.offsetWidth;
        textEl.style.display = '';

        
        if (tools && (l = tools.length)) {
            for (i = 0; i < l; i++) {
                if (tools[i].el) {
                    result += tools[i].el.dom.offsetWidth;
                }
            }
        }

        
        if (me.iconCmp) {
            result += me.iconCmp.el.dom.offsetWidth;
        }

        
        return result + 10;
    },

    
    setIconCls: function(cls) {
        var me = this,
            isEmpty = !cls || !cls.length,
            iconCmp = me.iconCmp;

        me.iconCls = cls;
        if (!me.iconCmp && !isEmpty) {
            me.initIconCmp();
            me.insert(0, me.iconCmp);
        } else if (iconCmp) {
            if (isEmpty) {
                me.iconCmp.destroy();
                delete me.iconCmp;
            } else {
                iconCmp.removeCls(iconCmp.iconCls);
                iconCmp.addCls(cls);
                iconCmp.iconCls = cls;
            }
        }
    },

    
    setIcon: function(icon) {
        var me = this,
            isEmpty = !icon || !icon.length,
            iconCmp = me.iconCmp;

        me.icon = icon;
        if (!me.iconCmp && !isEmpty) {
            me.initIconCmp();
            me.insert(0, me.iconCmp);
        } else if (iconCmp) {
            if (isEmpty) {
                me.iconCmp.destroy();
                delete me.iconCmp;
            } else {
                iconCmp.setSrc(me.icon);
            }
        }
    },

     
    setGlyph: function(glyph) {
        var me = this,
            iconCmp = me.iconCmp;
        
        if (!me.iconCmp) {
            me.initIconCmp();
            me.insert(0, me.iconCmp);
        } else if (iconCmp) {
            if (glyph) {
                me.iconCmp.setGlyph(glyph);
            } else {
                me.iconCmp.destroy();
                delete me.iconCmp;
            }
        }
    },

    
    getTools: function(){
        return this.tools.slice();
    },

    
    addTool: function(tool) {
        
        
        this.add(Ext.ComponentManager.create(tool, 'tool'));
    },

    syncBeforeAfterTitleClasses: function() {
        var me = this,
            items = me.items,
            childItems = items.items,
            titlePosition = me.titlePosition,
            itemCount = childItems.length,
            itemGeneration = items.generation,
            syncGen = me.syncBeforeAfterGen,
            afterCls, beforeCls, i, item;

        if (syncGen === itemGeneration) {
            return;
        }
        me.syncBeforeAfterGen = itemGeneration;

        for (i = 0; i < itemCount; ++i) {
            item = childItems[i];

            afterCls  = item.afterTitleCls  || (item.afterTitleCls  = item.baseCls + '-after-title')
            beforeCls = item.beforeTitleCls || (item.beforeTitleCls = item.baseCls + '-before-title')

            if (!me.title || i < titlePosition) {
                if (syncGen) {
                    item.removeCls(afterCls);
                } 
                item.addCls(beforeCls);
            } else if (i > titlePosition) {
                if (syncGen) {
                    item.removeCls(beforeCls);
                }
                item.addCls(afterCls);
            }
        }
    },

    
    onAdd: function(component, index) {
        var tools = this.tools;
        this.callParent(arguments);
        if (component.isTool) {
            tools.push(component);
            tools[component.type] = component;
        }
    },

    
    initRenderData: function() {
        return Ext.applyIf(this.callParent(), {
            bodyCls: this.bodyCls,
            bodyTargetCls: this.bodyTargetCls,
            headerCls: this.headerCls
        });
    },

    getDockName: function() {
        return this.dock;
    },
    
    getFramingInfoCls: function(){
        var me = this,
            cls = me.callParent(),
            owner = me.ownerCt;
            
        if (!me.expanding && (owner && owner.collapsed) || me.isCollapsedExpander) {
            cls += '-' + owner.collapsedCls; 
        }
        return cls + '-' + me.dock;
    }
});





Ext.define('Ext.dd.DragDrop', {
                                         

    
    constructor: function(id, sGroup, config) {
        if(id) {
            this.init(id, sGroup, config);
        }
    },

    

    
    id: null,

    
    config: null,

    
    dragElId: null,

    
    handleElId: null,

    
    invalidHandleTypes: null,

    
    invalidHandleIds: null,

    
    invalidHandleClasses: null,

    
    startPageX: 0,

    
    startPageY: 0,

    
    groups: null,

    
    locked: false,

    
    lock: function() {
        this.locked = true;
    },

    
    moveOnly: false,

    
    unlock: function() {
        this.locked = false;
    },

    
    isTarget: true,

    
    padding: null,

    
    _domRef: null,

    
    __ygDragDrop: true,

    
    constrainX: false,

    
    constrainY: false,

    
    minX: 0,

    
    maxX: 0,

    
    minY: 0,

    
    maxY: 0,

    
    maintainOffset: false,

    
    xTicks: null,

    
    yTicks: null,

    
    primaryButtonOnly: true,

    
    available: false,

    
    hasOuterHandles: false,

    
    b4StartDrag: function(x, y) { },

    
    startDrag: function(x, y) {  },

    
    b4Drag: function(e) { },

    
    onDrag: function(e) {  },

    
    onDragEnter: function(e, id) {  },

    
    b4DragOver: function(e) { },

    
    onDragOver: function(e, id) {  },

    
    b4DragOut: function(e) { },

    
    onDragOut: function(e, id) {  },

    
    b4DragDrop: function(e) { },

    
    onDragDrop: function(e, id) {  },

    
    onInvalidDrop: function(e) {  },

    
    b4EndDrag: function(e) { },

    
    endDrag: function(e) {  },

    
    b4MouseDown: function(e) {  },

    
    onMouseDown: function(e) {  },

    
    onMouseUp: function(e) {  },

    
    onAvailable: function () {
    },

    
    defaultPadding: {
        left: 0,
        right: 0,
        top: 0,
        bottom: 0
    },

    
    constrainTo : function(constrainTo, pad, inContent){
        if(Ext.isNumber(pad)){
            pad = {left: pad, right:pad, top:pad, bottom:pad};
        }
        pad = pad || this.defaultPadding;
        var b = Ext.get(this.getEl()).getBox(),
            ce = Ext.get(constrainTo),
            s = ce.getScroll(),
            c,
            cd = ce.dom,
            xy,
            topSpace,
            leftSpace;
        if(cd == document.body){
            c = { x: s.left, y: s.top, width: Ext.Element.getViewWidth(), height: Ext.Element.getViewHeight()};
        }else{
            xy = ce.getXY();
            c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight};
        }

        topSpace = b.y - c.y;
        leftSpace = b.x - c.x;

        this.resetConstraints();
        this.setXConstraint(leftSpace - (pad.left||0), 
                c.width - leftSpace - b.width - (pad.right||0), 
        this.xTickSize
        );
        this.setYConstraint(topSpace - (pad.top||0), 
                c.height - topSpace - b.height - (pad.bottom||0), 
        this.yTickSize
        );
    },

    
    getEl: function() {
        if (!this._domRef) {
            this._domRef = Ext.getDom(this.id);
        }

        return this._domRef;
    },

    
    getDragEl: function() {
        return Ext.getDom(this.dragElId);
    },

    
    init: function(id, sGroup, config) {
        this.initTarget(id, sGroup, config);
        Ext.EventManager.on(this.id, "mousedown", this.handleMouseDown, this);
        
    },

    
    initTarget: function(id, sGroup, config) {
        
        this.config = config || {};

        
        this.DDMInstance = Ext.dd.DragDropManager;
        
        this.groups = {};

        
        
        if (typeof id !== "string") {
            id = Ext.id(id);
        }

        
        this.id = id;

        
        this.addToGroup((sGroup) ? sGroup : "default");

        
        
        this.handleElId = id;

        
        this.setDragElId(id);

        
        this.invalidHandleTypes = { A: "A" };
        this.invalidHandleIds = {};
        this.invalidHandleClasses = [];

        this.applyConfig();

        this.handleOnAvailable();
    },

    
    applyConfig: function() {

        
        
        this.padding           = this.config.padding || [0, 0, 0, 0];
        this.isTarget          = (this.config.isTarget !== false);
        this.maintainOffset    = (this.config.maintainOffset);
        this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);

    },

    
    handleOnAvailable: function() {
        this.available = true;
        this.resetConstraints();
        this.onAvailable();
    },

    
    setPadding: function(iTop, iRight, iBot, iLeft) {
        
        if (!iRight && 0 !== iRight) {
            this.padding = [iTop, iTop, iTop, iTop];
        } else if (!iBot && 0 !== iBot) {
            this.padding = [iTop, iRight, iTop, iRight];
        } else {
            this.padding = [iTop, iRight, iBot, iLeft];
        }
    },

    
    setInitPosition: function(diffX, diffY) {
        var el = this.getEl(),
            dx, dy, p;

        if (!this.DDMInstance.verifyEl(el)) {
            return;
        }

        dx = diffX || 0;
        dy = diffY || 0;

        p = Ext.Element.getXY( el );

        this.initPageX = p[0] - dx;
        this.initPageY = p[1] - dy;

        this.lastPageX = p[0];
        this.lastPageY = p[1];

        this.setStartPosition(p);
    },

    
    setStartPosition: function(pos) {
        var p = pos || Ext.Element.getXY( this.getEl() );
        this.deltaSetXY = null;

        this.startPageX = p[0];
        this.startPageY = p[1];
    },

    
    addToGroup: function(sGroup) {
        this.groups[sGroup] = true;
        this.DDMInstance.regDragDrop(this, sGroup);
    },

    
    removeFromGroup: function(sGroup) {
        if (this.groups[sGroup]) {
            delete this.groups[sGroup];
        }

        this.DDMInstance.removeDDFromGroup(this, sGroup);
    },

    
    setDragElId: function(id) {
        this.dragElId = id;
    },

    
    setHandleElId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        this.handleElId = id;
        this.DDMInstance.regHandle(this.id, id);
    },

    
    setOuterHandleElId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        Ext.EventManager.on(id, "mousedown", this.handleMouseDown, this);
        this.setHandleElId(id);

        this.hasOuterHandles = true;
    },

    
    unreg: function() {
        Ext.EventManager.un(this.id, "mousedown", this.handleMouseDown, this);
        this._domRef = null;
        this.DDMInstance._remove(this);
    },

    
    destroy : function(){
        this.unreg();
    },

    
    isLocked: function() {
        return (this.DDMInstance.isLocked() || this.locked);
    },

    
    handleMouseDown: function(e, oDD){
        var me = this;

        if ((me.primaryButtonOnly && e.button != 0) || me.isLocked()) {
            return;
        }

        me.DDMInstance.refreshCache(me.groups);

        if (me.hasOuterHandles || me.DDMInstance.isOverTarget(e.getPoint(), me))  {
            if (me.clickValidator(e)) {
                
                me.setStartPosition();
                me.b4MouseDown(e);
                me.onMouseDown(e);

                me.DDMInstance.handleMouseDown(e, me);

                me.DDMInstance.stopEvent(e);
            }
        }
    },

    clickValidator: function(e) {
        var target = e.getTarget();
        return ( this.isValidHandleChild(target) &&
                    (this.id == this.handleElId ||
                        this.DDMInstance.handleWasClicked(target, this.id)) );
    },

    
    addInvalidHandleType: function(tagName) {
        var type = tagName.toUpperCase();
        this.invalidHandleTypes[type] = type;
    },

    
    addInvalidHandleId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        this.invalidHandleIds[id] = id;
    },

    
    addInvalidHandleClass: function(cssClass) {
        this.invalidHandleClasses.push(cssClass);
    },

    
    removeInvalidHandleType: function(tagName) {
        var type = tagName.toUpperCase();
        
        delete this.invalidHandleTypes[type];
    },

    
    removeInvalidHandleId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        delete this.invalidHandleIds[id];
    },

    
    removeInvalidHandleClass: function(cssClass) {
        for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
            if (this.invalidHandleClasses[i] == cssClass) {
                delete this.invalidHandleClasses[i];
            }
        }
    },

    
    isValidHandleChild: function(node) {

        var valid = true,
            nodeName,
            i, len;
        
        try {
            nodeName = node.nodeName.toUpperCase();
        } catch(e) {
            nodeName = node.nodeName;
        }
        valid = valid && !this.invalidHandleTypes[nodeName];
        valid = valid && !this.invalidHandleIds[node.id];

        for (i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
            valid = !Ext.fly(node).hasCls(this.invalidHandleClasses[i]);
        }


        return valid;

    },

    
    setXTicks: function(iStartX, iTickSize) {
        this.xTicks = [];
        this.xTickSize = iTickSize;

        var tickMap = {},
            i;

        for (i = this.initPageX; i >= this.minX; i = i - iTickSize) {
            if (!tickMap[i]) {
                this.xTicks[this.xTicks.length] = i;
                tickMap[i] = true;
            }
        }

        for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
            if (!tickMap[i]) {
                this.xTicks[this.xTicks.length] = i;
                tickMap[i] = true;
            }
        }

        Ext.Array.sort(this.xTicks, this.DDMInstance.numericSort);
    },

    
    setYTicks: function(iStartY, iTickSize) {
        this.yTicks = [];
        this.yTickSize = iTickSize;

        var tickMap = {},
            i;

        for (i = this.initPageY; i >= this.minY; i = i - iTickSize) {
            if (!tickMap[i]) {
                this.yTicks[this.yTicks.length] = i;
                tickMap[i] = true;
            }
        }

        for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
            if (!tickMap[i]) {
                this.yTicks[this.yTicks.length] = i;
                tickMap[i] = true;
            }
        }

        Ext.Array.sort(this.yTicks, this.DDMInstance.numericSort);
    },

    
    setXConstraint: function(iLeft, iRight, iTickSize) {
        this.leftConstraint = iLeft;
        this.rightConstraint = iRight;

        this.minX = this.initPageX - iLeft;
        this.maxX = this.initPageX + iRight;
        if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }

        this.constrainX = true;
    },

    
    clearConstraints: function() {
        this.constrainX = false;
        this.constrainY = false;
        this.clearTicks();
    },

    
    clearTicks: function() {
        this.xTicks = null;
        this.yTicks = null;
        this.xTickSize = 0;
        this.yTickSize = 0;
    },

    
    setYConstraint: function(iUp, iDown, iTickSize) {
        this.topConstraint = iUp;
        this.bottomConstraint = iDown;

        this.minY = this.initPageY - iUp;
        this.maxY = this.initPageY + iDown;
        if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }

        this.constrainY = true;

    },

    
    resetConstraints: function() {
        
        if (this.initPageX || this.initPageX === 0) {
            
            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0,
                dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;

            this.setInitPosition(dx, dy);

        
        } else {
            this.setInitPosition();
        }

        if (this.constrainX) {
            this.setXConstraint( this.leftConstraint,
                                 this.rightConstraint,
                                 this.xTickSize        );
        }

        if (this.constrainY) {
            this.setYConstraint( this.topConstraint,
                                 this.bottomConstraint,
                                 this.yTickSize         );
        }
    },

    
    getTick: function(val, tickArray) {
        if (!tickArray) {
            
            
            return val;
        } else if (tickArray[0] >= val) {
            
            
            return tickArray[0];
        } else {
            var i, len, next, diff1, diff2;
            for (i=0, len=tickArray.length; i<len; ++i) {
                next = i + 1;
                if (tickArray[next] && tickArray[next] >= val) {
                    diff1 = val - tickArray[i];
                    diff2 = tickArray[next] - val;
                    return (diff2 > diff1) ? tickArray[i] : tickArray[next];
                }
            }

            
            
            return tickArray[tickArray.length - 1];
        }
    },

    
    toString: function() {
        return ("DragDrop " + this.id);
    }

});





Ext.define('Ext.dd.DD', {
    extend:  Ext.dd.DragDrop ,
                                         

    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.init(id, sGroup, config);
        }
    },

    
    scroll: true,

    
    autoOffset: function(iPageX, iPageY) {
        var x = iPageX - this.startPageX,
            y = iPageY - this.startPageY;
        this.setDelta(x, y);
    },

    
    setDelta: function(iDeltaX, iDeltaY) {
        this.deltaX = iDeltaX;
        this.deltaY = iDeltaY;
    },

    
    setDragElPos: function(iPageX, iPageY) {
        
        

        var el = this.getDragEl();
        this.alignElWithMouse(el, iPageX, iPageY);
    },

    
    alignElWithMouse: function(el, iPageX, iPageY) {
        var oCoord = this.getTargetCoord(iPageX, iPageY),
            fly = el.dom ? el : Ext.fly(el, '_dd'),
            elSize = fly.getSize(),
            EL = Ext.Element,
            vpSize,
            aCoord,
            newLeft,
            newTop;

        if (!this.deltaSetXY) {
            vpSize = this.cachedViewportSize = { width: EL.getDocumentWidth(), height: EL.getDocumentHeight() };
            aCoord = [
                Math.max(0, Math.min(oCoord.x, vpSize.width - elSize.width)),
                Math.max(0, Math.min(oCoord.y, vpSize.height - elSize.height))
            ];
            fly.setXY(aCoord);
            newLeft = this.getLocalX(fly);
            newTop  = fly.getLocalY();
            this.deltaSetXY = [newLeft - oCoord.x, newTop - oCoord.y];
        } else {
            vpSize = this.cachedViewportSize;
            this.setLocalXY(
                fly,
                Math.max(0, Math.min(oCoord.x + this.deltaSetXY[0], vpSize.width - elSize.width)),
                Math.max(0, Math.min(oCoord.y + this.deltaSetXY[1], vpSize.height - elSize.height))
            );
        }

        this.cachePosition(oCoord.x, oCoord.y);
        this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
        return oCoord;
    },

    
    cachePosition: function(iPageX, iPageY) {
        if (iPageX) {
            this.lastPageX = iPageX;
            this.lastPageY = iPageY;
        } else {
            var aCoord = Ext.Element.getXY(this.getEl());
            this.lastPageX = aCoord[0];
            this.lastPageY = aCoord[1];
        }
    },

    
    autoScroll: function(x, y, h, w) {

        if (this.scroll) {
            
            var clientH = Ext.Element.getViewHeight(),
                
                clientW = Ext.Element.getViewWidth(),
                
                st = this.DDMInstance.getScrollTop(),
                
                sl = this.DDMInstance.getScrollLeft(),
                
                bot = h + y,
                
                right = w + x,
                
                
                
                toBot = (clientH + st - y - this.deltaY),
                
                toRight = (clientW + sl - x - this.deltaX),
                
                
                thresh = 40,
                
                
                
                scrAmt = (document.all) ? 80 : 30;

            
            
            if ( bot > clientH && toBot < thresh ) {
                window.scrollTo(sl, st + scrAmt);
            }

            
            
            if ( y < st && st > 0 && y - st < thresh ) {
                window.scrollTo(sl, st - scrAmt);
            }

            
            
            if ( right > clientW && toRight < thresh ) {
                window.scrollTo(sl + scrAmt, st);
            }

            
            
            if ( x < sl && sl > 0 && x - sl < thresh ) {
                window.scrollTo(sl - scrAmt, st);
            }
        }
    },

    
    getTargetCoord: function(iPageX, iPageY) {
        var x = iPageX - this.deltaX,
            y = iPageY - this.deltaY;

        if (this.constrainX) {
            if (x < this.minX) {
                x = this.minX;
            }
            if (x > this.maxX) {
                x = this.maxX;
            }
        }

        if (this.constrainY) {
            if (y < this.minY) {
                y = this.minY;
            }
            if (y > this.maxY) {
                y = this.maxY;
            }
        }

        x = this.getTick(x, this.xTicks);
        y = this.getTick(y, this.yTicks);


        return {x: x, y: y};
    },

    
    applyConfig: function() {
        this.callParent();
        this.scroll = (this.config.scroll !== false);
    },

    
    b4MouseDown: function(e) {
        
        this.autoOffset(e.getPageX(), e.getPageY());
    },

    
    b4Drag: function(e) {
        this.setDragElPos(e.getPageX(), e.getPageY());
    },

    toString: function() {
        return ("DD " + this.id);
    },
    
    getLocalX: function(el) {
        return el.getLocalX();
    },

    setLocalXY: function(el, x, y) {
        el.setLocalXY(x, y);
    }

    
    
    
    

});




Ext.define('Ext.dd.DDProxy', {
    extend:  Ext.dd.DD ,

    statics: {
        
        dragElId: "ygddfdiv"
    },

    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.init(id, sGroup, config);
            this.initFrame();
        }
    },

    
    resizeFrame: true,

    
    centerFrame: false,

    
    createFrame: function() {
        var self = this,
            body = document.body,
            div,
            s;

        if (!body || !body.firstChild) {
            setTimeout( function() { self.createFrame(); }, 50 );
            return;
        }

        div = this.getDragEl();

        if (!div) {
            div    = document.createElement("div");
            div.id = this.dragElId;
            s  = div.style;

            s.position   = "absolute";
            s.visibility = "hidden";
            s.cursor     = "move";
            s.border     = "2px solid #aaa";
            s.zIndex     = 999;

            
            
            
            body.insertBefore(div, body.firstChild);
        }
    },

    
    initFrame: function() {
        this.createFrame();
    },

    applyConfig: function() {
        this.callParent();

        this.resizeFrame = (this.config.resizeFrame !== false);
        this.centerFrame = (this.config.centerFrame);
        this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
    },

    
    showFrame: function(iPageX, iPageY) {
        var el = this.getEl(),
            dragEl = this.getDragEl(),
            s = dragEl.style;

        this._resizeProxy();

        if (this.centerFrame) {
            this.setDelta( Math.round(parseInt(s.width,  10)/2),
                           Math.round(parseInt(s.height, 10)/2) );
        }

        this.setDragElPos(iPageX, iPageY);

        Ext.fly(dragEl).show();
    },

    
    _resizeProxy: function() {
        if (this.resizeFrame) {
            var el = this.getEl();
            Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
        }
    },

    
    b4MouseDown: function(e) {
        var x = e.getPageX(),
            y = e.getPageY();
        this.autoOffset(x, y);
        this.setDragElPos(x, y);
    },

    
    b4StartDrag: function(x, y) {
        
        this.showFrame(x, y);
    },

    
    b4EndDrag: function(e) {
        Ext.fly(this.getDragEl()).hide();
    },

    
    
    
    endDrag: function(e) {

        var lel = this.getEl(),
            del = this.getDragEl();

        
        del.style.visibility = "";

        this.beforeMove();
        
        
        lel.style.visibility = "hidden";
        Ext.dd.DDM.moveToEl(lel, del);
        del.style.visibility = "hidden";
        lel.style.visibility = "";

        this.afterDrag();
    },

    beforeMove : function(){

    },

    afterDrag : function(){

    },

    toString: function() {
        return ("DDProxy " + this.id);
    }

});


Ext.define('Ext.dd.StatusProxy', {
    extend:  Ext.Component ,
    animRepair: false,

    childEls: [
        'ghost'
    ],

    renderTpl: [
        '<div class="' + Ext.baseCSSPrefix + 'dd-drop-icon"></div>' +
        '<div id="{id}-ghost" class="' + Ext.baseCSSPrefix + 'dd-drag-ghost"></div>'
    ],
    
    repairCls: Ext.baseCSSPrefix + 'dd-drag-repair',

    
    constructor: function(config) {
        var me = this;

        config = config || {};

        Ext.apply(me, {
            hideMode: 'visibility',
            hidden: true,
            floating: true,
            id: me.id || Ext.id(),
            cls: Ext.baseCSSPrefix + 'dd-drag-proxy ' + this.dropNotAllowed,
            shadow: config.shadow || false,
            renderTo: Ext.getDetachedBody()
        });
        me.callParent(arguments);
        this.dropStatus = this.dropNotAllowed;
    },

    
    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',

    
    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',

    
    setStatus : function(cssClass){
        cssClass = cssClass || this.dropNotAllowed;
        if (this.dropStatus != cssClass) {
            this.el.replaceCls(this.dropStatus, cssClass);
            this.dropStatus = cssClass;
        }
    },

    
    reset : function(clearGhost){
        var me = this,
            clsPrefix = Ext.baseCSSPrefix + 'dd-drag-proxy ';

        me.el.replaceCls(clsPrefix + me.dropAllowed, clsPrefix + me.dropNotAllowed);
        me.dropStatus = me.dropNotAllowed;
        if (clearGhost) {
            me.ghost.update('');
        }
    },

    
    update : function(html){
        if (typeof html == "string") {
            this.ghost.update(html);
        } else {
            this.ghost.update("");
            html.style.margin = "0";
            this.ghost.dom.appendChild(html);
        }
        var el = this.ghost.dom.firstChild;
        if (el) {
            Ext.fly(el).setStyle('float', 'none');
        }
    },

    
    getGhost : function(){
        return this.ghost;
    },

    
    hide : function(clear) {
        this.callParent();
        if (clear) {
            this.reset(true);
        }
    },

    
    stop : function(){
        if (this.anim && this.anim.isAnimated && this.anim.isAnimated()) {
            this.anim.stop();
        }
    },

    
    sync : function(){
        this.el.sync();
    },

    
    repair : function(xy, callback, scope) {
        var me = this;

        me.callback = callback;
        me.scope = scope;
        if (xy && me.animRepair !== false) {
            me.el.addCls(me.repairCls);
            me.el.hideUnders(true);
            me.anim = me.el.animate({
                duration: me.repairDuration || 500,
                easing: 'ease-out',
                to: {
                    x: xy[0],
                    y: xy[1]
                },
                stopAnimation: true,
                callback: me.afterRepair,
                scope: me
            });
        } else {
            me.afterRepair();
        }
    },

    
    afterRepair : function() {
        var me = this;
    
        me.hide(true);
        me.el.removeCls(me.repairCls);
        if (typeof me.callback == "function") {
            me.callback.call(me.scope || me);
        }
        delete me.callback;
        delete me.scope;
    }
});


Ext.define('Ext.dd.DragSource', {
    extend:  Ext.dd.DDProxy ,
               
                             
                                
      

    

    

    
    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',

    
    animRepair: true,

    
    repairHighlightColor: 'c3daf9',

    
    constructor: function(el, config) {
        this.el = Ext.get(el);
        if(!this.dragData){
            this.dragData = {};
        }

        Ext.apply(this, config);

        if(!this.proxy){
            this.proxy = new Ext.dd.StatusProxy({
                id: this.el.id + '-drag-status-proxy',
                animRepair: this.animRepair
            });
        }
        this.callParent([this.el.dom, this.ddGroup || this.group,
              {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true}]);

        this.dragging = false;
    },

    
    getDragData : function(e){
        return this.dragData;
    },

    
    onDragEnter : function(e, id){
        var target = Ext.dd.DragDropManager.getDDById(id),
            status;
        this.cachedTarget = target;
        if (this.beforeDragEnter(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                status = target.notifyEnter(this, e, this.dragData);
                this.proxy.setStatus(status);
            } else {
                this.proxy.setStatus(this.dropAllowed);
            }

            if (this.afterDragEnter) {
                
                this.afterDragEnter(target, e, id);
            }
        }
    },

    
    beforeDragEnter: function(target, e, id) {
        return true;
    },

    
    onDragOver: function(e, id) {
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id),
            status;
        if (this.beforeDragOver(target, e, id) !== false) {
            if(target.isNotifyTarget){
                status = target.notifyOver(this, e, this.dragData);
                this.proxy.setStatus(status);
            }

            if (this.afterDragOver) {
                
                this.afterDragOver(target, e, id);
            }
        }
    },

    
    beforeDragOver: function(target, e, id) {
        return true;
    },

    
    onDragOut: function(e, id) {
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
        if (this.beforeDragOut(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                target.notifyOut(this, e, this.dragData);
            }
            this.proxy.reset();
            if (this.afterDragOut) {
                
                this.afterDragOut(target, e, id);
            }
        }
        this.cachedTarget = null;
    },

    
    beforeDragOut: function(target, e, id){
        return true;
    },

    
    onDragDrop: function(e, id){
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
        if (this.beforeDragDrop(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                if (target.notifyDrop(this, e, this.dragData) !== false) { 
                    this.onValidDrop(target, e, id);
                } else {
                    this.onInvalidDrop(target, e, id);
                }
            } else {
                this.onValidDrop(target, e, id);
            }

            if (this.afterDragDrop) {
                
                this.afterDragDrop(target, e, id);
            }
        }
        delete this.cachedTarget;
    },

    
    beforeDragDrop: function(target, e, id){
        return true;
    },

    
    onValidDrop: function(target, e, id){
        this.hideProxy();
        if(this.afterValidDrop){
            
            this.afterValidDrop(target, e, id);
        }
    },

    
    getRepairXY: function(e, data){
        return this.el.getXY();
    },

    
    onInvalidDrop: function(target, e, id) {
        
        
        
        var me = this;
        
        if (!e) {
            e = target;
            target = null;
            id = e.getTarget().id;
        }
        if (me.beforeInvalidDrop(target, e, id) !== false) {
            if (me.cachedTarget) {
                if(me.cachedTarget.isNotifyTarget){
                    me.cachedTarget.notifyOut(me, e, me.dragData);
                }
                me.cacheTarget = null;
            }
            me.proxy.repair(me.getRepairXY(e, me.dragData), me.afterRepair, me);

            if (me.afterInvalidDrop) {
                
                me.afterInvalidDrop(e, id);
            }
        }
    },

    
    afterRepair: function() {
        var me = this;
        if (Ext.enableFx) {
            me.el.highlight(me.repairHighlightColor);
        }
        me.dragging = false;
    },

    
    beforeInvalidDrop: function(target, e, id) {
        return true;
    },

    
    handleMouseDown: function(e) {
        if (this.dragging) {
            return;
        }
        var data = this.getDragData(e);
        if (data && this.onBeforeDrag(data, e) !== false) {
            this.dragData = data;
            this.proxy.stop();
            this.callParent(arguments);
        }
    },

    
    onBeforeDrag: function(data, e){
        return true;
    },

    
    onStartDrag: Ext.emptyFn,

    alignElWithMouse: function() {
        this.proxy.ensureAttachedToBody(true);
        return this.callParent(arguments);
    },

    
    startDrag: function(x, y) {
        this.proxy.reset();
        this.proxy.hidden = false;
        this.dragging = true;
        this.proxy.update("");
        this.onInitDrag(x, y);
        this.proxy.show();
    },

    
    onInitDrag: function(x, y) {
        var clone = this.el.dom.cloneNode(true);
        clone.id = Ext.id(); 
        this.proxy.update(clone);
        this.onStartDrag(x, y);
        return true;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    hideProxy: function() {
        this.proxy.hide();
        this.proxy.reset(true);
        this.dragging = false;
    },

    
    triggerCacheRefresh: function() {
        Ext.dd.DDM.refreshCache(this.groups);
    },

    
    b4EndDrag: function(e) {
    },

    
    endDrag : function(e){
        this.onEndDrag(this.dragData, e);
    },

    
    onEndDrag : function(data, e){
    },

    
    autoOffset : function(x, y) {
        this.setDelta(-12, -20);
    },

    destroy: function(){
        this.callParent();
        Ext.destroy(this.proxy);
    }
});


Ext.define('Ext.panel.Proxy', {

    alternateClassName: 'Ext.dd.PanelProxy',
    
    
    moveOnDrag: true,

    
    constructor: function(panel, config){
        var me = this;
        
        
        me.panel = panel;
        me.id = me.panel.id +'-ddproxy';
        Ext.apply(me, config);
    },

    
    insertProxy: true,

    
    setStatus: Ext.emptyFn,
    reset: Ext.emptyFn,
    update: Ext.emptyFn,
    stop: Ext.emptyFn,
    sync: Ext.emptyFn,

    
    getEl: function(){
        return this.ghost.el;
    },

    
    getGhost: function(){
        return this.ghost;
    },

    
    getProxy: function(){
        return this.proxy;
    },

    
    hide : function(){
        var me = this;
        
        if (me.ghost) {
            if (me.proxy) {
                me.proxy.remove();
                delete me.proxy;
            }

            
            me.panel.unghost(null, me.moveOnDrag);
            delete me.ghost;
        }
    },

    
    show: function(){
        var me = this,
            panelSize;
            
        if (!me.ghost) {
            panelSize = me.panel.getSize();
            me.panel.el.setVisibilityMode(Ext.Element.DISPLAY);
            me.ghost = me.panel.ghost();
            if (me.insertProxy) {
                
                
                me.proxy = me.panel.el.insertSibling({cls: Ext.baseCSSPrefix + 'panel-dd-spacer'});
                me.proxy.setSize(panelSize);
            }
        }
    },

    
    repair: function(xy, callback, scope) {
        this.hide();
        Ext.callback(callback, scope || this);
    },

    
    moveProxy : function(parentNode, before){
        if (this.proxy) {
            parentNode.insertBefore(this.proxy.dom, before);
        }
    }
});


Ext.define('Ext.panel.DD', {
    extend:  Ext.dd.DragSource ,
                                  

    constructor : function(panel, cfg){
        var me = this;
        
        me.panel = panel;
        me.dragData = {panel: panel};
        me.panelProxy = new Ext.panel.Proxy(panel, cfg);
        me.proxy = me.panelProxy.proxy;

        me.callParent([panel.el, cfg]);
        me.setupEl(panel);
    },
    
    setupEl: function(panel){
        var me = this,
            header = panel.header,
            el = panel.body;
            
        if (header) {
            me.setHandleElId(header.id);
            el = header.el;
        }
        if (el) {
            el.setStyle('cursor', 'move');
            me.scroll = false;
        } else {
            
            panel.on('boxready', me.setupEl, me, {single: true});
        }
    },

    showFrame: Ext.emptyFn,
    startDrag: Ext.emptyFn,
    
    b4StartDrag: function(x, y) {
        this.panelProxy.show();
    },
    
    b4MouseDown: function(e) {
        var x = e.getPageX(),
            y = e.getPageY();
            
        this.autoOffset(x, y);
    },
    
    onInitDrag : function(x, y){
        this.onStartDrag(x, y);
        return true;
    },
    
    createFrame : Ext.emptyFn,
    
    getDragEl : function(e){
        var ghost = this.panelProxy.ghost;
        if (ghost) {
            return ghost.el.dom;
        }
    },
    
    endDrag : function(e){
        this.panelProxy.hide();
        this.panel.saveState();
    },

    autoOffset : function(x, y) {
        x -= this.startPageX;
        y -= this.startPageY;
        this.setDelta(x, y);
    },
    
    
    
    onInvalidDrop: function(target, e, id) {
        var me = this;
        
        if (me.beforeInvalidDrop(target, e, id) !== false) {
            if (me.cachedTarget) {
                if(me.cachedTarget.isNotifyTarget){
                    me.cachedTarget.notifyOut(me, e, me.dragData);
                }
                me.cacheTarget = null;
            }

            if (me.afterInvalidDrop) {
                
                me.afterInvalidDrop(e, id);
            }
        }
    }
});


Ext.define('Ext.util.Memento', (function () {

    function captureOne (src, target, prop, prefix) {
        src[prefix ? prefix + prop : prop] = target[prop];
    }

    function removeOne (src, target, prop) {
        delete src[prop];
    }

    function restoreOne (src, target, prop, prefix) {
        var name = prefix ? prefix + prop : prop,
            value = src[name];

        if (value || src.hasOwnProperty(name)) {
            restoreValue(target, prop, value);
        }
    }

    function restoreValue (target, prop, value) {
        if (Ext.isDefined(value)) {
            target[prop] = value;
        } else {
            delete target[prop];
        }
    }

    function doMany (doOne, src, target, props, prefix) {
        if (src) {
            if (Ext.isArray(props)) {
                var p, pLen = props.length;
                for (p = 0; p < pLen; p++) {
                    doOne(src, target, props[p], prefix);
                }
            } else {
                doOne(src, target, props, prefix);
            }
        }
    }

    return {
        
        data: null,

        
        target: null,

        
        constructor: function (target, props) {
            if (target) {
                this.target = target;
                if (props) {
                    this.capture(props);
                }
            }
        },

        
        capture: function (props, target, prefix) {
            var me = this;
            doMany(captureOne, me.data || (me.data = {}), target || me.target, props, prefix);
        },

        
        remove: function (props) {
            doMany(removeOne, this.data, null, props);
        },

        
        restore: function (props, clear, target, prefix) {
            doMany(restoreOne, this.data, target || this.target, props, prefix);
            if (clear !== false) {
                this.remove(props);
            }
        },

        
        restoreAll: function (clear, target) {
            var me   = this,
                t    = target || this.target,
                data = me.data,
                prop;

            for (prop in data) {
                if (data.hasOwnProperty(prop)) {
                    restoreValue(t, prop, data[prop]);
                }
            }

            if (clear !== false) {
                delete me.data;
            }
        }
    };
}()));


Ext.define('Ext.panel.Panel', {
    extend:  Ext.panel.AbstractPanel ,
               
                           
                      
                          
                       
                        
                                    
                          
      
    alias: 'widget.panel',
    alternateClassName: 'Ext.Panel',

    
    collapsedCls: 'collapsed',

    
    animCollapse: Ext.enableFx,

    
    minButtonWidth: 75,

    
    collapsed: false,

    
    collapseFirst: true,

    
    hideCollapseTool: false,

    
    titleCollapse: undefined,

    

    

    
    floatable: true,

    

    
    collapsible: undefined,

    

    
    closable: false,

    
    closeAction: 'destroy',

    

    
    placeholderCollapseHideMode: Ext.Element.VISIBILITY,

    
     preventHeader: false,

    
    header: undefined,

    
    headerPosition: 'top',

    
    frame: false,

    
    frameHeader: true,

    

    

    

    
    manageHeight: true,

    
    
    

    

    

    
    constrain: false,

    
    constrainHeader: false,

    
    
    initComponent: function() {
        var me = this;

        me.addEvents(

            
            'beforeclose',
            
            
            'close',

            
            "beforeexpand",

            
            "beforecollapse",

            
            "expand",

            
            "collapse",

            
            'titlechange',

            
            'iconchange',
            
            
            'iconclschange',

            
            'glyphchange',

            
            'float',

            
            'unfloat'
        );

        if (me.collapsible) {
        
            this.addStateEvents(['expand', 'collapse']);
        }
        if (me.unstyled) {
            me.setUI('plain');
        }

        if (me.frame) {
            me.setUI(me.ui + '-framed');
        }

        
        me.bridgeToolbars();

        me.callParent();
        me.collapseDirection = me.collapseDirection || me.headerPosition || Ext.Component.DIRECTION_TOP;

        
        me.hiddenOnCollapse = new Ext.dom.CompositeElement();

    },

    beforeDestroy: function() {
        var me = this;
        Ext.destroy(
            me.placeholder,
            me.ghostPanel,
            me.dd
        );
        me.callParent();
    },

    initAria: function() {
        this.callParent();
        this.initHeaderAria();
    },

    getFocusEl: function() {
        return  this.el;
    },

    initHeaderAria: function() {
        var me = this,
            el = me.el,
            header = me.header;
        if (el && header) {
            el.dom.setAttribute('aria-labelledby', header.titleCmp.id);
        }
    },

    
    getHeader: function() {
        return this.header;
    },

    
    setTitle: function(newTitle) {
        var me = this,
            oldTitle = me.title,
            header = me.header,
            reExpander = me.reExpander,
            placeholder = me.placeholder;

        me.title = newTitle;

        if (header) {
            if (header.isHeader) {
                header.setTitle(newTitle);
            } else {
                header.title = newTitle;
            }
        } else if (me.rendered) {
            me.updateHeader();
        }

        if (reExpander) {
            reExpander.setTitle(newTitle);
        }

        if (placeholder && placeholder.setTitle) {
            placeholder.setTitle(newTitle);
        }

        me.fireEvent('titlechange', me, newTitle, oldTitle);
    },

    
    setIconCls: function(newIconCls) {
        var me = this,
            oldIconCls = me.iconCls,
            header = me.header,
            placeholder = me.placeholder;

        me.iconCls = newIconCls;

        if (header) {
            if (header.isHeader) {
                header.setIconCls(newIconCls);
            } else {
                header.iconCls = newIconCls;
            }
        } else {
            me.updateHeader();
        }

        if (placeholder && placeholder.setIconCls) {
            placeholder.setIconCls(newIconCls);
        }

        me.fireEvent('iconclschange', me, newIconCls, oldIconCls);
    },
    
    
    setIcon: function(newIcon) {
        var me = this,
            oldIcon = me.icon,
            header = me.header,
            placeholder = me.placeholder;

        me.icon = newIcon;

        if (header) {
            if (header.isHeader) {
                header.setIcon(newIcon);
            } else {
                header.icon = newIcon;
            }
        } else {
            me.updateHeader();
        }

        if (placeholder && placeholder.setIcon) {
            placeholder.setIcon(newIcon);
        }

        me.fireEvent('iconchange', me, newIcon, oldIcon);
    },

    
    setGlyph: function(newGlyph) {
        var me = this,
            oldGlyph = me.glyph,
            header = me.header,
            placeholder = me.placeholder;

        me.glyph = newGlyph;

        if (header) {
            if (header.isHeader) {
                header.setGlyph(newGlyph);
            } else {
                header.glyph = newGlyph;
            }
        } else {
            me.updateHeader();
        }

        if (placeholder && placeholder.setGlyph) {
            placeholder.setIcon(newGlyph);
        }

        me.fireEvent('glyphchange', me, newGlyph, oldGlyph);
    },

    bridgeToolbars: function() {
        var me = this,
            docked = [],
            fbar,
            fbarDefaults,
            minButtonWidth = me.minButtonWidth;

        function initToolbar (toolbar, pos, useButtonAlign) {
            if (Ext.isArray(toolbar)) {
                toolbar = {
                    xtype: 'toolbar',
                    items: toolbar
                };
            }
            else if (!toolbar.xtype) {
                toolbar.xtype = 'toolbar';
            }
            toolbar.dock = pos;
            if (pos == 'left' || pos == 'right') {
                toolbar.vertical = true;
            }

            
            if (useButtonAlign) {
                toolbar.layout = Ext.applyIf(toolbar.layout || {}, {
                    
                    pack: { left:'start', center:'center' }[me.buttonAlign] || 'end'
                });
            }
            return toolbar;
        }

        

        

        
        if (me.tbar) {
            docked.push(initToolbar(me.tbar, 'top'));
            me.tbar = null;
        }

        
        if (me.bbar) {
            docked.push(initToolbar(me.bbar, 'bottom'));
            me.bbar = null;
        }

        
        if (me.buttons) {
            me.fbar = me.buttons;
            me.buttons = null;
        }

        
        if (me.fbar) {
            fbar = initToolbar(me.fbar, 'bottom', true); 
            fbar.ui = 'footer';

            
            if (minButtonWidth) {
                fbarDefaults = fbar.defaults;
                fbar.defaults = function(config) {
                    var defaults = fbarDefaults || {};
                    if ((!config.xtype || config.xtype === 'button' || (config.isComponent && config.isXType('button'))) &&
                            !('minWidth' in defaults)) {
                        defaults = Ext.apply({minWidth: minButtonWidth}, defaults);
                    }
                    return defaults;
                };
            }

            docked.push(fbar);
            me.fbar = null;
        }

        
        if (me.lbar) {
            docked.push(initToolbar(me.lbar, 'left'));
            me.lbar = null;
        }

        
        if (me.rbar) {
            docked.push(initToolbar(me.rbar, 'right'));
            me.rbar = null;
        }

        if (me.dockedItems) {
            if (!Ext.isArray(me.dockedItems)) {
                me.dockedItems = [me.dockedItems];
            }
            me.dockedItems = me.dockedItems.concat(docked);
        } else {
            me.dockedItems = docked;
        }
    },

    isPlaceHolderCollapse: function(){
        return this.collapseMode == 'placeholder';
    },

    onBoxReady: function(){
        this.callParent();
        if (this.collapsed) {
            this.setHiddenDocked();
        }    
    },

    beforeRender: function() {
        var me = this,
            wasCollapsed;

        me.callParent();

        
        
        me.initTools();

        
        if (!(me.preventHeader || (me.header === false))) {
            me.updateHeader();
        }

        
        if (me.collapsed) {
            if (me.isPlaceHolderCollapse()) {
                if (!me.hidden) {
                    me.setHiddenState(true);

                    
                    
                    
                    
                    me.preventCollapseFire = true;
                    me.placeholderCollapse();
                    delete me.preventCollapseFire;
                    wasCollapsed = me.collapsed;

                    
                    
                    
                    me.collapsed = false;
                }
            } else {
                me.beginCollapse();
                me.addClsWithUI(me.collapsedCls);
            }
        }

        
        if (wasCollapsed) {
            me.collapsed = wasCollapsed;
        }
    },

    
    initTools: function() {
        var me = this,
            tools = me.tools,
            i, tool;

        me.tools = [];
        for (i = tools && tools.length; i; ) {
            --i;
            me.tools[i] = tool = tools[i];
            tool.toolOwner = me;
        }

        
        
        if (me.collapsible && !(me.hideCollapseTool || me.header === false || me.preventHeader)) {
            me.collapseDirection = me.collapseDirection || me.headerPosition || 'top';
            me.collapseTool = me.expandTool = Ext.widget({
                xtype: 'tool',
                handler: me.toggleCollapse,
                scope: me
            });

            me.updateCollapseTool();
            
            if (me.collapseFirst) {
                me.tools.unshift(me.collapseTool);
            }
        }

        
        me.addTools();

        
        if (me.closable) {
            me.addClsWithUI('closable');
            me.addTool(Ext.widget({
                xtype : 'tool',
                type: 'close',
                handler: Ext.Function.bind(me.close, me, [])
            }));
        }

        
        if (me.collapseTool && !me.collapseFirst) {
            me.addTool(me.collapseTool);
        }
    },

    
    addTools: Ext.emptyFn,

    updateCollapseTool: function () {
        var me = this,
            collapseTool = me.collapseTool;
    
        if (collapseTool) {
            if (me.collapsed && !me.isPlaceHolderCollapse()) {
                collapseTool.setType('expand-' + me.getOppositeDirection(me.collapseDirection));
            } else {
                collapseTool.setType('collapse-' + me.collapseDirection);
            }
        }
    },

    
    close: function() {
        if (this.fireEvent('beforeclose', this) !== false) {
            this.doClose();
        }
    },

    
    doClose: function() {
        this.fireEvent('close', this);
        this[this.closeAction]();
    },

    
    updateHeader: function(force) {
        var me = this,
            header = me.header,
            title = me.title,
            tools = me.tools,
            icon = me.icon || me.iconCls,
            vertical = me.headerPosition === 'left' || me.headerPosition === 'right';

        if (Ext.isObject(header) || (header !== false && (force || (title || icon) || (tools && tools.length) || (me.collapsible && !me.titleCollapse)))) {
            if (header && header.isHeader) {
                header.show();
            } else {
                
                header = me.header = Ext.widget(Ext.apply({
                    xtype       : 'header',
                    title       : title,
                    titleAlign  : me.titleAlign,
                    orientation : vertical ? 'vertical' : 'horizontal',
                    dock        : me.headerPosition || 'top',
                    textCls     : me.headerTextCls,
                    iconCls     : me.iconCls,
                    icon        : me.icon,
                    glyph       : me.glyph,
                    baseCls     : me.baseCls + '-header',
                    tools       : tools,
                    ui          : me.ui,
                    id          : me.id + '_header',
                    overCls: me.headerOverCls,
                    indicateDrag: me.draggable,
                    frame       : (me.frame || me.alwaysFramed) && me.frameHeader,
                    ignoreParentFrame : me.frame || me.overlapHeader,
                    ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement,
                    ownerCt     : me,
                    listeners   : me.collapsible && me.titleCollapse ? {
                        click: me.toggleCollapse,
                        scope: me
                    } : null
                }, me.header));
                
                
                
                me.addDocked(header, 0);
            }
            me.initHeaderAria();
        } else if (header) {
            header.hide();
        }
    },

    
    setUI: function(ui) {
        var me = this;

        me.callParent(arguments);

        if (me.header && me.header.rendered) {
            me.header.setUI(ui);
        }
    },

    
    getDefaultContentTarget: function() {
        return this.body;
    },

    getTargetEl: function() {
        var me = this;
        return me.body || me.protoBody || me.frameBody || me.el;
    },

    

    
    isVisible: function(deep){
        var me = this;
        if (me.collapsed && me.placeholder) {
            return me.placeholder.isVisible(deep);
        }
        return me.callParent(arguments);
    },

    
    onHide: function() {
        var me = this;
        if (me.collapsed && me.placeholder) {
            me.placeholder.hide();
        } else {
            me.callParent(arguments);
        }
    },

    
    onShow: function() {
        var me = this;
        if (me.collapsed && me.isPlaceHolderCollapse()) {
            
            me.setHiddenState(true);
            me.placeholderCollapse();
        } else {
            me.callParent(arguments);
        }
    },

    onRemoved: function(destroying) {
        var me = this;

        
        
        
        if (me.placeholder && !destroying) {
            me.ownerCt.remove(me.placeholder, false);
        }

        me.callParent(arguments);
    },

    
    addTool: function(tools) {
        if (!Ext.isArray(tools)) {
            tools = [tools];
        }

        var me     = this,
            header = me.header,
            t,
            tLen   = tools.length,
            tool;

        for (t = 0; t < tLen; t++) {
            tool = tools[t];
            tool.toolOwner = me;
            
            if (header && header.isHeader) {
                header.addTool(tool);
            } else {
                
                
                me.tools.push(tool);
            }
        }

        me.updateHeader();
    },

    getOppositeDirection: function(d) {
        var c = Ext.Component;
        switch (d) {
            case c.DIRECTION_TOP:
                return c.DIRECTION_BOTTOM;
            case c.DIRECTION_RIGHT:
                return c.DIRECTION_LEFT;
            case c.DIRECTION_BOTTOM:
                return c.DIRECTION_TOP;
            case c.DIRECTION_LEFT:
                return c.DIRECTION_RIGHT;
        }
    },

    getWidthAuthority: function() {
        if (this.collapsed && this.collapsedHorizontal()) {
            return 1; 
        }

        return this.callParent();
    },

    getHeightAuthority: function() {
        if (this.collapsed && this.collapsedVertical()) {
            return 1; 
        }

        return this.callParent();
    },

    collapsedHorizontal: function () {
        var dir = this.getCollapsed();
        return dir === 'left' || dir === 'right';
    },

    collapsedVertical: function () {
        var dir = this.getCollapsed();
        return dir === 'top' || dir === 'bottom';
    },

    restoreDimension: function(){
        var dir = this.collapseDirection;
        
        
        return (dir === 'top' || dir === 'bottom') ? 'height' : 'width';    
    },

    
    getCollapsed: function() {
        var me = this;
        
        
        if (me.collapsed === true) {
            return me.collapseDirection;
        }
        return me.collapsed;
    },

    getState: function() {
        var me = this,
            state = me.callParent(),
            memento;

        state = me.addPropertyToState(state, 'collapsed');

        
        if (me.collapsed) {
            memento = me.collapseMemento;
            memento = memento && memento.data;

            if (me.collapsedVertical()) {
                if (state) {
                    delete state.height;
                }
                if (memento) {
                    state = me.addPropertyToState(state, 'height', memento.height);
                }
            } else {
                if (state) {
                    delete state.width;
                }
                if (memento) {
                    state = me.addPropertyToState(state, 'width', memento.width);
                }
            }
        }

        return state;
    },

    findReExpander: function (direction) {
        var me = this,
            c = Ext.Component,
            dockedItems = me.dockedItems.items,
            dockedItemCount = dockedItems.length,
            comp, i;
            
        
        if (me.collapseMode === 'mini') {
            return;
        }

        switch (direction) {
            case c.DIRECTION_TOP:
            case c.DIRECTION_BOTTOM:

                
                
                for (i = 0; i < dockedItemCount; i++) {
                    comp = dockedItems[i];
                    if (!comp.hidden) {
                        if (comp.isHeader && (!comp.dock || comp.dock === 'top' || comp.dock === 'bottom')) {
                            return comp;
                        }
                    }
                }
                break;

            case c.DIRECTION_LEFT:
            case c.DIRECTION_RIGHT:

                
                
                for (i = 0; i < dockedItemCount; i++) {
                    comp = dockedItems[i];
                    if (!comp.hidden) {
                        if (comp.isHeader && (comp.dock === 'left' || comp.dock === 'right')) {
                            return comp;
                        }
                    }
                }
                break;

            default:
                throw('Panel#findReExpander must be passed a valid collapseDirection');
        }
    },

    getReExpander: function (direction) {
        var me = this,
            collapseDir = direction || me.collapseDirection,
            reExpander = me.reExpander || me.findReExpander(collapseDir);

        me.expandDirection = me.getOppositeDirection(collapseDir);

        if (!reExpander) {
        
            me.reExpander = reExpander = me.createReExpander(collapseDir, {
                dock: collapseDir,
                cls: Ext.baseCSSPrefix + 'docked ' + me.baseCls + '-' + me.ui + '-collapsed',
                isCollapsedExpander: true
            });

            me.dockedItems.insert(0, reExpander);
        }
        return reExpander;
    },

    createReExpander: function(direction, defaults) {
        var me = this,
            isLeft = direction === 'left',
            isRight = direction === 'right',
            isVertical = isLeft || isRight,
            result = Ext.apply({
                hideMode: 'offsets',
                title: me.title || '&#160;',
                titleAlign: me.titleAlign,
                orientation: isVertical ? 'vertical' : 'horizontal',
                textCls: me.headerTextCls,
                icon: me.icon,
                iconCls: me.iconCls,
                glyph: me.glyph,
                baseCls: me.self.prototype.baseCls + '-header',
                ui: me.ui,
                frame: me.frame && me.frameHeader,
                ignoreParentFrame: me.frame || me.overlapHeader,
                indicateDrag: me.draggable,
                collapseImmune: true,
                ownerCt: me.ownerCt,
                ownerLayout: me.componentLayout,
                margin: me.margin
            }, defaults);
            
        
        
        if (me.collapseMode === 'mini') {
            if (isVertical) {
                result.width = 1;
            } else {
                result.height = 1;
            }
        }

        
        
        
        if (!me.hideCollapseTool) {
            if (isLeft || (isRight && me.isPlaceHolderCollapse())) {
                
                
                result.titlePosition = 1;
            }
            result.tools = [{
                xtype: 'tool',
                type: 'expand-' + me.getOppositeDirection(direction),
                uiCls: ['top'],
                handler: me.toggleCollapse,
                scope: me
            }];
        }
        result = new Ext.panel.Header(result);
        result.addClsWithUI(me.getHeaderCollapsedClasses(result));
        return result;
    },

    
    
    getHeaderCollapsedClasses: function(header) {
        var me = this,
            collapsedCls = me.collapsedCls,
            collapsedClasses;

        collapsedClasses = [ collapsedCls, collapsedCls + '-' + header.getDockName()];
        if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
            collapsedClasses.push(collapsedCls + '-border-' + header.getDockName());
        }
        return collapsedClasses;
    },

    
    beginCollapse: function() {
        var me = this,
            lastBox = me.lastBox,
            rendered = me.rendered,
            collapseMemento = me.collapseMemento || (me.collapseMemento = new Ext.util.Memento(me)),
            sizeModel = me.getSizeModel(),
            header = me.header,
            reExpander;

        
        
        
        
        
        
        
        
        
        
        
        collapseMemento.capture(['height', 'minHeight', 'width', 'minWidth']);
        if (lastBox) {
            collapseMemento.capture(me.restoreDimension(), lastBox, 'last.');
        }
        
        
        
        
        
        if (me.collapsedVertical()) {
            if (sizeModel.width.shrinkWrap) {
                me.width = rendered ? me.getWidth() : me.width || me.minWidth || 100;
            }
            delete me.height;
            me.minHeight = 0;
        } else if (me.collapsedHorizontal()) {
            if (sizeModel.height.shrinkWrap) {
                me.height = rendered ? me.getHeight() : me.height || me.minHeight || 100;
            }
            delete me.width;
            me.minWidth = 0;
        }

        if (me.ownerCt) {
            me.ownerCt.getLayout().beginCollapse(me);
        }

        
        
        if (!me.isPlaceHolderCollapse() && header !== false) {
            if (header === (reExpander = me.getReExpander())) {
                header.collapseImmune = true;
                header.getHierarchyState().collapseImmune = true;
                header.addClsWithUI(me.getHeaderCollapsedClasses(header));

                
                if (header.rendered) {
                    header.updateFrame();
                }
            } else if (reExpander.el) {
                
                reExpander.el.show();
                reExpander.hidden = false;
            }
        }
        if (me.resizer) {
            me.resizer.disable();
        }
    },

    beginExpand: function() {
        var me = this,
            lastBox = me.lastBox,
            collapseMemento = me.collapseMemento,
            restoreDimension = this.restoreDimension(),
            header = me.header,
            reExpander;

        if (collapseMemento) {
            collapseMemento.restore(['minHeight', 'minWidth', restoreDimension]);
            if (lastBox) {
                collapseMemento.restore(restoreDimension, true, lastBox, 'last.');
            }
        }

        if (me.ownerCt) {
            me.ownerCt.getLayout().beginExpand(me);
        }

        if (!me.isPlaceHolderCollapse() && header !== false) {
            
            if (header === (reExpander = me.getReExpander())) {
                delete header.collapseImmune;
                delete header.getHierarchyState().collapseImmune;
                header.removeClsWithUI(me.getHeaderCollapsedClasses(header));

                
                if (header.rendered) {
                    header.expanding = true;
                    header.updateFrame();
                    delete header.expanding;
                }
            } else {
                
                reExpander.hidden = true;
                reExpander.el.hide();
            }
        }
        if (me.resizer) {
            me.resizer.enable();
        }
    },

    
    collapse: function(direction, animate) {
        var me = this,
            collapseDir = direction || me.collapseDirection,
            ownerCt = me.ownerCt;

        if (me.isCollapsingOrExpanding) {
            return me;
        }

        if (arguments.length < 2) {
            animate = me.animCollapse;
        }

        if (me.collapsed || me.fireEvent('beforecollapse', me, direction, animate) === false) {
            return me;
        }

        if (ownerCt && me.isPlaceHolderCollapse()) {
            return me.placeholderCollapse(direction, animate);
        }

        me.collapsed = collapseDir;
        me.beginCollapse();

        me.getHierarchyState().collapsed = true;
        me.fireHierarchyEvent('collapse');

        return me.doCollapseExpand(1, animate);
    },

    doCollapseExpand: function (flags, animate) {
        var me = this,
            originalAnimCollapse = me.animCollapse,
            ownerLayout = me.ownerLayout;

        
        
        me.animCollapse = animate;

        
        
        me.isCollapsingOrExpanding = flags;

        
        
        if (animate) {
            me.addCls(Ext.baseCSSPrefix + 'animating-size');
        }

        if (ownerLayout && !animate) {
            ownerLayout.onContentChange(me);
        } else {
            me.updateLayout({ isRoot: true });
        }

        
        me.animCollapse = originalAnimCollapse;

        return me;
    },

    
    afterCollapse: function(animated) {
        var me = this,
            ownerLayout = me.ownerLayout;

        me.isCollapsingOrExpanding = 0;
        me.updateCollapseTool();

        
        
        if (animated) {
            me.removeCls(Ext.baseCSSPrefix + 'animating-size');
        }

        if (ownerLayout && animated) {
            ownerLayout.onContentChange(me);
        }

        me.setHiddenDocked();
        me.fireEvent('collapse', me);
    },
    
    setHiddenDocked: function(){
        
        
        var me = this,
            toHide = me.hiddenOnCollapse,
            items = me.getDockedItems(),
            len = items.length,
            i = 0,
            item, reExpander;
            
        if (me.header !== false) {
            reExpander = me.getReExpander();
        }
            
        toHide.add(me.body);
        for (; i < len; i++) {
            item = items[i];
            if (item && item !== reExpander && item.el) {
                toHide.add(item.el);
            }
        }
        toHide.setStyle('visibility', 'hidden');
    },
    
    restoreHiddenDocked: function(){
        var toShow = this.hiddenOnCollapse;
        
        toShow.setStyle('visibility', '');
        toShow.clear();
    },

    getPlaceholder: function(direction) {
        var me = this,
            collapseDir = direction || me.collapseDirection,
            listeners = null,
            placeholder = me.placeholder,
            floatable = me.floatable,
            titleCollapse = me.titleCollapse;

        if (!placeholder) {
            if (floatable || (me.collapsible && titleCollapse)) {
                listeners = {
                    click: {
                        
                        fn: (!titleCollapse && floatable) ? me.floatCollapsedPanel : me.toggleCollapse,
                        element: 'el',
                        scope: me
                    }
                };
            }

            me.placeholder = placeholder = Ext.widget(me.createReExpander(collapseDir, {
                id: me.id + '-placeholder',
                listeners: listeners
            }));
        }

        
        if (!placeholder.placeholderFor) {
            
            if (!placeholder.isComponent) {
                me.placeholder = placeholder = me.lookupComponent(placeholder);
            }
            Ext.applyIf(placeholder, {
                margins: me.margins,
                placeholderFor: me
            });

            placeholder.addCls([Ext.baseCSSPrefix + 'region-collapsed-placeholder', Ext.baseCSSPrefix + 'region-collapsed-' + collapseDir + '-placeholder', me.collapsedCls]);
        }

        return placeholder;
    },

    placeholderCollapse: function(direction, animate) {
        var me = this,
            ownerCt = me.ownerCt,
            collapseDir = direction || me.collapseDirection,
            floatCls = Ext.baseCSSPrefix + 'border-region-slide-in',
            placeholder = me.getPlaceholder(collapseDir),
            slideInDirection;

        me.isCollapsingOrExpanding = 1;

        
        me.setHiddenState(true);
        me.collapsed = collapseDir;

        if (placeholder.rendered) {
            
            if (placeholder.el.dom.parentNode !== me.el.dom.parentNode) {
                me.el.dom.parentNode.insertBefore(placeholder.el.dom, me.el.dom);
            }

            placeholder.hidden = false;
            placeholder.el.show();
            ownerCt.updateLayout();
        } else {
            ownerCt.insert(ownerCt.items.indexOf(me), placeholder);
        }

        if (me.rendered) {
            
            me.el.setVisibilityMode(me.placeholderCollapseHideMode);
            if (animate) {
                me.el.addCls(floatCls);
                placeholder.el.hide();
                slideInDirection = me.convertCollapseDir(collapseDir);

                me.el.slideOut(slideInDirection, {
                    preserveScroll: true,
                    duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            me.el.removeCls(floatCls);
                            
                            placeholder.el.show().setStyle('display', 'none').slideIn(slideInDirection, {
                                easing: 'linear',
                                duration: 100,
                                listeners: {
                                    afteranimate: function() {
                                        placeholder.focus();
                                        me.isCollapsingOrExpanding = 0;
                                        me.fireEvent('collapse', me);
                                    }
                                }
                            });
                        }
                    }
                });
            } else {
                me.el.hide();
                me.isCollapsingOrExpanding = 0;
                me.fireEvent('collapse', me);
            }
        } else {
            me.isCollapsingOrExpanding = 0;
            if (!me.preventCollapseFire) {
                me.fireEvent('collapse', me);
            }
        }

        return me;
    },

    floatCollapsedPanel: function() {
        var me = this,
            placeholder = me.placeholder,
            ps = placeholder.getSize(),
            myBox,
            floatCls = Ext.baseCSSPrefix + 'border-region-slide-in',
            collapsed = me.collapsed,
            layoutOwner = me.ownerCt || me,
            slideDirection;

        if (me.isSliding) {
            return;
        }

        
        if (me.el.hasCls(floatCls)) {
            me.slideOutFloatedPanel();
            return;
        }
        me.isSliding = true;

        
        placeholder.el.hide();
        placeholder.hidden = true;
        me.el.show();
        me.setHiddenState(false);
        me.collapsed = false;
        layoutOwner.updateLayout();
        myBox = me.getBox(false, true);

        
        placeholder.el.show();
        placeholder.hidden = false;
        me.el.hide();
        me.setHiddenState(true);
        me.collapsed = collapsed;
        layoutOwner.updateLayout();

        me.slideOutTask = me.slideOutTask || new Ext.util.DelayedTask(me.slideOutFloatedPanel, me);
        placeholder.el.on('mouseleave', me.onMouseLeaveFloated, me);
        me.el.on('mouseleave', me.onMouseLeaveFloated, me);
        placeholder.el.on('mouseenter', me.onMouseEnterFloated, me);
        me.el.on('mouseenter', me.onMouseEnterFloated, me);

        me.el.addCls(floatCls);
        me.floated = true;

        
        if (me.collapseTool) {
            me.collapseTool.el.hide();
        }

        switch (me.collapsed) {
            case 'top':
                me.setLocalXY(myBox.x, myBox.y + ps.height - 1);
                break;
            case 'right':
                me.setLocalXY(myBox.x - ps.width + 1, myBox.y);
                break;
            case 'bottom':
                me.setLocalXY(myBox.x, myBox.y - ps.height + 1);
                break;
            case 'left':
                me.setLocalXY(myBox.x + ps.width - 1, myBox.y);
                break;
        }
        slideDirection = me.convertCollapseDir(me.collapsed);

        
        
        me.floatedFromCollapse = me.collapsed;
        me.collapsed = false;
        me.setHiddenState(false);

        me.el.slideIn(slideDirection, {
            preserveScroll: true,
            duration: Ext.Number.from(me.animCollapse, Ext.fx.Anim.prototype.duration),
            listeners: {
                afteranimate: function() {
                    me.isSliding = false;
                    me.fireEvent('float', me);
                }
            }
        });
    },

    onMouseLeaveFloated: function(e) {
        this.slideOutTask.delay(500);
    },

    onMouseEnterFloated: function(e) {
        this.slideOutTask.cancel();
    },

    isLayoutRoot: function() {
        if (this.floatedFromCollapse) {
            return true;
        }
        return this.callParent();
    },

    slideOutFloatedPanel: function() {
        var me = this,
            compEl = this.el,
            collapseDirection;

        if (me.isSliding || me.isDestroyed) {
            return;
        }

        me.isSliding = true;
        me.floated = false;

        me.slideOutFloatedPanelBegin();

        if (typeof me.collapsed == 'string') {
            collapseDirection = me.convertCollapseDir(me.collapsed);
        }

        compEl.slideOut(collapseDirection, {
            preserveScroll: true,
            duration: Ext.Number.from(me.animCollapse, Ext.fx.Anim.prototype.duration),
            listeners: {
                afteranimate: function() {
                    me.slideOutFloatedPanelEnd();
                    
                    
                    me.el.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
                }
            }
        });
    },

    
    slideOutFloatedPanelBegin: function() {
        var me = this,
            placeholderEl = me.placeholder.el,
            el = me.el;

        me.collapsed = me.floatedFromCollapse;
        me.setHiddenState(true);
        me.floatedFromCollapse = null;

        
        placeholderEl.un('mouseleave', me.onMouseLeaveFloated, me);
        el.un('mouseleave', me.onMouseLeaveFloated, me);
        placeholderEl.un('mouseenter', me.onMouseEnterFloated, me);
        el.un('mouseenter', me.onMouseEnterFloated, me);
    },

    
    slideOutFloatedPanelEnd: function() {
        var me = this;

        if (me.collapseTool) {
            me.collapseTool.el.show();
        }
        me.slideOutTask.cancel();
        me.isSliding = false;
        me.fireEvent('unfloat', me);
    },

    
    expand: function(animate) {
        var me = this;

        if (me.isCollapsingOrExpanding) {
            return me;
        }

        if (!arguments.length) {
            animate = me.animCollapse;
        }

        if (!me.collapsed && !me.floatedFromCollapse) {
            return me;
        }

        if (me.fireEvent('beforeexpand', me, animate) === false) {
            return me;
        }

        delete this.getHierarchyState().collapsed;

        if (me.isPlaceHolderCollapse()) {
            return me.placeholderExpand(animate);
        }

        me.restoreHiddenDocked();
        me.beginExpand();
        me.collapsed = false;

        return me.doCollapseExpand(2, animate);
    },

    placeholderExpand: function(animate) {
        var me = this,
            collapseDir = me.collapsed,
            floatCls = Ext.baseCSSPrefix + 'border-region-slide-in',
            finalPos,
            floatedPos,
            center = me.ownerLayout ? me.ownerLayout.centerRegion: null;

        
        if (Ext.AbstractComponent.layoutSuspendCount) {
            animate = false;
        }

        if (me.floatedFromCollapse) {
            floatedPos = me.getPosition(true);
            
            me.slideOutFloatedPanelBegin();
            me.slideOutFloatedPanelEnd();
            me.floated = false;
        }

        if (animate) {

            
            Ext.suspendLayouts();
            me.placeholder.hide();
            me.el.show();
            me.collapsed = false;
            me.setHiddenState(false);

            
            
            if (center && !floatedPos) {
                center.hidden = true;
            }

            Ext.resumeLayouts(true);
            center.hidden = false;
            me.el.addCls(floatCls);

            
            
            
            
            
            
            
            
            
            

            me.isCollapsingOrExpanding = 2;

            
            if (floatedPos) {
                finalPos = me.getXY();
                me.setLocalXY(floatedPos[0], floatedPos[1]);
                me.setXY([finalPos[0], finalPos[1]], {
                    duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            me.el.removeCls(floatCls);
                            me.isCollapsingOrExpanding = 0;
                            me.fireEvent('expand', me);
                        }
                    }
                });
            }
            
            else {
                me.el.hide();
                me.placeholder.el.show();
                me.placeholder.hidden = false;

                
                me.setHiddenState(false);
                me.el.slideIn(me.convertCollapseDir(collapseDir), {
                    preserveScroll: true,
                    duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            
                            
                            
                            
                            
                            me.el.removeCls(floatCls);
                            me.placeholder.hide();

                            
                            me.updateLayout();

                            me.isCollapsingOrExpanding = 0;
                            me.fireEvent('expand', me);
                        }
                    }
                });
            }

        } else {
            me.floated = me.collapsed = false;
            me.el.removeCls(floatCls);
            Ext.suspendLayouts();
            me.placeholder.hide();
            me.show();
            Ext.resumeLayouts(true);
            me.fireEvent('expand', me);
        }

        return me;
    },

    
    afterExpand: function(animated) {
        var me = this,
            ownerLayout = me.ownerLayout;

        me.isCollapsingOrExpanding = 0;
        me.updateCollapseTool();

        
        
        if (animated) {
            me.removeCls(Ext.baseCSSPrefix + 'animating-size');
        }

        if (ownerLayout && animated) {
            ownerLayout.onContentChange(me);
        }

        me.fireEvent('expand', me);
        me.fireHierarchyEvent('expand');
    },
    
    
    setBorder: function(border, targetEl) {
        if (targetEl) {
            
            return;
        }
        
        var me = this,
            header = me.header;
            
        if (!border) {
            border = 0;
        } else if (border === true) {
            border = '1px';
        } else {
            border = me.unitizeBox(border);
        }
        
        if (header) {
            if (header.isHeader) {
                header.setBorder(border);
            } else {
                header.border = border;
            }
        }
        
        if (me.rendered && me.bodyBorder !== false) {
            me.body.setStyle('border-width', border);
        }
        me.updateLayout();
        
        me.border = border;
    },

    
    toggleCollapse: function() {
        return (this.collapsed || this.floatedFromCollapse) ? this.expand() : this.collapse();
    },

    
    getKeyMap : function() {
        return this.keyMap || (this.keyMap = new Ext.util.KeyMap(Ext.apply({
            target: this.el
        }, this.keys)));
    },

    
    initDraggable : function() {
        
        
        if (this.simpleDrag) {
            this.initSimpleDraggable();
        }
        
        else {
            
            this.dd = new Ext.panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable);
        }
    },

    
    initSimpleDraggable: function() {
        var me = this,
            ddConfig, dd;

        if (!me.header) {
            me.updateHeader(true);
        }

        
        if (me.header) {
            ddConfig = Ext.applyIf({
                el: me.el,
                delegate: '#' + Ext.escapeId(me.header.id)
            }, me.draggable);

            
            if (me.constrain || me.constrainHeader) {
                ddConfig.constrain = me.constrain;
                ddConfig.constrainDelegate = me.constrainHeader;
                ddConfig.constrainTo = me.constrainTo || me.container;
            }

            dd = me.dd = new Ext.util.ComponentDragger(this, ddConfig);
            me.relayEvents(dd, ['dragstart', 'drag', 'dragend']);
            if (me.maximized) {
                dd.disable();
            }
        }
    },
    
    
    
    ghostTools : function() {
        var tools = [],
            header = this.header,
            headerTools = header ? header.query('tool[hidden=false]') : [],
            t, tLen, tool;

        if (headerTools.length) {
            t = 0;
            tLen = headerTools.length;

            for (; t < tLen; t++) {
                tool = headerTools[t];

                
                
                
                
                tools.push({
                    type: tool.type
                });
            }
        } else {
            tools = [{
                type: 'placeholder'
            }];
        }
        return tools;
    },

    
    
    ghost: function(cls) {
        var me = this,
            ghostPanel = me.ghostPanel,
            box = me.getBox(),
            header;

        if (!ghostPanel) {
            ghostPanel = new Ext.panel.Panel({
                renderTo: Ext.getBody(),
                floating: {
                    shadow: false
                },
                frame: me.frame && !me.alwaysFramed,
                alwaysFramed: me.alwaysFramed,
                overlapHeader: me.overlapHeader,
                headerPosition: me.headerPosition,
                baseCls: me.baseCls,
                cls: me.baseCls + '-ghost ' + (cls ||'')
            });
            me.ghostPanel = ghostPanel;
        } else {
            ghostPanel.el.show();
        }
        me.ghostPanel.hidden = false;
        ghostPanel.floatParent = me.floatParent;
        if (me.floating) {
            ghostPanel.zIndexManager.assignZIndices();
        } else {
            ghostPanel.toFront();
        }
        if (!(me.preventHeader || (me.header === false))) {
            header = ghostPanel.header;
            
            if (header) {
                header.suspendLayouts();
                Ext.Array.forEach(header.query('tool'), header.remove, header);
                header.resumeLayouts();
            }
            ghostPanel.addTool(me.ghostTools());
            ghostPanel.setTitle(me.title);

            if (me.iconCls) {
                ghostPanel.setIconCls(me.iconCls);
            } else if (me.icon) {
                ghostPanel.setIcon(me.icon);
            } else if (me.glyph) {
                ghostPanel.setGlyph(me.glyph);
            }
            
            ghostPanel.header.addCls(Ext.baseCSSPrefix + 'header-ghost');
        }

        ghostPanel.setPagePosition(box.x, box.y);
        ghostPanel.setSize(box.width, box.height);
        me.el.hide();
        return ghostPanel;
    },

    
    unghost: function(show, matchPosition) {
        var me = this;
        if (!me.ghostPanel) {
            return;
        }
        if (show !== false) {
            
            
            me.el.show();
            if (matchPosition !== false) {
                me.setPagePosition(me.ghostPanel.getXY());
                if (me.hideMode == 'offsets') {
                    
                    delete me.el.hideModeStyles;
                }
            }
            Ext.defer(me.focus, 10, me);
        }
        me.ghostPanel.el.hide();
        me.ghostPanel.hidden = true;
    },

    beginDrag: function() {
        if (this.floatingDescendants) {
            this.floatingDescendants.hide();
        }
    },

    endDrag: function() {
        if (this.floatingDescendants) {
            this.floatingDescendants.show();
        }
    },

    initResizable: function() {
        this.callParent(arguments);
        if (this.collapsed) {
            this.resizer.disable();
        }
    },

    
    convertCollapseDir: function(collapseDir) {
        return collapseDir.substr(0, 1);
    }
}, function() {
    this.prototype.animCollapse = Ext.enableFx;
});


Ext.define('Ext.tip.Tip', {
    extend:  Ext.panel.Panel ,

    alternateClassName: 'Ext.Tip',

    
    
    
    
    
    minWidth : 40,
    
    maxWidth : 500,
    
    shadow : "sides",

    
    defaultAlign : "tl-bl?",
    
    constrainPosition : true,

    
    autoRender: true,
    hidden: true,
    baseCls: Ext.baseCSSPrefix + 'tip',
    floating: {
        shadow: true,
        shim: true
    },
    focusOnToFront: false,

    
    closeAction: 'hide',

    ariaRole: 'tooltip',

    
    alwaysFramed: true,

    frameHeader: false,

    initComponent: function() {
        var me = this;

        me.floating = Ext.apply( {}, {
            shadow: me.shadow,
            constrain: me.constrainPosition
        }, me.self.prototype.floating);
        me.callParent(arguments);

        
        me.constrain = me.constrain || me.constrainPosition;
    },

    
    showAt : function(xy){
        var me = this;
        this.callParent(arguments);
        
        if (me.isVisible()) {
            me.setPagePosition(xy[0], xy[1]);
            if (me.constrainPosition || me.constrain) {
                me.doConstrain();
            }
            me.toFront(true);
        }
    },

    
    initDraggable : function(){
        var me = this;
        me.draggable = {
            el: me.getDragEl(),
            delegate: me.header.el,
            constrain: me,
            constrainTo: me.el.dom.parentNode
        };
        
        Ext.Component.prototype.initDraggable.call(me);
    },

    
    ghost: undefined,
    unghost: undefined
});


Ext.define('Ext.tip.ToolTip', {
    extend:  Ext.tip.Tip ,
    alias: 'widget.tooltip',
    alternateClassName: 'Ext.ToolTip',
    
    
    
    autoHide: true,
    
    
    showDelay: 500,
    
    hideDelay: 200,
    
    dismissDelay: 5000,
    
    
    trackMouse: false,
    
    
    anchorToTarget: true,
    
    anchorOffset: 0,
    

    
    targetCounter: 0,
    quickShowInterval: 250,

    
    initComponent: function() {
        var me = this;
        me.callParent(arguments);
        me.lastActive = new Date();
        me.setTarget(me.target);
        me.origAnchor = me.anchor;
    },

    
    onRender: function(ct, position) {
        var me = this;
        me.callParent(arguments);
        me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
        me.anchorEl = me.el.createChild({
            cls: Ext.baseCSSPrefix + 'tip-anchor ' + me.anchorCls
        });
    },

    
    setTarget: function(target) {
        var me = this,
            t = Ext.get(target),
            tg;

        if (me.target) {
            tg = Ext.get(me.target);
            me.mun(tg, 'mouseover', me.onTargetOver, me);
            me.mun(tg, 'mouseout', me.onTargetOut, me);
            me.mun(tg, 'mousemove', me.onMouseMove, me);
        }

        me.target = t;
        if (t) {

            me.mon(t, {
                
                
                freezeEvent: true,

                mouseover: me.onTargetOver,
                mouseout: me.onTargetOut,
                mousemove: me.onMouseMove,
                scope: me
            });
        }
        if (me.anchor) {
            me.anchorTarget = me.target;
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            t = me.delegate ? e.getTarget(me.delegate) : me.triggerElement = true,
            xy;
        if (t) {
            me.targetXY = e.getXY();
            if (t === me.triggerElement) {
                if (!me.hidden && me.trackMouse) {
                    xy = me.getTargetXY();
                    if (me.constrainPosition) {
                        xy = me.el.adjustForConstraints(xy, me.el.parent());
                    }
                    me.setPagePosition(xy);
                }
            } else {
                me.hide();
                me.lastActive = new Date(0);
                me.onTargetOver(e);
            }
        } else if ((!me.closable && me.isVisible()) && me.autoHide !== false) {
            me.hide();
        }
    },

    
    getTargetXY: function() {
        var me = this,
            mouseOffset,
            offsets, xy, dw, dh, de, bd, scrollX, scrollY, axy, sz, constrainPosition;
        if (me.delegate) {
            me.anchorTarget = me.triggerElement;
        }
        if (me.anchor) {
            me.targetCounter++;
            offsets = me.getOffsets();
            xy = (me.anchorToTarget && !me.trackMouse) ? me.getAlignToXY(me.anchorTarget, me.getAnchorAlign()) : me.targetXY;
            dw = Ext.Element.getViewWidth() - 5;
            dh = Ext.Element.getViewHeight() - 5;
            de = document.documentElement;
            bd = document.body;
            scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5;
            scrollY = (de.scrollTop || bd.scrollTop || 0) + 5;
            axy = [xy[0] + offsets[0], xy[1] + offsets[1]];
            sz = me.getSize();
            constrainPosition = me.constrainPosition;

            me.anchorEl.removeCls(me.anchorCls);

            if (me.targetCounter < 2 && constrainPosition) {
                if (axy[0] < scrollX) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'l-r';
                        if (me.mouseOffset) {
                            me.mouseOffset[0] *= -1;
                        }
                    }
                    me.anchor = 'left';
                    return me.getTargetXY();
                }
                if (axy[0] + sz.width > dw) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'r-l';
                        if (me.mouseOffset) {
                            me.mouseOffset[0] *= -1;
                        }
                    }
                    me.anchor = 'right';
                    return me.getTargetXY();
                }
                if (axy[1] < scrollY) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 't-b';
                        if (me.mouseOffset) {
                            me.mouseOffset[1] *= -1;
                        }
                    }
                    me.anchor = 'top';
                    return me.getTargetXY();
                }
                if (axy[1] + sz.height > dh) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'b-t';
                        if (me.mouseOffset) {
                            me.mouseOffset[1] *= -1;
                        }
                    }
                    me.anchor = 'bottom';
                    return me.getTargetXY();
                }
            }

            me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
            me.anchorEl.addCls(me.anchorCls);
            me.targetCounter = 0;
            return axy;
        } else {
            mouseOffset = me.getMouseOffset();
            return (me.targetXY) ? [me.targetXY[0] + mouseOffset[0], me.targetXY[1] + mouseOffset[1]] : mouseOffset;
        }
    },

    getMouseOffset: function() {
        var me = this,
        offset = me.anchor ? [0, 0] : [15, 18];
        if (me.mouseOffset) {
            offset[0] += me.mouseOffset[0];
            offset[1] += me.mouseOffset[1];
        }
        return offset;
    },

    
    getAnchorPosition: function() {
        var me = this,
            m;
        if (me.anchor) {
            me.tipAnchor = me.anchor.charAt(0);
        } else {
            m = me.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);
            me.tipAnchor = m[1].charAt(0);
        }

        switch (me.tipAnchor) {
        case 't':
            return 'top';
        case 'b':
            return 'bottom';
        case 'r':
            return 'right';
        }
        return 'left';
    },

    
    getAnchorAlign: function() {
        switch (this.anchor) {
        case 'top':
            return 'tl-bl';
        case 'left':
            return 'tl-tr';
        case 'right':
            return 'tr-tl';
        default:
            return 'bl-tl';
        }
    },

    
    getOffsets: function() {
        var me = this,
            mouseOffset,
            offsets,
            ap = me.getAnchorPosition().charAt(0);
        if (me.anchorToTarget && !me.trackMouse) {
            switch (ap) {
            case 't':
                offsets = [0, 9];
                break;
            case 'b':
                offsets = [0, -13];
                break;
            case 'r':
                offsets = [ - 13, 0];
                break;
            default:
                offsets = [9, 0];
                break;
            }
        } else {
            switch (ap) {
            case 't':
                offsets = [ - 15 - me.anchorOffset, 30];
                break;
            case 'b':
                offsets = [ - 19 - me.anchorOffset, -13 - me.el.dom.offsetHeight];
                break;
            case 'r':
                offsets = [ - 15 - me.el.dom.offsetWidth, -13 - me.anchorOffset];
                break;
            default:
                offsets = [25, -13 - me.anchorOffset];
                break;
            }
        }
        mouseOffset = me.getMouseOffset();
        offsets[0] += mouseOffset[0];
        offsets[1] += mouseOffset[1];

        return offsets;
    },

    
    onTargetOver: function(e) {
        var me = this,
            delegate = me.delegate,
            t;

        if (me.disabled || e.within(me.target.dom, true)) {
            return;
        }
        t = delegate ? e.getTarget(delegate) : true;
        if (t) {
            me.triggerElement = t;
            me.triggerEvent = e;
            me.clearTimer('hide');
            me.targetXY = e.getXY();
            me.delayShow();
        }
    },

    
    delayShow: function() {
        var me = this;
        if (me.hidden && !me.showTimer) {
            if (Ext.Date.getElapsed(me.lastActive) < me.quickShowInterval) {
                me.show();
            } else {
                me.showTimer = Ext.defer(me.showFromDelay, me.showDelay, me);
            }
        }
        else if (!me.hidden && me.autoHide !== false) {
            me.show();
        }
    },
    
    showFromDelay: function(){
        this.fromDelayShow = true;
        this.show();
        delete this.fromDelayShow;
    },
    
    onShowVeto: function(){
        this.callParent();
        delete this.triggerElement;
        this.clearTimer('show');
    },

    
    onTargetOut: function(e) {
        var me = this,
            triggerEl = me.triggerElement,
            
            
            target = triggerEl === true ? me.target : triggerEl;

        
        
        if (me.disabled || !triggerEl || e.within(target, true)) {
            return;
        }
        if (me.showTimer) {
            me.clearTimer('show');
            me.triggerElement = null;
        }
        if (me.autoHide !== false) {
            me.delayHide();
        }
    },

    
    delayHide: function() {
        var me = this;
        if (!me.hidden && !me.hideTimer) {
            me.hideTimer = Ext.defer(me.hide, me.hideDelay, me);
        }
    },

    
    hide: function() {
        var me = this;
        me.clearTimer('dismiss');
        me.lastActive = new Date();
        if (me.anchorEl) {
            me.anchorEl.hide();
        }
        me.callParent(arguments);
        delete me.triggerElement;
    },

    
    show: function() {
        var me = this;

        
        
        this.callParent();
        if (this.hidden === false) {
            me.setPagePosition(-10000, -10000);

            if (me.anchor) {
                me.anchor = me.origAnchor;
            }
            
            if (!me.calledFromShowAt) {
                me.showAt(me.getTargetXY());
            }

            if (me.anchor) {
                me.syncAnchor();
                me.anchorEl.show();
            } else {
                me.anchorEl.hide();
            }
        }
    },

    
    showAt: function(xy) {
        var me = this;
        me.lastActive = new Date();
        me.clearTimers();
        me.calledFromShowAt = true;

        
        if (!me.isVisible()) {
            this.callParent(arguments);
        }

        
        if (me.isVisible()) {
            me.setPagePosition(xy[0], xy[1]);
            if (me.constrainPosition || me.constrain) {
                me.doConstrain();
            }
            me.toFront(true);
            me.el.sync(true);
            if (me.dismissDelay && me.autoHide !== false) {
                me.dismissTimer = Ext.defer(me.hide, me.dismissDelay, me);
            }
            if (me.anchor) {
                me.syncAnchor();
                if (!me.anchorEl.isVisible()) {
                    me.anchorEl.show();
                }
            } else {
                me.anchorEl.hide();
            }
        }
        delete me.calledFromShowAt;
    },

    
    syncAnchor: function() {
        var me = this,
            anchorPos,
            targetPos,
            offset;
        switch (me.tipAnchor.charAt(0)) {
        case 't':
            anchorPos = 'b';
            targetPos = 'tl';
            offset = [20 + me.anchorOffset, 1];
            break;
        case 'r':
            anchorPos = 'l';
            targetPos = 'tr';
            offset = [ - 1, 12 + me.anchorOffset];
            break;
        case 'b':
            anchorPos = 't';
            targetPos = 'bl';
            offset = [20 + me.anchorOffset, -1];
            break;
        default:
            anchorPos = 'r';
            targetPos = 'tl';
            offset = [1, 12 + me.anchorOffset];
            break;
        }
        me.anchorEl.alignTo(me.el, anchorPos + '-' + targetPos, offset);
        me.anchorEl.setStyle('z-index', parseInt(me.el.getZIndex(), 10) || 0 + 1).setVisibilityMode(Ext.Element.DISPLAY);
    },

    
    setPagePosition: function(x, y) {
        var me = this;
        me.callParent(arguments);
        if (me.anchor) {
            me.syncAnchor();
        }
    },

    _timerNames: {},
    
    clearTimer: function (name) {
        var me = this,
            names = me._timerNames,
            propName = names[name] || (names[name] = name + 'Timer'),
            timer = me[propName];

        if (timer) {
            clearTimeout(timer);
            me[propName] = null;
        }
    },

    
    clearTimers: function() {
        var me = this;
        me.clearTimer('show');
        me.clearTimer('dismiss');
        me.clearTimer('hide');
    },

    
    onShow: function() {
        var me = this;
        me.callParent();
        me.mon(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
    },

    
    onHide: function() {
        var me = this;
        me.callParent();
        me.mun(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
    },

    
    onDocMouseDown: function(e) {
        var me = this;
        if (!me.closable && !e.within(me.el.dom)) {
            me.disable();
            Ext.defer(me.doEnable, 100, me);
        }
    },

    
    doEnable: function() {
        if (!this.isDestroyed) {
            this.enable();
        }
    },

    
    onDisable: function() {
        this.callParent();
        this.clearTimers();
        this.hide();
    },

    beforeDestroy: function() {
        var me = this;
        me.clearTimers();
        Ext.destroy(me.anchorEl);
        delete me.anchorEl;
        delete me.target;
        delete me.anchorTarget;
        delete me.triggerElement;
        me.callParent();
    },

    
    onDestroy: function() {
        Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
        this.callParent();
    }
});


Ext.define('Ext.tip.QuickTip', {
    extend:  Ext.tip.ToolTip ,
    alias: 'widget.quicktip',
    alternateClassName: 'Ext.QuickTip',

    

    
    interceptTitles : false,

    
    title: '&#160;',

    
    tagConfig : {
        namespace : 'data-',
        attribute : 'qtip',
        width : 'qwidth',
        target : 'target',
        title : 'qtitle',
        hide : 'hide',
        cls : 'qclass',
        align : 'qalign',
        anchor : 'anchor',
        showDelay: 'qshowDelay'
    },

    
    initComponent : function(){
        var me = this;

        me.target = me.target || Ext.getDoc();
        me.targets = me.targets || {};
        me.callParent();
    },

    
    register : function(config){
        var configs = Ext.isArray(config) ? config : arguments,
            i = 0,
            len = configs.length,
            target, j, targetLen;

        for (; i < len; i++) {
            config = configs[i];
            target = config.target;
            if (target) {
                if (Ext.isArray(target)) {
                    for (j = 0, targetLen = target.length; j < targetLen; j++) {
                        this.targets[Ext.id(target[j])] = config;
                    }
                } else{
                    this.targets[Ext.id(target)] = config;
                }
            }
        }
    },

    
    unregister : function(el){
        delete this.targets[Ext.id(el)];
    },

    
    cancelShow: function(el){
        var me = this,
            activeTarget = me.activeTarget;

        el = Ext.get(el).dom;
        if (me.isVisible()) {
            if (activeTarget && activeTarget.el == el) {
                me.hide();
            }
        } else if (activeTarget && activeTarget.el == el) {
            me.clearTimer('show');
        }
    },

    
    getTipCfg: function(e) {
        var t = e.getTarget(),
            titleText = t.title,
            cfg;

        if (this.interceptTitles && titleText && Ext.isString(titleText)) {
            t.qtip = titleText;
            t.removeAttribute("title");
            e.preventDefault();
            return {
                text: titleText
            };
        }
        else {
            cfg = this.tagConfig;
            t = e.getTarget('[' + cfg.namespace + cfg.attribute + ']');
            if (t) {
                return {
                    target: t,
                    text: t.getAttribute(cfg.namespace + cfg.attribute)
                };
            }
        }
    },

    
    onTargetOver : function(e){
        var me = this,
            target = e.getTarget(me.delegate),
            hasShowDelay,
            delay,
            elTarget,
            cfg,
            ns,
            tipConfig,
            autoHide,
            targets, targetEl, value, key;

        if (me.disabled) {
            return;
        }

        
        
        
        me.targetXY = e.getXY();

        
        if(!target || target.nodeType !== 1 || target == document.documentElement || target == document.body){
            return;
        }

        if (me.activeTarget && ((target == me.activeTarget.el) || Ext.fly(me.activeTarget.el).contains(target))) {
            
            
            
            
            if (me.targetTextEmpty()) {
                me.onShowVeto();
                delete me.activeTarget;
            } else {
                me.clearTimer('hide');
                me.show();
            }
            return;
        }

        if (target) {
            targets = me.targets;

            for (key in targets) {
                if (targets.hasOwnProperty(key)) {
                    value = targets[key];

                    targetEl = Ext.fly(value.target);
                    if (targetEl && (targetEl.dom === target || targetEl.contains(target))) {
                        elTarget = targetEl.dom;
                        break;
                    }
                }
            }

            if (elTarget) {
                me.activeTarget = me.targets[elTarget.id];
                me.activeTarget.el = target;
                me.anchor = me.activeTarget.anchor;
                if (me.anchor) {
                    me.anchorTarget = target;
                }
                hasShowDelay = parseInt(me.activeTarget.showDelay, 10);
                if (hasShowDelay) {
                    delay = me.showDelay;
                    me.showDelay = hasShowDelay;
                }
                me.delayShow();
                if (hasShowDelay) {
                    me.showDelay = delay;
                }
                return;
            }
        }

        
        elTarget = Ext.fly(target, '_quicktip-target');
        cfg = me.tagConfig;
        ns = cfg.namespace;
        tipConfig = me.getTipCfg(e);

        if (tipConfig) {

            
            
            if (tipConfig.target) {
                target = tipConfig.target;
                elTarget = Ext.fly(target, '_quicktip-target');
            }
            autoHide = elTarget.getAttribute(ns + cfg.hide);

            me.activeTarget = {
                el: target,
                text: tipConfig.text,
                width: +elTarget.getAttribute(ns + cfg.width) || null,
                autoHide: autoHide != "user" && autoHide !== 'false',
                title: elTarget.getAttribute(ns + cfg.title),
                cls: elTarget.getAttribute(ns + cfg.cls),
                align: elTarget.getAttribute(ns + cfg.align),
                showDelay: parseInt(elTarget.getAttribute(ns + cfg.showDelay), 10)
            };
            me.anchor = elTarget.getAttribute(ns + cfg.anchor);
            if (me.anchor) {
                me.anchorTarget = target;
            }
            hasShowDelay = parseInt(me.activeTarget.showDelay, 10);
            if (hasShowDelay) {
                delay = me.showDelay;
                me.showDelay = hasShowDelay;
            }
            me.delayShow();
            if (hasShowDelay) {
                me.showDelay = delay;
            }
        }
    },

    
    onTargetOut : function(e){
        var me = this,
            active = me.activeTarget,
            hasHideDelay,
            delay;

        
        
        if (active && e.within(me.activeTarget.el) && !me.getTipCfg(e)) {
            return;
        }

        me.clearTimer('show');
        delete me.activeTarget;
        if (me.autoHide !== false) {
            hasHideDelay = active && parseInt(active.hideDelay, 10);
            if (hasHideDelay) {
                delay = me.hideDelay;
                me.hideDelay = hasHideDelay;
            }
            me.delayHide();
            if (hasHideDelay) {
                me.hideDelay = delay;
            }
        }
    },
    
    targetTextEmpty: function(){
        var me = this,
            target = me.activeTarget,
            cfg = me.tagConfig,
            el, text;
            
         if (target) {
             el = target.el;
             if (el) {
                 text = el.getAttribute(cfg.namespace + cfg.attribute);
                 
                 
                 
                 if (!text && !me.targets[target.target]) {
                     return true;
                 }
             }
         }
         return false;
    },
    
    show: function(){
        var me = this,
            fromDelay = me.fromDelayShow;
            
        
        
        if (fromDelay && me.targetTextEmpty()) {
            me.onShowVeto();
            delete me.activeTarget;
            return;
        }
        me.callParent(arguments);    
    },

    
    showAt : function(xy){
        var me = this,
            target = me.activeTarget,
            header = me.header,
            cls;

        if (target) {
            if (!me.rendered) {
                me.render(Ext.getBody());
                me.activeTarget = target;
            }
            me.suspendLayouts();
            if (target.title) {
                me.setTitle(target.title);
                header.show();
            } else if (header) {
                header.hide();
            }
            me.update(target.text);
            me.autoHide = target.autoHide;
            me.dismissDelay = target.dismissDelay || me.dismissDelay;
            if (target.mouseOffset) {
                xy[0] += target.mouseOffset[0];
                xy[1] += target.mouseOffset[1];
            }

            cls = me.lastCls;
            if (cls) {
                me.removeCls(cls);
                delete me.lastCls;
            }

            cls = target.cls;
            if (cls) {
                me.addCls(cls);
                me.lastCls = cls;
            }

            me.setWidth(target.width);

            if (me.anchor) {
                me.constrainPosition = false;
            } else if (target.align) { 
                xy = me.getAlignToXY(target.el, target.align);
                me.constrainPosition = false;
            }else{
                me.constrainPosition = true;
            }
            me.resumeLayouts(true);
        }
        me.callParent([xy]);
    },

    
    hide: function(){
        delete this.activeTarget;
        this.callParent();
    }
});


Ext.define('Ext.tip.QuickTipManager', {
                                   
    singleton: true,
    alternateClassName: 'Ext.QuickTips',
    disabled: false,

    
    init : function (autoRender, config) {
        var me = this;

        if (!me.tip) {
            if (!Ext.isReady) {
                Ext.onReady(function(){
                    Ext.tip.QuickTipManager.init(autoRender, config);
                });
                return;
            }

            var tipConfig = Ext.apply({ disabled: me.disabled, id: 'ext-quicktips-tip' }, config),
                className = tipConfig.className,
                xtype = tipConfig.xtype;

            if (className) {
                delete tipConfig.className;
            } else if (xtype) {
                className = 'widget.' + xtype;
                delete tipConfig.xtype;
            }

            if (autoRender !== false) {
                tipConfig.renderTo = document.body;

            }

            me.tip = Ext.create(className || 'Ext.tip.QuickTip', tipConfig);

            
            
            Ext.quickTipsActive = true;
        }
    },

    
    destroy: function() {
        Ext.destroy(this.tip);
        this.tip = undefined;
    },

    
    ddDisable : function() {
        var me = this,
            tip = me.tip;

        
        if (tip && !me.disabled) {
            tip.disable();
        }
    },

    
    ddEnable : function() {
        var me = this,
            tip = me.tip;

        
        if (tip && !me.disabled) {
            tip.enable();
        }
    },

    
    enable : function(){
        var me = this,
            tip = me.tip;

        if (tip) {
            tip.enable();
        }
        me.disabled = false;
    },

    
    disable : function(){
        var me = this,
            tip = me.tip;

        if(tip){
            tip.disable();
        }
        me.disabled = true;
    },

    
    isEnabled : function(){
        var tip = this.tip;

        return tip !== undefined && !tip.disabled;
    },

    
    getQuickTip : function(){
        return this.tip;
    },

    
    register : function(){
        var tip = this.tip;

        tip.register.apply(tip, arguments);
    },

    
    unregister : function(){
        var tip = this.tip;

        tip.unregister.apply(tip, arguments);
    },

    
    tips : function(){
        var tip = this.tip;

        tip.register.apply(tip, arguments);
    }
});


Ext.define('Ext.app.Application', {
    extend:  Ext.app.Controller ,

               
                                 
      

    

    

    
    scope: undefined,

    
    enableQuickTips: true,

    
    appFolder: 'app',
    

    
    appProperty: 'app',

    
    namespaces: [],

    
    autoCreateViewport: false,

    
    paths: null,
    
    onClassExtended: function(cls, data, hooks) {
        var Controller = Ext.app.Controller,
            proto = cls.prototype,
            requires = [],
            onBeforeClassCreated, paths, namespace, ns, appFolder;
        
        
        
        namespace = data.name      || cls.superclass.name;
        appFolder = data.appFolder || cls.superclass.appFolder;
        
        if (namespace) {
            data.$namespace = namespace;
            Ext.app.addNamespaces(namespace);
        }

        if (data.namespaces) {
            Ext.app.addNamespaces(data.namespaces);
        }

        if (!data['paths processed']) {
            if (namespace && appFolder) {
                Ext.Loader.setPath(namespace, appFolder);
            }
            
            paths = data.paths;

            if (paths) {
                for (ns in paths) {
                    if (paths.hasOwnProperty(ns)) {
                        Ext.Loader.setPath(ns, paths[ns]);
                    }
                }
            }
        }
        else {
            delete data['paths processed'];
        }

        if (data.autoCreateViewport) {
            
            Controller.processDependencies(proto, requires, namespace, 'view', ['Viewport']);
        }

        
        if (requires.length) {
            onBeforeClassCreated = hooks.onBeforeCreated;

            hooks.onBeforeCreated = function(cls, data) {
                var args = Ext.Array.clone(arguments);
                
                Ext.require(requires, function () {
                    return onBeforeClassCreated.apply(this, args);
                });
            };
        }
    },

    
    constructor: function(config) {
        var me = this;


        me.callParent(arguments);

        me.doInit(me);

        me.initNamespace();
        me.initControllers();
        me.onBeforeLaunch();
        
        me.finishInitControllers();
    },

    initNamespace: function() {
        var me = this,
            appProperty = me.appProperty,
            ns;

        ns = Ext.namespace(me.name);

        if (ns) {
            ns.getApplication = function() {
                return me;
            };

            if (appProperty) {
                if (!ns[appProperty]) {
                    ns[appProperty] = me;
                }
            }
        }
    },

    initControllers: function() {
        var me = this,
            controllers = Ext.Array.from(me.controllers);

        me.controllers = new Ext.util.MixedCollection();

        for (var i = 0, ln = controllers.length; i < ln; i++) {
            me.getController(controllers[i]);
        }
    },
    
    finishInitControllers: function() {
        var me = this,
            controllers, i, l;
        
        controllers = me.controllers.getRange();
        
        for (i = 0, l = controllers.length; i < l; i++) {
            controllers[i].finishInit(me);
        }
    },

    
    launch: Ext.emptyFn,

    
    onBeforeLaunch: function() {
        var me = this,
            controllers, c, cLen, controller;

        if (me.enableQuickTips) {
            me.initQuickTips();
        }

        if (me.autoCreateViewport) {
            me.initViewport();
        }

        me.launch.call(me.scope || me);
        me.launched = true;
        me.fireEvent('launch', me);

        controllers = me.controllers.items;
        cLen        = controllers.length;

        for (c = 0; c < cLen; c++) {
            controller = controllers[c];
            controller.onLaunch(me);
        }
    },

    getModuleClassName: function(name, kind) {
        return Ext.app.Controller.getFullName(name, kind, this.name).absoluteName;
    },

    initQuickTips: function() {
        Ext.tip.QuickTipManager.init();
    },

    initViewport: function() {
        var viewport = this.getView('Viewport');

        if (viewport) {
            viewport.create();
        }
    },

    getController: function(name) {
        var me          = this,
            controllers = me.controllers,
            className, controller;

        controller = controllers.get(name);

        if (!controller) {
            className  = me.getModuleClassName(name, 'controller');

            controller = Ext.create(className, {
                application: me,
                id:          name
            });

            controllers.add(controller);

            if (me._initialized) {
                controller.doInit(me);
            }
        }

        return controller;
    },

    getApplication: function() {
        return this;
    }
});


Ext.define('Ext.app.domain.Controller', {
    extend:  Ext.app.EventDomain ,
    singleton: true,

               
                            
      

    type: 'controller',
    idProperty: 'id',

    constructor: function() {
        var me = this;
        
        me.callParent();
        me.monitor(Ext.app.Controller);
    }
});


Ext.define('Ext.direct.Provider', {
   alias: 'direct.provider',

    mixins: {
        observable:  Ext.util.Observable 
    },
    
    isProvider: true,

   
    
    
    
    constructor: function(config) {
        var me = this;
        
        Ext.apply(me, config);
        
        Ext.applyIf(me, {
            id: Ext.id(null, 'provider-')
        });

        me.addEvents(
            
            'connect',
            
            
            'disconnect',
            
            
            'data',
            
            
            'exception'
        );

        me.mixins.observable.constructor.call(me, config);
    },

    
    isConnected: function() {
        return false;
    },

    
    connect: Ext.emptyFn,

    
    disconnect: Ext.emptyFn
});



Ext.define('Ext.app.domain.Direct', {
    extend:  Ext.app.EventDomain ,
    singleton: true,
    
               
                             
      
    
    type: 'direct',
    idProperty: 'id',
    
    constructor: function() {
        var me = this;
        
        me.callParent();
        me.monitor(Ext.direct.Provider);
    }
});


Ext.define('Ext.button.Split', {

    
    alias: 'widget.splitbutton',

    extend:  Ext.button.Button ,
    alternateClassName: 'Ext.SplitButton',
    
    
    
    

    
    arrowCls      : 'split',
    split         : true,

    
    initComponent : function(){
        this.callParent();
        
        this.addEvents("arrowclick");
    },

    
    setArrowHandler : function(handler, scope){
        this.arrowHandler = handler;
        this.scope = scope;
    },

    
    onClick : function(e, t) {
        var me = this;
        
        e.preventDefault();
        if (!me.disabled) {
            if (me.overMenuTrigger) {
                me.maybeShowMenu();
                me.fireEvent("arrowclick", me, e);
                if (me.arrowHandler) {
                    me.arrowHandler.call(me.scope || me, me, e);
                }
            } else {
                me.doToggle();
                me.fireHandler(e);
            }
        }
    }
});


Ext.define('Ext.button.Cycle', {

    

    alias: 'widget.cycle',

    extend:  Ext.button.Split ,
    alternateClassName: 'Ext.CycleButton',

    

    
    
    
    
    

    
    

    
    getButtonText: function(item) {
        var me = this,
            text = '';

        if (item && me.showText === true) {
            if (me.prependText) {
                text += me.prependText;
            }
            text += item.text;
            return text;
        }
        return me.text;
    },

    
    setActiveItem: function(item, suppressEvent) {
        var me = this;

        if (!Ext.isObject(item)) {
            item = me.menu.getComponent(item);
        }
        if (item) {
            if (!me.rendered) {
                me.text = me.getButtonText(item);
                me.iconCls = item.iconCls;
                me.glyph = item.glyph;
            } else {
                me.setText(me.getButtonText(item));
                me.setIconCls(item.iconCls);
                me.setGlyph(item.glyph);
            }
            me.activeItem = item;
            if (!item.checked) {
                item.setChecked(true, false);
            }
            if (me.forceIcon) {
                me.setIconCls(me.forceIcon);
            }
            if (me.forceGlyph) {
                me.setGlyph(me.forceGlyph);
            }
            if (!suppressEvent) {
                me.fireEvent('change', me, item);
            }
        }
    },

    
    getActiveItem: function() {
        return this.activeItem;
    },

    
    initComponent: function() {
        var me      = this,
            checked = 0,
            items,
            i, iLen, item;

        me.addEvents(
            
            "change"
        );

        if (me.changeHandler) {
            me.on('change', me.changeHandler, me.scope || me);
            delete me.changeHandler;
        }

        
        
        items = (me.menu.items || []).concat(me.items || []);
        me.menu = Ext.applyIf({
            cls: Ext.baseCSSPrefix + 'cycle-menu',
            items: []
        }, me.menu);

        iLen = items.length;

        
        for (i = 0; i < iLen; i++) {
            item = items[i];

            item = Ext.applyIf({
                group        : me.id,
                itemIndex    : i,
                checkHandler : me.checkHandler,
                scope        : me,
                checked      : item.checked || false
            }, item);

            me.menu.items.push(item);

            if (item.checked) {
                checked = i;
            }
        }

        me.itemCount = me.menu.items.length;
        me.callParent(arguments);
        me.on('click', me.toggleSelected, me);
        me.setActiveItem(checked, me);

        
        if (me.width && me.showText) {
            me.addCls(Ext.baseCSSPrefix + 'cycle-fixed-width');
        }
    },

    
    checkHandler: function(item, pressed) {
        if (pressed) {
            this.setActiveItem(item);
        }
    },

    
    toggleSelected: function() {
        var me = this,
            m = me.menu,
            checkItem;

        checkItem = me.activeItem.next(':not([disabled])') || m.items.getAt(0);
        checkItem.setChecked(true);
    }
});


Ext.define('Ext.chart.Callout', {

    

    

    constructor: function(config) {
        if (config.callouts) {
            config.callouts.styles = Ext.applyIf(config.callouts.styles || {}, {
                color: "#000",
                font: "11px Helvetica, sans-serif"
            });
            this.callouts = Ext.apply(this.callouts || {}, config.callouts);
            this.calloutsArray = [];
        }
    },

    renderCallouts: function() {
        if (!this.callouts) {
            return;
        }

        var me = this,
            items = me.items,
            animate = me.chart.animate,
            config = me.callouts,
            styles = config.styles,
            group = me.calloutsArray,
            store = me.chart.getChartStore(),
            len = store.getCount(),
            ratio = items.length / len,
            previouslyPlacedCallouts = [],
            i,
            count,
            j,
            p,
            item,
            label,
            storeItem,
            display;
            
        for (i = 0, count = 0; i < len; i++) {
            for (j = 0; j < ratio; j++) {
                item = items[count];
                label = group[count];
                storeItem = store.getAt(i);
                
                display = (!config.filter || config.filter(storeItem));
                
                if (!display && !label) {
                    count++;
                    continue;               
                }
                
                if (!label) {
                    group[count] = label = me.onCreateCallout(storeItem, item, i, display, j, count);
                }
                for (p in label) {
                    if (label[p] && label[p].setAttributes) {
                        label[p].setAttributes(styles, true);
                    }
                }
                if (!display) {
                    for (p in label) {
                        if (label[p]) {
                            if (label[p].setAttributes) {
                                label[p].setAttributes({
                                    hidden: true
                                }, true);
                            } else if(label[p].setVisible) {
                                label[p].setVisible(false);
                            }
                        }
                    }
                }
                if (config && config.renderer) {
                    config.renderer(label, storeItem);
                }
                me.onPlaceCallout(label, storeItem, item, i, display, animate,
                                  j, count, previouslyPlacedCallouts);
                previouslyPlacedCallouts.push(label);
                count++;
            }
        }
        this.hideCallouts(count);
    },

    onCreateCallout: function(storeItem, item, i, display) {
        var me = this,
            group = me.calloutsGroup,
            config = me.callouts,
            styles = (config ? config.styles : undefined),
            width = (styles ? styles.width : 0),
            height = (styles ? styles.height : 0),
            chart = me.chart,
            surface = chart.surface,
            calloutObj = {
                
                
                lines: false
            };

        calloutObj.lines = surface.add(Ext.apply({},
        {
            type: 'path',
            path: 'M0,0',
            stroke: me.getLegendColor() || '#555'
        },
        styles));

        if (config.items) {
            calloutObj.panel = new Ext.Panel({
                style: "position: absolute;",    
                width: width,
                height: height,
                items: config.items,
                renderTo: chart.el
            });
        }

        return calloutObj;
    },

    hideCallouts: function(index) {
        var calloutsArray = this.calloutsArray,
            len = calloutsArray.length,
            co,
            p;
        while (len-->index) {
            co = calloutsArray[len];
            for (p in co) {
                if (co[p]) {
                    co[p].hide(true);
                }
            }
        }
    }
});


Ext.define('Ext.draw.CompositeSprite', {

    

    extend:  Ext.util.MixedCollection ,
    mixins: {
        animate:  Ext.util.Animate 
    },
    autoDestroy: false,
    
    
    isCompositeSprite: true,
    constructor: function(config) {
        var me = this;
        
        config = config || {};
        Ext.apply(me, config);

        me.addEvents(
            
            'mousedown',
            
            'mouseup',
            
            'mouseover',
            
            'mouseout',
            
            'click'
        );
        me.id = Ext.id(null, 'ext-sprite-group-');
        me.callParent();
    },

    
    onClick: function(e) {
        this.fireEvent('click', e);
    },

    
    onMouseUp: function(e) {
        this.fireEvent('mouseup', e);
    },

    
    onMouseDown: function(e) {
        this.fireEvent('mousedown', e);
    },

    
    onMouseOver: function(e) {
        this.fireEvent('mouseover', e);
    },

    
    onMouseOut: function(e) {
        this.fireEvent('mouseout', e);
    },

    attachEvents: function(o) {
        var me = this;
        
        o.on({
            scope: me,
            mousedown: me.onMouseDown,
            mouseup: me.onMouseUp,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            click: me.onClick
        });
    },

    
    add: function(key, o) {
        var result = this.callParent(arguments);
        this.attachEvents(result);
        return result;
    },

    insert: function(index, key, o) {
        return this.callParent(arguments);
    },

    
    remove: function(o) {
        var me = this;
        
        o.un({
            scope: me,
            mousedown: me.onMouseDown,
            mouseup: me.onMouseUp,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            click: me.onClick
        });
        return me.callParent(arguments);
    },
    
    
    getBBox: function() {
        var i = 0,
            sprite,
            bb,
            items = this.items,
            len = this.length,
            infinity = Infinity,
            minX = infinity,
            maxHeight = -infinity,
            minY = infinity,
            maxWidth = -infinity,
            maxWidthBBox, maxHeightBBox;
        
        for (; i < len; i++) {
            sprite = items[i];
            if (sprite.el && ! sprite.bboxExcluded) {
                bb = sprite.getBBox();
                minX = Math.min(minX, bb.x);
                minY = Math.min(minY, bb.y);
                maxHeight = Math.max(maxHeight, bb.height + bb.y);
                maxWidth = Math.max(maxWidth, bb.width + bb.x);
            }
        }
        
        return {
            x: minX,
            y: minY,
            height: maxHeight - minY,
            width: maxWidth - minX
        };
    },

    
    setAttributes: function(attrs, redraw) {
        var i = 0,
            items = this.items,
            len = this.length;
            
        for (; i < len; i++) {
            items[i].setAttributes(attrs, redraw);
        }
        return this;
    },

    
    hide: function(redraw) {
        var i = 0,
            items = this.items,
            len = this.length;
            
        for (; i < len; i++) {
            items[i].hide(redraw);
        }
        return this;
    },

    
    show: function(redraw) {
        var i = 0,
            items = this.items,
            len = this.length;
            
        for (; i < len; i++) {
            items[i].show(redraw);
        }
        return this;
    },

    
    redraw: function() {
        var me = this,
            i = 0,
            items = me.items,
            surface = me.getSurface(),
            len = me.length;
        
        if (surface) {
            for (; i < len; i++) {
                surface.renderItem(items[i]);
            }
        }
        return me;
    },

    
    setStyle: function(obj) {
        var i = 0,
            items = this.items,
            len = this.length,
            item, el;
            
        for (; i < len; i++) {
            item = items[i];
            el = item.el;
            if (el) {
                el.setStyle(obj);
            }
        }
    },

    
    addCls: function(obj) {
        var i = 0,
            items = this.items,
            surface = this.getSurface(),
            len = this.length;
        
        if (surface) {
            for (; i < len; i++) {
                surface.addCls(items[i], obj);
            }
        }
    },

    
    removeCls: function(obj) {
        var i = 0,
            items = this.items,
            surface = this.getSurface(),
            len = this.length;
        
        if (surface) {
            for (; i < len; i++) {
                surface.removeCls(items[i], obj);
            }
        }
    },
    
    
    getSurface: function(){
        var first = this.first();
        if (first) {
            return first.surface;
        }
        return null;
    },
    
    
    destroy: function(){
        var me = this,
            surface = me.getSurface(),
            destroySprites = me.autoDestroy,
            item;
            
        if (surface) {
            while (me.getCount() > 0) {
                item = me.first();
                me.remove(item);
                surface.remove(item, destroySprites);
            }
        }
        me.clearListeners();
    }
});


Ext.define('Ext.draw.Surface', {

    

    mixins: {
        observable:  Ext.util.Observable 
    },

                                           
                                                                                                                         

    separatorRe: /[, ]+/,
    
    enginePriority: ['Svg', 'Vml'],

    statics: {
        
        create: function(config, enginePriority) {
            enginePriority = enginePriority || this.prototype.enginePriority;

            var i = 0,
                len = enginePriority.length;

            for (; i < len; i++) {
                if (Ext.supports[enginePriority[i]]) {
                    return Ext.create('Ext.draw.engine.' + enginePriority[i], config);
                }
            }
            return false;
        },
        
        
        save: function(surface, config) {
            config = config || {};
            var exportTypes = {
                    'image/png': 'Image',
                    'image/jpeg': 'Image',
                    'image/svg+xml': 'Svg'
                },
                prefix = exportTypes[config.type] || 'Svg',
                exporter = Ext.draw.engine[prefix + 'Exporter'];           

            return exporter.generate(surface, config);
            
        }
    },

    

    
    availableAttrs: {
        blur: 0,
        "clip-rect": "0 0 1e9 1e9",
        cursor: "default",
        cx: 0,
        cy: 0,
        'dominant-baseline': 'auto',
        fill: "none",
        "fill-opacity": 1,
        font: '10px "Arial"',
        "font-family": '"Arial"',
        "font-size": "10",
        "font-style": "normal",
        "font-weight": 400,
        gradient: "",
        height: 0,
        hidden: false,
        href: "http://sencha.com/",
        opacity: 1,
        path: "M0,0",
        radius: 0,
        rx: 0,
        ry: 0,
        scale: "1 1",
        src: "",
        stroke: "none",
        "stroke-dasharray": "",
        "stroke-linecap": "butt",
        "stroke-linejoin": "butt",
        "stroke-miterlimit": 0,
        "stroke-opacity": 1,
        "stroke-width": 1,
        target: "_blank",
        text: "",
        "text-anchor": "middle",
        title: "Ext Draw",
        width: 0,
        x: 0,
        y: 0,
        zIndex: 0
    },

    
    

    container: undefined,
    height: 352,
    width: 512,
    x: 0,
    y: 0,

    

    
    orderSpritesByZIndex: true,


    
    constructor: function(config) {
        var me = this;
        config = config || {};
        Ext.apply(me, config);

        me.domRef = Ext.getDoc().dom;

        me.customAttributes = {};

        me.addEvents(
            
            'mousedown',
            
            'mouseup',
            
            'mouseover',
            
            'mouseout',
            
            'mousemove',
            
            'mouseenter',
            
            'mouseleave',
            
            'click',
            
            'dblclick'
        );

        me.mixins.observable.constructor.call(me);

        me.getId();
        me.initGradients();
        me.initItems();
        if (me.renderTo) {
            me.render(me.renderTo);
            delete me.renderTo;
        }
        me.initBackground(config.background);
    },

    
    
    initSurface: Ext.emptyFn,

    
    
    renderItem: Ext.emptyFn,

    
    renderItems: Ext.emptyFn,

    
    setViewBox: function (x, y, width, height) {
        if (isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height)) {
            this.viewBox = {x: x, y: y, width: width, height: height};
            this.applyViewBox();
        }
    },

    
    addCls: Ext.emptyFn,

    
    removeCls: Ext.emptyFn,

    
    setStyle: Ext.emptyFn,

    
    initGradients: function() {
        if (this.hasOwnProperty('gradients')) {
            var gradients = this.gradients,
                fn = this.addGradient,
                g, gLen;

            if (gradients) {
                for (g = 0, gLen = gradients.length; g < gLen; g++) {
                    if (fn.call(this, gradients[g], g, gLen) === false) {
                        break;
                    }
                }
            }
        }
    },

    
    initItems: function() {
        var items = this.items;
        this.items = new Ext.draw.CompositeSprite();
        this.items.autoDestroy = true;
        this.groups = new Ext.draw.CompositeSprite();
        if (items) {
            this.add(items);
        }
    },

    
    initBackground: function(config) {
        var me = this,
            width = me.width,
            height = me.height,
            gradientId, gradient;
        if (Ext.isString(config)) {
            config = {
                fill : config
            };
        }
        if (config) {
            if (config.gradient) {
                gradient = config.gradient;
                gradientId = gradient.id;
                me.addGradient(gradient);
                me.background = me.add({
                    type: 'rect',
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    fill: 'url(#' + gradientId + ')',
                    zIndex: -1
                });
            } else if (config.fill) {
                me.background = me.add({
                    type: 'rect',
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    fill: config.fill,
                    zIndex: -1
                });
            } else if (config.image) {
                me.background = me.add({
                    type: 'image',
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    src: config.image,
                    zIndex: -1
                });
            }
            
            me.background.bboxExcluded = true;
        }
    },

    
    setSize: function(w, h) {
        this.applyViewBox();
    },

    
    scrubAttrs: function(sprite) {
        var i,
            attrs = {},
            exclude = {},
            sattr = sprite.attr;
        for (i in sattr) {
            
            if (this.translateAttrs.hasOwnProperty(i)) {
                
                attrs[this.translateAttrs[i]] = sattr[i];
                exclude[this.translateAttrs[i]] = true;
            }
            else if (this.availableAttrs.hasOwnProperty(i) && !exclude[i]) {
                
                attrs[i] = sattr[i];
            }
        }
        return attrs;
    },

    
    onClick: function(e) {
        this.processEvent('click', e);
    },
    
    
    onDblClick: function(e) {
        this.processEvent('dblclick', e);
    },

    
    onMouseUp: function(e) {
        this.processEvent('mouseup', e);
    },

    
    onMouseDown: function(e) {
        this.processEvent('mousedown', e);
    },

    
    onMouseOver: function(e) {
        this.processEvent('mouseover', e);
    },

    
    onMouseOut: function(e) {
        this.processEvent('mouseout', e);
    },

    
    onMouseMove: function(e) {
        this.fireEvent('mousemove', e);
    },

    
    onMouseEnter: Ext.emptyFn,

    
    onMouseLeave: Ext.emptyFn,

    
    addGradient: Ext.emptyFn,

    
    add: function() {
        var args = Array.prototype.slice.call(arguments),
            sprite,
            hasMultipleArgs = args.length > 1,
            items,
            results,
            i,
            ln,
            item;
            
        if (hasMultipleArgs || Ext.isArray(args[0])) {
            items = hasMultipleArgs ? args : args[0];
            results = [];

            for (i = 0, ln = items.length; i < ln; i++) {
                item = items[i];
                item = this.add(item);
                results.push(item);
            }

            return results;
        }
        sprite = this.prepareItems(args[0], true)[0];
        this.insertByZIndex(sprite);
        this.onAdd(sprite);
        return sprite;
    },

    
    insertByZIndex: function(sprite) {
        var me = this,
            sprites = me.items.items,
            len = sprites.length,
            ceil = Math.ceil,
            zIndex = sprite.attr.zIndex,
            idx = len,
            high = idx - 1,
            low = 0,
            otherZIndex;

        if (me.orderSpritesByZIndex && len && zIndex < sprites[high].attr.zIndex) {
            
            while (low <= high) {
                idx = ceil((low + high) / 2);
                otherZIndex = sprites[idx].attr.zIndex;
                if (otherZIndex > zIndex) {
                    high = idx - 1;
                }
                else if (otherZIndex < zIndex) {
                    low = idx + 1;
                }
                else {
                    break;
                }
            }
            
            while (idx < len && sprites[idx].attr.zIndex <= zIndex) {
                idx++;
            }
        }

        me.items.insert(idx, sprite);
        return idx;
    },

    onAdd: function(sprite) {
        var group = sprite.group,
            draggable = sprite.draggable,
            groups, ln, i;
        if (group) {
            groups = [].concat(group);
            ln = groups.length;
            for (i = 0; i < ln; i++) {
                group = groups[i];
                this.getGroup(group).add(sprite);
            }
            delete sprite.group;
        }
        if (draggable) {
            sprite.initDraggable();
        }
    },

    
    remove: function(sprite, destroySprite) {
        if (sprite) {
            this.items.remove(sprite);

            var groups = [].concat(this.groups.items),
                gLen   = groups.length,
                g;

            for (g = 0; g < gLen; g++) {
                groups[g].remove(sprite);
            }

            sprite.onRemove();
            if (destroySprite === true) {
                sprite.destroy();
            }
        }
    },

    
    removeAll: function(destroySprites) {
        var items = this.items.items,
            ln = items.length,
            i;
        for (i = ln - 1; i > -1; i--) {
            this.remove(items[i], destroySprites);
        }
    },

    onRemove: Ext.emptyFn,

    onDestroy: Ext.emptyFn,

    
    applyViewBox: function() {
        var me = this,
            viewBox = me.viewBox,
            width = me.width || 1, 
            height = me.height || 1,
            viewBoxX, viewBoxY, viewBoxWidth, viewBoxHeight,
            relativeHeight, relativeWidth, size;

        if (viewBox && (width || height)) {
            viewBoxX = viewBox.x;
            viewBoxY = viewBox.y;
            viewBoxWidth = viewBox.width;
            viewBoxHeight = viewBox.height;
            relativeHeight = height / viewBoxHeight;
            relativeWidth = width / viewBoxWidth;
            size = Math.min(relativeWidth, relativeHeight);

            if (viewBoxWidth * size < width) {
                viewBoxX -= (width - viewBoxWidth * size) / 2 / size;
            }
            if (viewBoxHeight * size < height) {
                viewBoxY -= (height - viewBoxHeight * size) / 2 / size;
            }

            me.viewBoxShift = {
                dx: -viewBoxX,
                dy: -viewBoxY,
                scale: size
            };
            
            if (me.background) {
                me.background.setAttributes(Ext.apply({}, {
                    x: viewBoxX,
                    y: viewBoxY,
                    width: width / size,
                    height: height / size
                }, { hidden: false }), true);
            }
        } else {
            if (me.background && width && height) {
                me.background.setAttributes(Ext.apply({x: 0, y: 0, width: width, height: height}, { hidden: false }), true);
            }
        }
    },


    getBBox: function (sprite, isWithoutTransform) {
        var realPath = this["getPath" + sprite.type](sprite);
        if (isWithoutTransform) {
            sprite.bbox.plain = sprite.bbox.plain || Ext.draw.Draw.pathDimensions(realPath);
            return sprite.bbox.plain;
        }
        if (sprite.dirtyTransform) {
            this.applyTransformations(sprite, true);
        }
        sprite.bbox.transform = sprite.bbox.transform || Ext.draw.Draw.pathDimensions(Ext.draw.Draw.mapPath(realPath, sprite.matrix));
        return sprite.bbox.transform;
    },
    
    transformToViewBox: function (x, y) {
        if (this.viewBoxShift) {
            var me = this, shift = me.viewBoxShift;
            return [x / shift.scale - shift.dx, y / shift.scale - shift.dy];
        } else {
            return [x, y];
        }
    },

    
    applyTransformations: function(sprite, onlyMatrix) {
        if (sprite.type == 'text') {
            
            sprite.bbox.transform = 0;
            this.transform(sprite, false);
        }


        sprite.dirtyTransform = false;
        
        var me = this,
            attr = sprite.attr;

        if (attr.translation.x != null || attr.translation.y != null) {
            me.translate(sprite);
        }
        if (attr.scaling.x != null || attr.scaling.y != null) {
            me.scale(sprite);
        }
        if (attr.rotation.degrees != null) {
            me.rotate(sprite);
        }
        
        sprite.bbox.transform = 0;
        this.transform(sprite, onlyMatrix);
        sprite.transformations = [];
    },

    
    rotate: function (sprite) {
        var bbox,
            deg = sprite.attr.rotation.degrees,
            centerX = sprite.attr.rotation.x,
            centerY = sprite.attr.rotation.y;
        if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
            bbox = this.getBBox(sprite, true);
            centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
            centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
        }
        sprite.transformations.push({
            type: "rotate",
            degrees: deg,
            x: centerX,
            y: centerY
        });
    },

    
    translate: function(sprite) {
        var x = sprite.attr.translation.x || 0,
            y = sprite.attr.translation.y || 0;
        sprite.transformations.push({
            type: "translate",
            x: x,
            y: y
        });
    },

    
    scale: function(sprite) {
        var bbox,
            x = sprite.attr.scaling.x || 1,
            y = sprite.attr.scaling.y || 1,
            centerX = sprite.attr.scaling.centerX,
            centerY = sprite.attr.scaling.centerY;

        if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
            bbox = this.getBBox(sprite, true);
            centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
            centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
        }
        sprite.transformations.push({
            type: "scale",
            x: x,
            y: y,
            centerX: centerX,
            centerY: centerY
        });
    },

    
    rectPath: function (x, y, w, h, r) {
        if (r) {
            return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
        }
        return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
    },

    
    ellipsePath: function (x, y, rx, ry) {
        if (ry == null) {
            ry = rx;
        }
        return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
    },

    
    getPathpath: function (el) {
        return el.attr.path;
    },

    
    getPathcircle: function (el) {
        var a = el.attr;
        return this.ellipsePath(a.x, a.y, a.radius, a.radius);
    },

    
    getPathellipse: function (el) {
        var a = el.attr;
        return this.ellipsePath(a.x, a.y,
                                a.radiusX || (a.width / 2) || 0,
                                a.radiusY || (a.height / 2) || 0);
    },

    
    getPathrect: function (el) {
        var a = el.attr;
        return this.rectPath(a.x || 0, a.y || 0, a.width || 0, a.height || 0, a.r || 0);
    },

    
    getPathimage: function (el) {
        var a = el.attr;
        return this.rectPath(a.x || 0, a.y || 0, a.width, a.height);
    },

    
    getPathtext: function (el) {
        var bbox = this.getBBoxText(el);
        return this.rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
    },

    createGroup: function(id) {
        var group = this.groups.get(id);
        if (!group) {
            group = new Ext.draw.CompositeSprite({
                surface: this
            });
            group.id = id || Ext.id(null, 'ext-surface-group-');
            this.groups.add(group);
        }
        return group;
    },

    
    getGroup: function(id) {
        var group;
        if (typeof id == "string") {
            group = this.groups.get(id);
            if (!group) {
                group = this.createGroup(id);
            }
        } else {
            group = id;
        }
        return group;
    },

    
    prepareItems: function(items, applyDefaults) {
        items = [].concat(items);
        
        var item, i, ln;
        for (i = 0, ln = items.length; i < ln; i++) {
            item = items[i];
            if (!(item instanceof Ext.draw.Sprite)) {
                
                item.surface = this;
                items[i] = this.createItem(item);
            } else {
                item.surface = this;
            }
        }
        return items;
    },

    
    setText: Ext.emptyFn,

    
    
    createItem: Ext.emptyFn,

    
    getId: function() {
        return this.id || (this.id = Ext.id(null, 'ext-surface-'));
    },

    
    destroy: function() {
        var me = this;
        delete me.domRef;
        if (me.background) {
            me.background.destroy();
        }
        me.removeAll(true);
        Ext.destroy(me.groups.items);
    }
});



Ext.define('Ext.layout.component.Draw', {

    

    alias: 'layout.draw',

    extend:  Ext.layout.component.Auto ,

    setHeightInDom: true,

    setWidthInDom: true,

    

    type: 'draw',
    
    measureContentWidth : function (ownerContext) {
        var target = ownerContext.target,
            paddingInfo = ownerContext.getPaddingInfo(),
            bbox = this.getBBox(ownerContext);
            
        if (!target.viewBox) {
            if (target.autoSize) {
                return bbox.width + paddingInfo.width;
            } else {
                return bbox.x + bbox.width + paddingInfo.width;
            }
        } else {
            if (ownerContext.heightModel.shrinkWrap) {
                return paddingInfo.width;
            } else {
                return bbox.width / bbox.height * (ownerContext.getProp('contentHeight') - paddingInfo.height) + paddingInfo.width;
            }
        }
    },
    
    measureContentHeight : function (ownerContext) {
        var target = ownerContext.target,
            paddingInfo = ownerContext.getPaddingInfo(),
            bbox = this.getBBox(ownerContext);
            
        if (!ownerContext.target.viewBox) {
            if (target.autoSize) {
                return bbox.height + paddingInfo.height;
            } else {
                return bbox.y + bbox.height + paddingInfo.height;
            }
        } else {
            if (ownerContext.widthModel.shrinkWrap) {
                return paddingInfo.height;
            } else {
                return bbox.height / bbox.width * (ownerContext.getProp('contentWidth') - paddingInfo.width) + paddingInfo.height;
            }
        }
    },
    
    getBBox: function(ownerContext) {
        var bbox = ownerContext.surfaceBBox;
        if (!bbox) {
            bbox = ownerContext.target.surface.items.getBBox();
            
            if (bbox.width === -Infinity && bbox.height === -Infinity) {
                bbox.width = bbox.height = bbox.x = bbox.y = 0;
            }
            ownerContext.surfaceBBox = bbox;
        }
        return bbox;
    },

    publishInnerWidth: function (ownerContext, width) {
        ownerContext.setContentWidth(width - ownerContext.getFrameInfo().width, true);
    },
    
    publishInnerHeight: function (ownerContext, height) {
        ownerContext.setContentHeight(height - ownerContext.getFrameInfo().height, true);
    },
    
    finishedLayout: function (ownerContext) {
        
        var props = ownerContext.props,
            paddingInfo = ownerContext.getPaddingInfo();

        
        
        this.owner.setSurfaceSize(props.contentWidth - paddingInfo.width, props.contentHeight - paddingInfo.height);
        
        
        this.callParent(arguments);
    }
});


Ext.define('Ext.draw.Component', {

    

    alias: 'widget.draw',

    extend:  Ext.Component ,

               
                           
                                   
      

    

    
    enginePriority: ['Svg', 'Vml'],

    baseCls: Ext.baseCSSPrefix + 'surface',

    componentLayout: 'draw',

    
    viewBox: true,

    shrinkWrap: 3,
    
    
    autoSize: false,

    

    

    

    initComponent: function() {
        this.callParent(arguments);

        this.addEvents(
            
            'mousedown',
            
            'mouseup',
            
            'mousemove',
            
            'mouseenter',
            
            'mouseleave',
            
            'click',
            
            'dblclick'
        );
    },

    
    onRender: function() {
        var me = this,
            viewBox = me.viewBox,
            autoSize = me.autoSize,
            bbox, items, width, height, x, y;
        me.callParent(arguments);

        if (me.createSurface() !== false) {
            items = me.surface.items;

            if (viewBox || autoSize) {
                bbox = items.getBBox();
                width = bbox.width;
                height = bbox.height;
                x = bbox.x;
                y = bbox.y;
                if (me.viewBox) {
                    me.surface.setViewBox(x, y, width, height);
                } else {
                    me.autoSizeSurface();
                }
            }
        }
    },

    
    autoSizeSurface: function() {
        var bbox = this.surface.items.getBBox();
        this.setSurfaceSize(bbox.width, bbox.height);
    },

    setSurfaceSize: function (width, height) {
        this.surface.setSize(width, height);
        if (this.autoSize) {
            var bbox = this.surface.items.getBBox();
            this.surface.setViewBox(bbox.x, bbox.y - (+Ext.isOpera), width, height);
        }
    },
    
    
    createSurface: function() {
        var me = this,
            cfg = Ext.applyIf({
                renderTo: me.el,
                height: me.height,
                width: me.width,
                items: me.items
            }, me.initialConfig), surface;

        
        delete cfg.listeners;
        if (!cfg.gradients) {
            cfg.gradients = me.gradients;
        }
        surface = Ext.draw.Surface.create(cfg, me.enginePriority);
        if (!surface) {
            
            return false;
        }
        me.surface = surface;


        function refire(eventName) {
            return function(e) {
                me.fireEvent(eventName, e);
            };
        }

        surface.on({
            scope: me,
            mouseup: refire('mouseup'),
            mousedown: refire('mousedown'),
            mousemove: refire('mousemove'),
            mouseenter: refire('mouseenter'),
            mouseleave: refire('mouseleave'),
            click: refire('click'),
            dblclick: refire('dblclick')
        });
    },


    
    onDestroy: function() {
        Ext.destroy(this.surface);
        this.callParent(arguments);
    }

});


Ext.chart = Ext.chart || {};

Ext.define('Ext.chart.theme.Theme', (


function() {
   

(function() {
    Ext.chart.theme = function(config, base) {
        config = config || {};
        var i = 0, d = Ext.Date.now(), l, colors, color,
            seriesThemes, markerThemes,
            seriesTheme, markerTheme, 
            key, gradients = [],
            midColor, midL;
        
        if (config.baseColor) {
            midColor = Ext.draw.Color.fromString(config.baseColor);
            midL = midColor.getHSL()[2];
            if (midL < 0.15) {
                midColor = midColor.getLighter(0.3);
            } else if (midL < 0.3) {
                midColor = midColor.getLighter(0.15);
            } else if (midL > 0.85) {
                midColor = midColor.getDarker(0.3);
            } else if (midL > 0.7) {
                midColor = midColor.getDarker(0.15);
            }
            config.colors = [ midColor.getDarker(0.3).toString(),
                              midColor.getDarker(0.15).toString(),
                              midColor.toString(),
                              midColor.getLighter(0.15).toString(),
                              midColor.getLighter(0.3).toString()];

            delete config.baseColor;
        }
        if (config.colors) {
            colors = config.colors.slice();
            markerThemes = base.markerThemes;
            seriesThemes = base.seriesThemes;
            l = colors.length;
            base.colors = colors;
            for (; i < l; i++) {
                color = colors[i];
                markerTheme = markerThemes[i] || {};
                seriesTheme = seriesThemes[i] || {};
                markerTheme.fill = seriesTheme.fill = markerTheme.stroke = seriesTheme.stroke = color;
                markerThemes[i] = markerTheme;
                seriesThemes[i] = seriesTheme;
            }
            base.markerThemes = markerThemes.slice(0, l);
            base.seriesThemes = seriesThemes.slice(0, l);
        
        }
        for (key in base) {
            if (key in config) {
                if (Ext.isObject(config[key]) && Ext.isObject(base[key])) {
                    Ext.apply(base[key], config[key]);
                } else {
                    base[key] = config[key];
                }
            }
        }
        if (config.useGradients) {
            colors = base.colors || (function () {
                var ans = [];
                for (i = 0, seriesThemes = base.seriesThemes, l = seriesThemes.length; i < l; i++) {
                    ans.push(seriesThemes[i].fill || seriesThemes[i].stroke);
                }
                return ans;
            }());
            for (i = 0, l = colors.length; i < l; i++) {
                midColor = Ext.draw.Color.fromString(colors[i]);
                if (midColor) {
                    color = midColor.getDarker(0.1).toString();
                    midColor = midColor.toString();
                    key = 'theme-' + midColor.substr(1) + '-' + color.substr(1) + '-' + d;
                    gradients.push({
                        id: key,
                        angle: 45,
                        stops: {
                            0: {
                                color: midColor.toString()
                            },
                            100: {
                                color: color.toString()
                            }
                        }
                    });
                    colors[i] = 'url(#' + key + ')'; 
                }
            }
            base.gradients = gradients;
            base.colors = colors;
        }
        
        Ext.apply(this, base);
    };
}());

return {

    

                                 

    

    theme: 'Base',
    themeAttrs: false,

    initTheme: function(theme) {
        var me = this,
            themes = Ext.chart.theme,
            key, gradients;
        if (theme) {
            theme = theme.split(':');
            for (key in themes) {
                if (key == theme[0]) {
                    gradients = theme[1] == 'gradients';
                    me.themeAttrs = new themes[key]({
                        useGradients: gradients
                    });
                    if (gradients) {
                        me.gradients = me.themeAttrs.gradients;
                    }
                    if (me.themeAttrs.background) {
                        me.background = me.themeAttrs.background;
                    }
                    return;
                }
            }
        }
    }
};

})());


Ext.define('Ext.chart.MaskLayer', {
    extend:  Ext.Component ,
    
    constructor: function(config) {
        config = Ext.apply(config || {}, {
            style: 'position:absolute;background-color:#ff9;cursor:crosshair;opacity:0.5;border:1px solid #00f;'
        });
        this.callParent([config]);    
    },
    
    initComponent: function() {
        var me = this;
        me.callParent(arguments);
        me.addEvents(
            'mousedown',
            'mouseup',
            'mousemove',
            'mouseenter',
            'mouseleave'
        );
    },

    initDraggable: function() {
        this.callParent(arguments);
        this.dd.onStart = function (e) {
            var me = this,
                comp = me.comp;
    
            
            this.startPosition = comp.getPosition(true);
    
            
            
            if (comp.ghost && !comp.liveDrag) {
                 me.proxy = comp.ghost();
                 me.dragTarget = me.proxy.header.el;
            }
    
            
            if (me.constrain || me.constrainDelegate) {
                me.constrainTo = me.calculateConstrainRegion();
            }
        };
    }
});


Ext.define('Ext.chart.Mask', {
               
                             
      
    
    

    
    constructor: function(config) {
        var me = this;

        me.addEvents('select');

        if (config) {
            Ext.apply(me, config);
        }
        if (me.enableMask) {
            me.on('afterrender', function() {
                
                var comp = new Ext.chart.MaskLayer({
                    renderTo: me.el,
                    hidden: true
                });
                comp.el.on({
                    'mousemove': function(e) {
                        me.onMouseMove(e);
                    },
                    'mouseup': function(e) {
                        me.onMouseUp(e);
                    }
                });
                comp.initDraggable();
                me.maskType = me.mask;
                me.mask = comp;
                me.maskSprite = me.surface.add({
                    type: 'path',
                    path: ['M', 0, 0],
                    zIndex: 1001,
                    opacity: 0.6,
                    hidden: true,
                    stroke: '#00f',
                    cursor: 'crosshair'
                });
            }, me, { single: true });
        }
    },

    onMouseUp: function(e) {
        var me = this,
            bbox = me.bbox || me.chartBBox,
            sel;
        me.maskMouseDown = false;
        me.mouseDown = false;
        if (me.mouseMoved) {
            me.handleMouseEvent(e);
            me.mouseMoved = false;
            sel = me.maskSelection;
            me.fireEvent('select', me, {
                x: sel.x - bbox.x,
                y: sel.y - bbox.y,
                width: sel.width,
                height: sel.height
            });
        }
    },

    onMouseDown: function(e) {
        this.handleMouseEvent(e);
    },

    onMouseMove: function(e) {
        this.handleMouseEvent(e);
    },

    handleMouseEvent: function(e) {
        var me = this,
            mask = me.maskType,
            bbox = me.bbox || me.chartBBox,
            x = bbox.x,
            y = bbox.y,
            math = Math,
            floor = math.floor,
            abs = math.abs,
            min = math.min,
            max = math.max,
            height = floor(y + bbox.height),
            width = floor(x + bbox.width),
            staticX = e.getPageX() - me.el.getX(),
            staticY = e.getPageY() - me.el.getY(),
            maskMouseDown = me.maskMouseDown,
            path;

        staticX = max(staticX, x);
        staticY = max(staticY, y);
        staticX = min(staticX, width);
        staticY = min(staticY, height);

        if (e.type === 'mousedown') {
            
            me.mouseDown = true;
            me.mouseMoved = false;
            me.maskMouseDown = {
                x: staticX,
                y: staticY
            };
        }
        else {
            
            
            me.mouseMoved = me.mouseDown;
            if (maskMouseDown && me.mouseDown) {
                if (mask == 'horizontal') {
                    staticY = y;
                    maskMouseDown.y = height;
                }
                else if (mask == 'vertical') {
                    staticX = x;
                    maskMouseDown.x = width;
                }
                width = maskMouseDown.x - staticX;
                height = maskMouseDown.y - staticY;
                path = ['M', staticX, staticY, 'l', width, 0, 0, height, -width, 0, 'z'];
                me.maskSelection = {
                    x: (width > 0 ? staticX : staticX + width) + me.el.getX(),
                    y: (height > 0 ? staticY : staticY + height) + me.el.getY(),
                    width: abs(width),
                    height: abs(height)
                };
                me.mask.updateBox(me.maskSelection);
                me.mask.show();
                me.maskSprite.setAttributes({
                    hidden: true    
                }, true);
            }
            else {
                if (mask == 'horizontal') {
                    path = ['M', staticX, y, 'L', staticX, height];
                }
                else if (mask == 'vertical') {
                    path = ['M', x, staticY, 'L', width, staticY];
                }
                else {
                    path = ['M', staticX, y, 'L', staticX, height, 'M', x, staticY, 'L', width, staticY];
                }
                me.maskSprite.setAttributes({
                    path: path,
                    'stroke-width': mask === true ? 1 : 1,
                    hidden: false
                }, true);
            }
        }

    },

    onMouseLeave: function(e) {
        var me = this;
        me.mouseMoved = false;
        me.mouseDown = false;
        me.maskMouseDown = false;
        me.mask.hide();
        me.maskSprite.hide(true);
    }
});
    


Ext.define('Ext.chart.Navigation', {

    
    setZoom: function(zoomConfig) {
        var me = this,
            axesItems = me.axes.items,
            i, ln, axis,
            bbox = me.chartBBox,
            xScale = bbox.width,
            yScale = bbox.height,
            zoomArea = {
                x : zoomConfig.x - me.el.getX(),
                y : zoomConfig.y - me.el.getY(),
                width : zoomConfig.width,
                height : zoomConfig.height
            },
            zoomer, ends, from, to, store, count, step, length, horizontal;

        for (i = 0, ln = axesItems.length; i < ln; i++) {
            axis = axesItems[i];
            horizontal = (axis.position == 'bottom' || axis.position == 'top');
            if (axis.type == 'Category') {
                if (!store) {
                    store = me.getChartStore();
                    count = store.data.items.length;
                }
                zoomer = zoomArea;
                length = axis.length;
                step = Math.round(length / count);
                if (horizontal) {
                    from = (zoomer.x ? Math.floor(zoomer.x / step) + 1 : 0);
                    to = (zoomer.x + zoomer.width) / step;
                } else {
                    from = (zoomer.y ? Math.floor(zoomer.y / step) + 1 : 0);
                    to = (zoomer.y + zoomer.height) / step;
                }
            }
            else {
                zoomer = {
                    x : zoomArea.x / xScale,
                    y : zoomArea.y / yScale,
                    width : zoomArea.width / xScale,
                    height : zoomArea.height / yScale
                }
                ends = axis.calcEnds();
                if (horizontal) {
                    from = (ends.to - ends.from) * zoomer.x + ends.from;
                    to = (ends.to - ends.from) * zoomer.width + from;
                } else {
                    to = (ends.to - ends.from) * (1 - zoomer.y) + ends.from;
                    from = to - (ends.to - ends.from) * zoomer.height;
                }
            }
            axis.minimum = from;
            axis.maximum = to;
            if (horizontal) {
                if (axis.doConstrain && me.maskType != 'vertical') {
                    axis.doConstrain();
                }
            }
            else {
                if (axis.doConstrain && me.maskType != 'horizontal') {
                    axis.doConstrain();
                }
            }
        }
        me.redraw(false);
    },

    
    restoreZoom: function() {
        var me = this,
            axesItems = me.axes.items,
            i, ln, axis;

        me.setSubStore(null);
        for (i = 0, ln = axesItems.length; i < ln; i++) {
            axis = axesItems[i];
            delete axis.minimum;
            delete axis.maximum;
        }
        me.redraw(false);
    }

});


Ext.define('Ext.chart.Shape', {

    

    singleton: true,

    

    circle: function (surface, opts) {
        return surface.add(Ext.apply({
            type: 'circle',
            x: opts.x,
            y: opts.y,
            stroke: null,
            radius: opts.radius
        }, opts));
    },
    line: function (surface, opts) {
        return surface.add(Ext.apply({
            type: 'rect',
            x: opts.x - opts.radius,
            y: opts.y - opts.radius,
            height: 2 * opts.radius,
            width: 2 * opts.radius / 5
        }, opts));
    },
    square: function (surface, opts) {
        return surface.add(Ext.applyIf({
            type: 'rect',
            x: opts.x - opts.radius,
            y: opts.y - opts.radius,
            height: 2 * opts.radius,
            width: 2 * opts.radius,
            radius: null
        }, opts));
    },
    triangle: function (surface, opts) {
        opts.radius *= 1.75;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: "M".concat(opts.x, ",", opts.y, "m0-", opts.radius * 0.58, "l", opts.radius * 0.5, ",", opts.radius * 0.87, "-", opts.radius, ",0z")
        }, opts));
    },
    diamond: function (surface, opts) {
        var r = opts.radius;
        r *= 1.5;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: ["M", opts.x, opts.y - r, "l", r, r, -r, r, -r, -r, r, -r, "z"]
        }, opts));
    },
    cross: function (surface, opts) {
        var r = opts.radius;
        r = r / 1.7;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: "M".concat(opts.x - r, ",", opts.y, "l", [-r, -r, r, -r, r, r, r, -r, r, r, -r, r, r, r, -r, r, -r, -r, -r, r, -r, -r, "z"])
        }, opts));
    },
    plus: function (surface, opts) {
        var r = opts.radius / 1.3;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: "M".concat(opts.x - r / 2, ",", opts.y - r / 2, "l", [0, -r, r, 0, 0, r, r, 0, 0, r, -r, 0, 0, r, -r, 0, 0, -r, -r, 0, 0, -r, "z"])
        }, opts));
    },
    arrow: function (surface, opts) {
        var r = opts.radius;
        return surface.add(Ext.apply({
            type: 'path',
            path: "M".concat(opts.x - r * 0.7, ",", opts.y - r * 0.4, "l", [r * 0.6, 0, 0, -r * 0.4, r, r * 0.8, -r, r * 0.8, 0, -r * 0.4, -r * 0.6, 0], "z")
        }, opts));
    },
    drop: function (surface, x, y, text, size, angle) {
        size = size || 30;
        angle = angle || 0;
        surface.add({
            type: 'path',
            path: ['M', x, y, 'l', size, 0, 'A', size * 0.4, size * 0.4, 0, 1, 0, x + size * 0.7, y - size * 0.7, 'z'],
            fill: '#000',
            stroke: 'none',
            rotate: {
                degrees: 22.5 - angle,
                x: x,
                y: y
            }
        });
        angle = (angle + 90) * Math.PI / 180;
        surface.add({
            type: 'text',
            x: x + size * Math.sin(angle) - 10, 
            y: y + size * Math.cos(angle) + 5,
            text:  text,
            'font-size': size * 12 / 40,
            stroke: 'none',
            fill: '#fff'
        });
    }
});


Ext.define('Ext.chart.LegendItem', {

    

    extend:  Ext.draw.CompositeSprite ,

                                  

    

    
    hiddenSeries: false,
    
    
    label: undefined,
    
    
    x: 0,
    y: 0,
    zIndex: 500,

    
    boldRe: /bold\s\d{1,}.*/i,

    constructor: function(config) {
        this.callParent(arguments);
        this.createLegend(config);
    },

    
    createLegend: function(config) {
        var me = this,
            series = me.series,
            index = config.yFieldIndex;
            
        me.label = me.createLabel(config);
        me.createSeriesMarkers(config);
        
        me.setAttributes({
            hidden: false
        }, true);
        
        me.yFieldIndex = index;

        
        me.on('mouseover', me.onMouseOver, me);
        me.on('mouseout',  me.onMouseOut,  me);
        me.on('mousedown', me.onMouseDown, me);
        
        if (!series.visibleInLegend(index)) {
            me.hiddenSeries = true;
            me.label.setAttributes({
               opacity: 0.5
            }, true);
        };

        
        me.updatePosition({ x: 0, y: 0 }); 
    },
    
    
    getLabelText: function() {
        var me = this,
            series = me.series,
            idx = me.yFieldIndex;

        function getSeriesProp(name) {
            var val = series[name];
            return (Ext.isArray(val) ? val[idx] : val);
        }
        
        return getSeriesProp('title') || getSeriesProp('yField');
    },
    
    
    createLabel: function(config) {
        var me = this,
            legend = me.legend;
        
        return me.add('label', me.surface.add({
            type: 'text',
            x: 20,
            y: 0,
            zIndex: (me.zIndex || 0) + 2,
            fill: legend.labelColor,
            font: legend.labelFont,
            text: me.getLabelText(),
            style: {
                cursor: 'pointer'
            }
        }));
    },
    
    
    createSeriesMarkers: function(config) {
        var me = this,
            index = config.yFieldIndex,
            series = me.series,
            seriesType = series.type,
            surface = me.surface,
            z = me.zIndex;

        
        if (seriesType === 'line' || seriesType === 'scatter') {
            if(seriesType === 'line') {
                var seriesStyle = Ext.apply(series.seriesStyle, series.style);
                me.drawLine(0.5, 0.5, 16.5, 0.5, z, seriesStyle, index);
            };
            
            if (series.showMarkers || seriesType === 'scatter') {
                var markerConfig = Ext.apply(series.markerStyle, series.markerConfig || {}, {
                    fill: series.getLegendColor(index)
                });
                me.drawMarker(8.5, 0.5, z, markerConfig);
            }
        }
        
        else {
            me.drawFilledBox(12, 12, z, index);
        }
    },
    
    
    drawLine: function(fromX, fromY, toX, toY, z, seriesStyle, index) {
        var me = this,
            surface = me.surface,
            series = me.series;
        
        return me.add('line', surface.add({
            type: 'path',
            path: 'M' + fromX + ',' + fromY + 'L' + toX + ',' + toY,
            zIndex: (z || 0) + 2,
            "stroke-width": series.lineWidth,
            "stroke-linejoin": "round",
            "stroke-dasharray": series.dash,
            stroke: seriesStyle.stroke || series.getLegendColor(index) || '#000',
            style: {
                cursor: 'pointer'
            }
        }));
    },
    
    
    drawMarker: function(x, y, z, markerConfig) {
        var me = this,
            surface = me.surface,
            series = me.series;

        return me.add('marker', Ext.chart.Shape[markerConfig.type](surface, {
            fill: markerConfig.fill,
            x: x,
            y: y,
            zIndex: (z || 0) + 2,
            radius: markerConfig.radius || markerConfig.size,
            style: {
                cursor: 'pointer'
            }
        }));
    },
    
    
    drawFilledBox: function(width, height, z, index) {
        var me = this,
            surface = me.surface,
            series = me.series;
            
        return me.add('box', surface.add({
            type: 'rect',
            zIndex: (z || 0) + 2,
            x: 0,
            y: 0,
            width: width,
            height: height,
            fill: series.getLegendColor(index),
            style: {
                cursor: 'pointer'
            }
        }));
    },
    
    
    onMouseOver: function() {
        var me = this;
        
        me.label.setStyle({
            'font-weight': 'bold'
        });
        me.series._index = me.yFieldIndex;
        me.series.highlightItem();
    },
    
    
    onMouseOut: function() {
        var me = this,
            legend = me.legend,
            boldRe = me.boldRe;

        me.label.setStyle({
            'font-weight': legend.labelFont && boldRe.test(legend.labelFont) ? 'bold' : 'normal'
        });
        me.series._index = me.yFieldIndex;
        me.series.unHighlightItem();
    },
    
    
    onMouseDown: function() {
        var me = this,
            index = me.yFieldIndex;

        if (!me.hiddenSeries) {
            me.series.hideAll(index);
            me.label.setAttributes({
                opacity: 0.5
            }, true);
        } else {
            me.series.showAll(index);
            me.label.setAttributes({
                opacity: 1
            }, true);
        }
        me.hiddenSeries = !me.hiddenSeries; 
        me.legend.chart.redraw();
    },

    
    updatePosition: function(relativeTo) {
        var me = this,
            items = me.items,
            ln = items.length,
            i = 0,
            item;
        if (!relativeTo) {
            relativeTo = me.legend;
        }
        for (; i < ln; i++) {
            item = items[i];
            switch (item.type) {
                case 'text':
                    item.setAttributes({
                        x: 20 + relativeTo.x + me.x,
                        y: relativeTo.y + me.y
                    }, true);
                    break;
                case 'rect':
                    item.setAttributes({
                        translate: {
                            x: relativeTo.x + me.x,
                            y: relativeTo.y + me.y - 6
                        }
                    }, true);
                    break;
                default:
                    item.setAttributes({
                        translate: {
                            x: relativeTo.x + me.x,
                            y: relativeTo.y + me.y
                        }
                    }, true);
            }
        }
    }
});


Ext.define('Ext.chart.Legend', {

    

                                       

    

    
    visible: true,
    
    
    update: true,

    
    position: 'bottom',

    
    x: 0,

    
    y: 0,

    
    labelColor: '#000',

    
    labelFont: '12px Helvetica, sans-serif',

    
    boxStroke: '#000',

    
    boxStrokeWidth: 1,

    
    boxFill: '#FFF',

    
    itemSpacing: 10,

    
    padding: 5,

    
    width: 0,
    
    height: 0,

    
    boxZIndex: 100,

    
    constructor: function(config) {
        var me = this;
        if (config) {
            Ext.apply(me, config);
        }
        me.items = [];
        
        me.isVertical = ("left|right|float".indexOf(me.position) !== -1);

        
        me.origX = me.x;
        me.origY = me.y;
    },

    
    create: function() {
        var me = this,
            seriesItems = me.chart.series.items,
            i, ln, series;

        me.createBox();
        
        if (me.rebuild !== false) {
            me.createItems();
        }
        
        if (!me.created && me.isDisplayed()) {
            me.created = true;

            
            for (i = 0, ln = seriesItems.length; i < ln; i++) {
                series = seriesItems[i];
                series.on('titlechange', me.redraw, me);
            }
        }
    },

    
    redraw: function() {
        var me = this;
        
        me.create();
        me.updatePosition();
    },

    
    isDisplayed: function() {
        return this.visible && this.chart.series.findIndex('showInLegend', true) !== -1;
    },

    
    createItems: function() {
        var me = this,
            seriesItems = me.chart.series.items,
            items = me.items,
            fields, i, li, j, lj, series, item;

        
        me.removeItems();
        
        
        for (i = 0, li = seriesItems.length; i < li; i++) {
            series = seriesItems[i];
            
            if (series.showInLegend) {
                fields = [].concat(series.yField);
                
                for (j = 0, lj = fields.length; j < lj; j++) {
                    item = me.createLegendItem(series, j);
                    items.push(item);
                }
            }
        }
        
        me.alignItems();
    },
    
    
    removeItems: function() {
        var me = this,
            items = me.items,
            len = items ? items.length : 0,
            i;

        if (len) {
            for (i = 0; i < len; i++) {
                items[i].destroy();
            }
        }
        
        
        items.length = [];
    },
    
    
    alignItems: function() {
        var me = this,
            padding = me.padding,
            vertical = me.isVertical,
            mfloor = Math.floor,
            dim, maxWidth, maxHeight, totalWidth, totalHeight;
        
        dim = me.updateItemDimensions();

        maxWidth    = dim.maxWidth;
        maxHeight   = dim.maxHeight;
        totalWidth  = dim.totalWidth;
        totalHeight = dim.totalHeight;

        
        me.width = mfloor((vertical ? maxWidth : totalWidth) + padding * 2);
        me.height = mfloor((vertical ? totalHeight : maxHeight) + padding * 2);
    },
    
    updateItemDimensions: function() {
        var me = this,
            items = me.items,
            padding = me.padding,
            itemSpacing = me.itemSpacing,
            maxWidth = 0,
            maxHeight = 0,
            totalWidth = 0,
            totalHeight = 0,
            vertical = me.isVertical,
            mfloor = Math.floor,
            mmax = Math.max,
            spacing = 0,
            i, l, item, bbox, width, height;

        
        
        for (i = 0, l = items.length; i < l; i++) {
            item = items[i];
                
            bbox = item.getBBox();

            
            width  = bbox.width;
            height = bbox.height;

            spacing = (i === 0 ? 0 : itemSpacing);
            
            
            item.x = padding + mfloor(vertical ? 0 : totalWidth + spacing);
            item.y = padding + mfloor(vertical ? totalHeight + spacing : 0) + height / 2;

            
            totalWidth  += spacing + width;
            totalHeight += spacing + height;
            maxWidth     = mmax(maxWidth, width);
            maxHeight    = mmax(maxHeight, height);
        }

        return {
            totalWidth:  totalWidth,
            totalHeight: totalHeight,
            maxWidth:    maxWidth,
            maxHeight:   maxHeight
        };
    },
    
    
    createLegendItem: function(series, yFieldIndex) {
        var me = this;
        
        return new Ext.chart.LegendItem({
            legend: me,
            series: series,
            surface: me.chart.surface,
            yFieldIndex: yFieldIndex
        });
    },
    
    
    getBBox: function() {
        var me = this;
        return {
            x: Math.round(me.x) - me.boxStrokeWidth / 2,
            y: Math.round(me.y) - me.boxStrokeWidth / 2,
            width: me.width + me.boxStrokeWidth,
            height: me.height + me.boxStrokeWidth
        };
    },

    
    createBox: function() {
        var me = this,
            box, bbox;

        if (me.boxSprite) {
            me.boxSprite.destroy();
        }

        bbox = me.getBBox();
        
        
        
        
        
        if (isNaN(bbox.width) || isNaN(bbox.height)) {
            me.boxSprite = false;
            return;
        }
        
        box = me.boxSprite = me.chart.surface.add(Ext.apply({
            type: 'rect',
            stroke: me.boxStroke,
            "stroke-width": me.boxStrokeWidth,
            fill: me.boxFill,
            zIndex: me.boxZIndex
        }, bbox));

        box.redraw();
    },

    
    calcPosition: function() {
        var me = this,
            x, y,
            legendWidth = me.width,
            legendHeight = me.height,
            chart = me.chart,
            chartBBox = chart.chartBBox,
            insets = chart.insetPadding,
            chartWidth = chartBBox.width - (insets * 2),
            chartHeight = chartBBox.height - (insets * 2),
            chartX = chartBBox.x + insets,
            chartY = chartBBox.y + insets,
            surface = chart.surface,
            mfloor = Math.floor;

        
        switch(me.position) {
            case "left":
                x = insets;
                y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
                break;
            case "right":
                x = mfloor(surface.width - legendWidth) - insets;
                y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
                break;
            case "top":
                x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
                y = insets;
                break;
            case "bottom":
                x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
                y = mfloor(surface.height - legendHeight) - insets;
                break;
            default:
                x = mfloor(me.origX) + insets;
                y = mfloor(me.origY) + insets;
        }
        
        return { x: x, y: y };
    },
    
    
    updatePosition: function() {
        var me = this,
            items = me.items,
            pos, i, l, bbox;

        if (me.isDisplayed()) {
            
            pos = me.calcPosition();
            
            me.x = pos.x;
            me.y = pos.y;

            
            for (i = 0, l = items.length; i < l; i++) {
                items[i].updatePosition();
            }

            bbox = me.getBBox();

            
            
            
            
            
            if (isNaN(bbox.width) || isNaN(bbox.height)) {
                if (me.boxSprite) {
                    me.boxSprite.hide(true);
                }
            }
            else {
                if (!me.boxSprite) {
                    me.createBox();
                }
                
                
                me.boxSprite.setAttributes(bbox, true);
                me.boxSprite.show(true);
            }
        }
    },
    
    
    toggle: function(show) {
      var me = this,
          i = 0,
          items = me.items,
          len = items.length;

      if (me.boxSprite) {
          if (show) {
              me.boxSprite.show(true);
          } else {
              me.boxSprite.hide(true);
          }
      }

      for (; i < len; ++i) {
          if (show) {
            items[i].show(true);
          } else {
              items[i].hide(true);
          }
      }

      me.visible = show;
    }
});


Ext.define('Ext.chart.theme.Base', {

    

                                        

    

    constructor: function(config) {
        var ident = Ext.identityFn;
        Ext.chart.theme.call(this, config, {
            background: false,
            axis: {
                stroke: '#444',
                'stroke-width': 1
            },
            axisLabelTop: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: ident
            },
            axisLabelRight: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: ident
            },
            axisLabelBottom: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: ident
            },
            axisLabelLeft: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: ident
            },
            axisTitleTop: {
                font: 'bold 18px Arial',
                fill: '#444'
            },
            axisTitleRight: {
                font: 'bold 18px Arial',
                fill: '#444',
                rotate: {
                    x:0, y:0,
                    degrees: 270
                }
            },
            axisTitleBottom: {
                font: 'bold 18px Arial',
                fill: '#444'
            },
            axisTitleLeft: {
                font: 'bold 18px Arial',
                fill: '#444',
                rotate: {
                    x:0, y:0,
                    degrees: 270
                }
            },
            series: {
                'stroke-width': 0
            },
            seriesLabel: {
                font: '12px Arial',
                fill: '#333'
            },
            marker: {
                stroke: '#555',
                radius: 3,
                size: 3
            },
            colors: [ "#94ae0a", "#115fa6","#a61120", "#ff8809", "#ffd13e", "#a61187", "#24ad9a", "#7c7474", "#a66111"],
            seriesThemes: [{
                fill: "#115fa6"
            }, {
                fill: "#94ae0a"
            }, {
                fill: "#a61120"
            }, {
                fill: "#ff8809"
            }, {
                fill: "#ffd13e"
            }, {
                fill: "#a61187"
            }, {
                fill: "#24ad9a"
            }, {
                fill: "#7c7474"
            }, {
                fill: "#115fa6"
            }, {
                fill: "#94ae0a"
            }, {
                fill: "#a61120"
            }, {
                fill: "#ff8809"
            }, {
                fill: "#ffd13e"
            }, {
                fill: "#a61187"
            }, {
                fill: "#24ad9a"
            }, {
                fill: "#7c7474"
            }, {
                fill: "#a66111"
            }],
            markerThemes: [{
                fill: "#115fa6",
                type: 'circle' 
            }, {
                fill: "#94ae0a",
                type: 'cross'
            }, {
                fill: "#115fa6",
                type: 'plus' 
            }, {
                fill: "#94ae0a",
                type: 'circle'
            }, {
                fill: "#a61120",
                type: 'cross'
            }]
        });
    }
}, function() {
    var palette = ['#b1da5a', '#4ce0e7', '#e84b67', '#da5abd', '#4d7fe6', '#fec935'],
        names = ['Green', 'Sky', 'Red', 'Purple', 'Blue', 'Yellow'],
        i = 0, j = 0, l = palette.length, themes = Ext.chart.theme,
        categories = [['#f0a50a', '#c20024', '#2044ba', '#810065', '#7eae29'],
                      ['#6d9824', '#87146e', '#2a9196', '#d39006', '#1e40ac'],
                      ['#fbbc29', '#ce2e4e', '#7e0062', '#158b90', '#57880e'],
                      ['#ef5773', '#fcbd2a', '#4f770d', '#1d3eaa', '#9b001f'],
                      ['#7eae29', '#fdbe2a', '#910019', '#27b4bc', '#d74dbc'],
                      ['#44dce1', '#0b2592', '#996e05', '#7fb325', '#b821a1']],
        cats = categories.length;
    
    
    for (; i < l; i++) {
        themes[names[i]] = (function(color) {
            return Ext.extend(themes.Base, {
                constructor: function(config) {
                    themes.Base.prototype.constructor.call(this, Ext.apply({
                        baseColor: color
                    }, config));
                }
            });
        }(palette[i]));
    }
    
    
    for (i = 0; i < cats; i++) {
        themes['Category' + (i + 1)] = (function(category) {
            return Ext.extend(themes.Base, {
                constructor: function(config) {
                    themes.Base.prototype.constructor.call(this, Ext.apply({
                        colors: category
                    }, config));
                }
            });
        }(categories[i]));
    }
});


Ext.define('Ext.chart.Chart', {
    extend:  Ext.draw.Component ,

    alias: 'widget.chart',
    
    mixins: {
        themeManager:  Ext.chart.theme.Theme ,
        mask:  Ext.chart.Mask ,
        navigation:  Ext.chart.Navigation ,
        bindable:  Ext.util.Bindable ,
        observable:  Ext.util.Observable 
    },

           
                                 
      
    
               
                                   
                                
                           
                               
                                
                              
      

    

    
    viewBox: false,

    

    
    animate: false,

    
    legend: false,

    
    insetPadding: 10,

    
    background: false,

    

    

    

    

    constructor: function(config) {
        var me = this,
            defaultAnim;

        config = Ext.apply({}, config);
        me.initTheme(config.theme || me.theme);
        if (me.gradients) {
            Ext.apply(config, { gradients: me.gradients });
        }
        if (me.background) {
            Ext.apply(config, { background: me.background });
        }
        if (config.animate) {
            defaultAnim = {
                easing: 'ease',
                duration: 500
            };
            if (Ext.isObject(config.animate)) {
                config.animate = Ext.applyIf(config.animate, defaultAnim);
            }
            else {
                config.animate = defaultAnim;
            }
        }

        me.mixins.observable.constructor.call(me, config);
        if (config.enableMask) {
            me.mixins.mask.constructor.call(me);
        }
        me.mixins.navigation.constructor.call(me);
        me.callParent([config]);
    },
    
    getChartStore: function(){
        return this.substore || this.store;
    },

    initComponent: function() {
        var me = this,
            axes,
            series;
        me.callParent();
        me.addEvents(
            'itemmousedown',
            'itemmouseup',
            'itemmouseover',
            'itemmouseout',
            'itemclick',
            'itemdblclick',
            'itemdragstart',
            'itemdrag',
            'itemdragend',
            
            'beforerefresh',
            
            'refresh'
        );
        Ext.applyIf(me, {
            zoom: {
                width: 1,
                height: 1,
                x: 0,
                y: 0
            }
        });
        me.maxGutters = { left: 0, right: 0, bottom: 0, top: 0 };
        me.store = Ext.data.StoreManager.lookup(me.store);
        axes = me.axes;
        me.axes = new Ext.util.MixedCollection(false, function(a) { return a.position; });
        if (axes) {
            me.axes.addAll(axes);
        }
        series = me.series;
        me.series = new Ext.util.MixedCollection(false, function(a) { return a.seriesId || (a.seriesId = Ext.id(null, 'ext-chart-series-')); });
        if (series) {
            me.series.addAll(series);
        }
        if (me.legend !== false) {
            me.legend = new Ext.chart.Legend(Ext.applyIf({chart:me}, me.legend));
        }

        me.on({
            mousemove: me.onMouseMove,
            mouseleave: me.onMouseLeave,
            mousedown: me.onMouseDown,
            mouseup: me.onMouseUp,
            click: me.onClick,
            dblclick: me.onDblClick,
            scope: me
        });
    },

    
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var me = this;
        if (Ext.isNumber(width) && Ext.isNumber(height)) {
            if (width !== oldWidth || height !== oldHeight) {
                me.curWidth = width;
                me.curHeight = height;
                me.redraw(true);
                me.needsRedraw = false;
            } else if (me.needsRedraw) {
                me.redraw();
                me.needsRedraw = false;
            }
        }
        this.callParent(arguments);
    },

    
    redraw: function(resize) {
        var me = this,
            seriesItems = me.series.items,
            seriesLen = seriesItems.length,
            axesItems = me.axes.items,
            axesLen = axesItems.length,
            themeIndex = 0,
            i, item,
            chartBBox = me.chartBBox = {
                x: 0,
                y: 0,
                height: me.curHeight,
                width: me.curWidth
            },
            legend = me.legend, 
            series;
            
        me.surface.setSize(chartBBox.width, chartBBox.height);
        
        for (i = 0; i < seriesLen; i++) {
            item = seriesItems[i];
            if (!item.initialized) {
                series = me.initializeSeries(item, i, themeIndex);
            } else {
                series = item;
            }
            
            
            series.onRedraw();
            
            
            if (Ext.isArray(item.yField)) {
                themeIndex += item.yField.length;
            } else {
                ++themeIndex;
            }
        }
        for (i = 0; i < axesLen; i++) {
            item = axesItems[i];
            if (!item.initialized) {
                me.initializeAxis(item);
            }
        }
        
        
        for (i = 0; i < axesLen; i++) {
            axesItems[i].processView();
        }
        for (i = 0; i < axesLen; i++) {
            axesItems[i].drawAxis(true);
        }

        
        if (legend !== false && legend.visible) {
            if (legend.update || !legend.created) {
                legend.create();
            }
        }

        
        me.alignAxes();

        
        if (legend !== false && legend.visible) {
            legend.updatePosition();
        }

        
        me.getMaxGutters();

        
        me.resizing = !!resize;

        for (i = 0; i < axesLen; i++) {
            axesItems[i].drawAxis();
        }
        for (i = 0; i < seriesLen; i++) {
            me.drawCharts(seriesItems[i]);
        }
        me.resizing = false;
    },

    
    afterRender: function() {
        var me = this;
        
        me.callParent(arguments);

        if (me.categoryNames) {
            me.setCategoryNames(me.categoryNames);
        }

        me.bindStore(me.store, true);
        me.refresh();

        if (me.surface.engine === 'Vml') {
            me.on('added', me.onAddedVml, me);
            me.mon(me.hierarchyEventSource, 'added', me.onContainerAddedVml, me);
        }
    },

    
    
    
    
    
    onAddedVml: function() {
        this.needsRedraw = true; 
    },

    onContainerAddedVml: function(container) {
        if (this.isDescendantOf(container)) {
            this.needsRedraw = true; 
        }
    },

    
    getEventXY: function(e) {
        var me = this,
            box = this.surface.getRegion(),
            pageXY = e.getXY(),
            x = pageXY[0] - box.left,
            y = pageXY[1] - box.top;
        return [x, y];
    },
    
    onClick: function(e) {
        this.handleClick('itemclick', e);
    },
    
    onDblClick: function(e) {
        this.handleClick('itemdblclick', e);
    },

    
    handleClick: function(name, e) {
        var me = this,
            position = me.getEventXY(e),
            seriesItems = me.series.items,
            i, ln, series,
            item;

        
        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    if (item) {
                        series.fireEvent(name, item);
                    }
                }
            }
        }
    },

    
    onMouseDown: function(e) {
        var me = this,
            position = me.getEventXY(e),
            seriesItems = me.series.items,
            i, ln, series,
            item;

        if (me.enableMask) {
            me.mixins.mask.onMouseDown.call(me, e);
        }
        
        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    if (item) {
                        series.fireEvent('itemmousedown', item);
                    }
                }
            }
        }
    },

    
    onMouseUp: function(e) {
        var me = this,
            position = me.getEventXY(e),
            seriesItems = me.series.items,
            i, ln, series,
            item;

        if (me.enableMask) {
            me.mixins.mask.onMouseUp.call(me, e);
        }
        
        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    if (item) {
                        series.fireEvent('itemmouseup', item);
                    }
                }
            }
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            position = me.getEventXY(e),
            seriesItems = me.series.items,
            i, ln, series,
            item, last, storeItem, storeField;

        
        if (me.enableMask) {
            me.mixins.mask.onMouseMove.call(me, e);
        }
        
        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    last = series._lastItemForPoint;
                    storeItem = series._lastStoreItem;
                    storeField = series._lastStoreField;


                    if (item !== last || item && (item.storeItem != storeItem || item.storeField != storeField)) {
                        if (last) {
                            series.fireEvent('itemmouseout', last);
                            delete series._lastItemForPoint;
                            delete series._lastStoreField;
                            delete series._lastStoreItem;
                        }
                        if (item) {
                            series.fireEvent('itemmouseover', item);
                            series._lastItemForPoint = item;
                            series._lastStoreItem = item.storeItem;
                            series._lastStoreField = item.storeField;
                        }
                    }
                }
            } else {
                last = series._lastItemForPoint;
                if (last) {
                    series.fireEvent('itemmouseout', last);
                    delete series._lastItemForPoint;
                    delete series._lastStoreField;
                    delete series._lastStoreItem;
                }
            }
        }
    },

    
    onMouseLeave: function(e) {
        var me = this,
            seriesItems = me.series.items,
            i, ln, series;

        if (me.enableMask) {
            me.mixins.mask.onMouseLeave.call(me, e);
        }
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            delete series._lastItemForPoint;
        }
    },

    
    delayRefresh: function() {
        var me = this;
        if (!me.refreshTask) {
            me.refreshTask = new Ext.util.DelayedTask(me.refresh, me);
        }
        me.refreshTask.delay(me.refreshBuffer);
    },

    
    refresh: function() {
        var me = this;
            
        if (me.rendered && me.curWidth !== undefined && me.curHeight !== undefined) {
            if (!me.isVisible(true)) {
                if (!me.refreshPending) {
                    me.setShowListeners('mon');
                    me.refreshPending = true;
                }
                return;
            }
            if (me.fireEvent('beforerefresh', me) !== false) {
                me.redraw();
                me.fireEvent('refresh', me);
            }
        }
    },
    
    onShow: function(){
        var me = this;
        me.callParent(arguments);
        if (me.refreshPending) {
            me.delayRefresh();
            me.setShowListeners('mun');
        }
        delete me.refreshPending;
    },
    
    setShowListeners: function(method){
        var me = this;
        me[method](me.hierarchyEventSource, {
            scope: me,
            single: true,
            show: me.forceRefresh,
            expand: me.forceRefresh
        });
    },
    
    doRefresh: function(){
        
        this.setSubStore(null);
        this.refresh();    
    },
    
    forceRefresh: function(container) {
        var me = this;
        if (me.isDescendantOf(container) && me.refreshPending) {
            
            
            me.setShowListeners('mun');
            me.delayRefresh();
        }    
        delete me.refreshPending;
    },

    bindStore: function(store, initial) {
        var me = this;
        me.mixins.bindable.bindStore.apply(me, arguments);
        if (me.store && !initial) {
            me.refresh();
        }
    },
    
    getStoreListeners: function() {
        var refresh = this.doRefresh,
            delayRefresh = this.delayRefresh;
            
        return {
            refresh: refresh,
            add: delayRefresh,
            bulkremove: delayRefresh,
            update: delayRefresh,
            clear: refresh
        };
    },
    
    setSubStore: function(subStore){
        this.substore = subStore;    
    },

    
    initializeAxis: function(axis) {
        var me = this,
            chartBBox = me.chartBBox,
            w = chartBBox.width,
            h = chartBBox.height,
            x = chartBBox.x,
            y = chartBBox.y,
            themeAttrs = me.themeAttrs,
            config = {
                chart: me
            };
        if (themeAttrs) {
            config.axisStyle = Ext.apply({}, themeAttrs.axis);
            config.axisLabelLeftStyle = Ext.apply({}, themeAttrs.axisLabelLeft);
            config.axisLabelRightStyle = Ext.apply({}, themeAttrs.axisLabelRight);
            config.axisLabelTopStyle = Ext.apply({}, themeAttrs.axisLabelTop);
            config.axisLabelBottomStyle = Ext.apply({}, themeAttrs.axisLabelBottom);
            config.axisTitleLeftStyle = Ext.apply({}, themeAttrs.axisTitleLeft);
            config.axisTitleRightStyle = Ext.apply({}, themeAttrs.axisTitleRight);
            config.axisTitleTopStyle = Ext.apply({}, themeAttrs.axisTitleTop);
            config.axisTitleBottomStyle = Ext.apply({}, themeAttrs.axisTitleBottom);
        }
        switch (axis.position) {
            case 'top':
                Ext.apply(config, {
                    length: w,
                    width: h,
                    x: x,
                    y: y
                });
            break;
            case 'bottom':
                Ext.apply(config, {
                    length: w,
                    width: h,
                    x: x,
                    y: h
                });
            break;
            case 'left':
                Ext.apply(config, {
                    length: h,
                    width: w,
                    x: x,
                    y: h
                });
            break;
            case 'right':
                Ext.apply(config, {
                    length: h,
                    width: w,
                    x: w,
                    y: h
                });
            break;
        }
        if (!axis.chart) {
            Ext.apply(config, axis);
            axis = me.axes.replace(Ext.createByAlias('axis.' + axis.type.toLowerCase(), config));
        } else {
            Ext.apply(axis, config);
        }
        axis.initialized = true;
    },


    
    getInsets: function() {
        var me = this,
            insetPadding = me.insetPadding;

        return {
            top: insetPadding,
            right: insetPadding,
            bottom: insetPadding,
            left: insetPadding
        };
    },

    
    calculateInsets: function() {
        var me = this,
            legend = me.legend,
            axes = me.axes,
            edges = ['top', 'right', 'bottom', 'left'],
            insets, i, l, edge, isVertical, axis, bbox;

        function getAxis(edge) {
            var i = axes.findIndex('position', edge);
            return (i < 0) ? null : axes.getAt(i);
        }
        
        insets = me.getInsets();

        
        for (i = 0, l = edges.length; i < l; i++) {
            edge = edges[i];
            
            isVertical = (edge === 'left' || edge === 'right');
            axis = getAxis(edge);

            
            if (legend !== false) {
                if (legend.position === edge) {
                    bbox = legend.getBBox();
                    insets[edge] += (isVertical ? bbox.width : bbox.height) + me.insetPadding;
                }
            }

            
            
            if (axis && axis.bbox) {
                bbox = axis.bbox;
                insets[edge] += (isVertical ? bbox.width : bbox.height);
            }
        };
        
        return insets;
    },

    
    alignAxes: function() {
        var me = this,
            axesItems = me.axes.items,
            insets, chartBBox, i, l, axis, pos, isVertical;
        
        insets = me.calculateInsets();

        
        chartBBox = {
            x: insets.left,
            y: insets.top,
            width: me.curWidth - insets.left - insets.right,
            height: me.curHeight - insets.top - insets.bottom
        };
        me.chartBBox = chartBBox;

        
        
        for (i = 0, l = axesItems.length; i < l; i++) {
            axis = axesItems[i];
            pos = axis.position;
            isVertical = pos === 'left' || pos === 'right';

            axis.x = (pos === 'right' ? chartBBox.x + chartBBox.width : chartBBox.x);
            axis.y = (pos === 'top' ? chartBBox.y : chartBBox.y + chartBBox.height);
            axis.width = (isVertical ? chartBBox.width : chartBBox.height);
            axis.length = (isVertical ? chartBBox.height : chartBBox.width);
        };
    },

    
    initializeSeries: function(series, idx, themeIndex) {
        var me = this,
            themeAttrs = me.themeAttrs,
            seriesObj, markerObj, seriesThemes, st,
            markerThemes, colorArrayStyle = [],
            isInstance = (series instanceof Ext.chart.series.Series).
            i = 0, l, config;

        if (!series.initialized) {
            config = {
                chart: me,
                seriesId: series.seriesId
            };
            if (themeAttrs) {
                seriesThemes = themeAttrs.seriesThemes;
                markerThemes = themeAttrs.markerThemes;
                seriesObj = Ext.apply({}, themeAttrs.series);
                markerObj = Ext.apply({}, themeAttrs.marker);
                config.seriesStyle = Ext.apply(seriesObj, seriesThemes[themeIndex % seriesThemes.length]);
                config.seriesLabelStyle = Ext.apply({}, themeAttrs.seriesLabel);
                config.markerStyle = Ext.apply(markerObj, markerThemes[themeIndex % markerThemes.length]);
                if (themeAttrs.colors) {
                    config.colorArrayStyle = themeAttrs.colors;
                } else {
                    colorArrayStyle = [];
                    for (l = seriesThemes.length; i < l; i++) {
                        st = seriesThemes[i];
                        if (st.fill || st.stroke) {
                            colorArrayStyle.push(st.fill || st.stroke);
                        }
                    }
                    if (colorArrayStyle.length) {
                        config.colorArrayStyle = colorArrayStyle;
                    }
                }
                config.seriesIdx = idx;
                config.themeIdx = themeIndex;
            }
            
            if (isInstance) {
                Ext.applyIf(series, config);
            }
            else {
                Ext.applyIf(config, series);
                series = me.series.replace(Ext.createByAlias('series.' + series.type.toLowerCase(), config));
            }
        }

        if (series.initialize) {
            series.initialize();
        }
        series.initialized = true;
        return series;
    },

    
    getMaxGutters: function() {
        var me = this,
            seriesItems = me.series.items,
            i, ln, series, gutters,
            lowerH = 0, upperH = 0, lowerV = 0, upperV = 0;

        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            gutters = seriesItems[i].getGutters();
            if (gutters) {
                if (gutters.verticalAxis) {
                    lowerV = Math.max(lowerV, gutters.lower);
                    upperV = Math.max(upperV, gutters.upper);
                }
                else {
                    lowerH = Math.max(lowerH, gutters.lower);
                    upperH = Math.max(upperH, gutters.upper);
                }
            }
        }
        me.maxGutters = {
            left: lowerH,
            right: upperH,
            bottom: lowerV,
            top: upperV
        };
    },

    
    drawAxis: function(axis) {
        axis.drawAxis();
    },

    
    drawCharts: function(series) {
        series.triggerafterrender = false;
        series.drawSeries();
        if (!this.animate) {
            series.fireEvent('afterrender');
        }
    },
    
    save: function(config){
        return Ext.draw.Surface.save(this.surface, config);
    },
    
    destroy: function() {
        Ext.destroy(this.surface);
        this.bindStore(null);
        this.callParent(arguments);
    }
});


Ext.define('Ext.chart.Highlight', {

    

                              

    

    
    highlight: false,

    
    highlightCfg : {
        fill: '#fdd',
        "stroke-width": 5,
        stroke: '#f55'
    },

    constructor: function(config) {
        
        if (config.highlight && (typeof config.highlight !== 'boolean')) { 
            this.highlightCfg = Ext.merge({}, this.highlightCfg, config.highlight);
        }
    },

    
    highlightItem: function(item) {
        if (!item) {
            return;
        }
        
        var me = this,
            sprite = item.sprite,
            opts = Ext.merge({}, me.highlightCfg, me.highlight),
            surface = me.chart.surface,
            animate = me.chart.animate,
            p, from, to, pi;

        if (!me.highlight || !sprite || sprite._highlighted) {
            return;
        }
        if (sprite._anim) {
            sprite._anim.paused = true;
        }
        sprite._highlighted = true;
        if (!sprite._defaults) {
            sprite._defaults = Ext.apply({}, sprite.attr);
            from = {};
            to = {};
            
            for (p in opts) {
                if (! (p in sprite._defaults)) {
                    sprite._defaults[p] = surface.availableAttrs[p];
                }
                from[p] = sprite._defaults[p];
                to[p] = opts[p];
                if (Ext.isObject(opts[p])) {
                    from[p] = {};
                    to[p] = {};
                    Ext.apply(sprite._defaults[p], sprite.attr[p]);
                    Ext.apply(from[p], sprite._defaults[p]);
                    for (pi in sprite._defaults[p]) {
                        if (! (pi in opts[p])) {
                            to[p][pi] = from[p][pi];
                        } else {
                            to[p][pi] = opts[p][pi];
                        }
                    }
                    for (pi in opts[p]) {
                        if (! (pi in to[p])) {
                            to[p][pi] = opts[p][pi];
                        }
                    }
                }
            }
            sprite._from = from;
            sprite._to = to;
            sprite._endStyle = to;
        }
        if (animate) {
            sprite._anim = new Ext.fx.Anim({
                target: sprite,
                from: sprite._from,
                to: sprite._to,
                duration: 150
            });
        } else {
            sprite.setAttributes(sprite._to, true);
        }
    },

    
    unHighlightItem: function() {
        if (!this.highlight || !this.items) {
            return;
        }

        var me = this,
            items = me.items,
            len = items.length,
            opts = Ext.merge({}, me.highlightCfg, me.highlight),
            animate = me.chart.animate,
            i = 0,
            obj, p, sprite;
        for (; i < len; i++) {
            if (!items[i]) {
                continue;
            }
            sprite = items[i].sprite;
            if (sprite && sprite._highlighted) {
                if (sprite._anim) {
                    sprite._anim.paused = true;
                }
                obj = {};
                for (p in opts) {
                    if (Ext.isObject(sprite._defaults[p])) {
                        obj[p] = Ext.apply({}, sprite._defaults[p]);
                    }
                    else {
                        obj[p] = sprite._defaults[p];
                    }
                }
                if (animate) {
                    
                    sprite._endStyle = obj;
                    sprite._anim = new Ext.fx.Anim({
                        target: sprite,
                        to: obj,
                        duration: 150
                    });
                }
                else {
                    sprite.setAttributes(obj, true);
                }
                delete sprite._highlighted;
                
            }
        }
    },

    cleanHighlights: function() {
        if (!this.highlight) {
            return;
        }

        var group = this.group,
            markerGroup = this.markerGroup,
            i = 0,
            l;
        for (l = group.getCount(); i < l; i++) {
            delete group.getAt(i)._defaults;
        }
        if (markerGroup) {
            for (l = markerGroup.getCount(); i < l; i++) {
                delete markerGroup.getAt(i)._defaults;
            }
        }
    }
});


Ext.define('Ext.chart.Label', {

    

                                 

    

    
    
    
    
    

    
    colorStringRe: /url\s*\(\s*#([^\/)]+)\s*\)/,

    
    constructor: function(config) {
        var me = this;
        me.label = Ext.applyIf(me.label || {},
        {
            display: "none",
            stackedDisplay: "none",
            color: "#000",
            field: "name",
            minMargin: 50,
            font: "11px Helvetica, sans-serif",
            orientation: "horizontal",
            renderer: Ext.identityFn
        });

        if (me.label.display !== 'none') {
            me.labelsGroup = me.chart.surface.getGroup(me.seriesId + '-labels');
        }
    },

    
    renderLabels: function() {
        var me = this,
            chart = me.chart,
            gradients = chart.gradients,
            items = me.items,
            animate = chart.animate,
            config = me.label,
            display = config.display,
            stackedDisplay = config.stackedDisplay,
            format = config.renderer,
            color = config.color,
            field = [].concat(config.field),
            group = me.labelsGroup,
            groupLength = (group || 0) && group.length,
            store = me.chart.getChartStore(),
            len = store.getCount(),
            itemLength = (items || 0) && items.length,
            ratio = itemLength / len,
            gradientsCount = (gradients || 0) && gradients.length,
            Color = Ext.draw.Color,
            hides = [],
            gradient, i, count, groupIndex, index, j, k, colorStopTotal, colorStopIndex, colorStop, item, label,
            storeItem, sprite, spriteColor, spriteBrightness, labelColor, colorString,
            total, totalPositive, totalNegative, topText, bottomText;

        if (display == 'none' || !group) {
            return;
        }
        
        if(itemLength == 0){
            while(groupLength--) {
                hides.push(groupLength);
            }
        } else {
            for (i = 0, count = 0, groupIndex = 0; i < len; i++) {
                index = 0;
                for (j = 0; j < ratio; j++) {
                    item = items[count];
                    label = group.getAt(groupIndex);
                    storeItem = store.getAt(i);
                    
                    while(this.__excludes && this.__excludes[index]) {
                        index++;
                    }

                    if (!item && label) {
                        label.hide(true);
                        groupIndex++;
                    }

                    if (item && field[j]) {
                        if (!label) {
                            label = me.onCreateLabel(storeItem, item, i, display);
                            if (!label) {
                                break;
                            }
                        }

                        
                        label.setAttributes({
                            fill: String(color)
                        }, true);

                        
                        me.onPlaceLabel(label, storeItem, item, i, display, animate, index);
                        groupIndex++;

                        
                        if (config.contrast && item.sprite) {
                            sprite = item.sprite;
                            
                            
                            if (animate && sprite._endStyle) {
                                colorString = sprite._endStyle.fill;
                            } else if (animate && sprite._to) {
                                colorString = sprite._to.fill;
                            } else {
                                colorString = sprite.attr.fill;
                            }
                            colorString = colorString || sprite.attr.fill;

                            spriteColor = Color.fromString(colorString);
                            
                            if (colorString && !spriteColor) {
                                colorString = colorString.match(me.colorStringRe)[1];
                                for (k = 0; k < gradientsCount; k++) {
                                    gradient = gradients[k];
                                    if (gradient.id == colorString) {
                                        
                                        colorStop = 0; colorStopTotal = 0;
                                        for (colorStopIndex in gradient.stops) {
                                            colorStop++;
                                            colorStopTotal += Color.fromString(gradient.stops[colorStopIndex].color).getGrayscale();
                                        }
                                        spriteBrightness = (colorStopTotal / colorStop) / 255;
                                        break;
                                    }
                                }
                            }
                            else {
                                spriteBrightness = spriteColor.getGrayscale() / 255;
                            }
                            if (label.isOutside) {
                                spriteBrightness = 1;
                            }
                            labelColor = Color.fromString(label.attr.fill || label.attr.color).getHSL();
                            labelColor[2] = spriteBrightness > 0.5 ? 0.2 : 0.8;
                            label.setAttributes({
                                fill: String(Color.fromHSL.apply({}, labelColor))
                            }, true);
                        }

                        
                        if (me.stacked && stackedDisplay && (item.totalPositiveValues || item.totalNegativeValues)) {
                            totalPositive = (item.totalPositiveValues || 0);
                            totalNegative = (item.totalNegativeValues || 0);
                            total = totalPositive + totalNegative;

                            if (stackedDisplay == 'total') {
                                topText = format(total);
                            } else if (stackedDisplay == 'balances') {
                                if (totalPositive == 0 && totalNegative == 0) {
                                    topText = format(0);
                                } else {
                                    topText = format(totalPositive);
                                    bottomText = format(totalNegative);
                                }
                            }

                            if (topText) {
                                label = group.getAt(groupIndex);
                                if (!label) {
                                    label = me.onCreateLabel(storeItem, item, i, 'over');
                                }
                                labelColor = Color.fromString(label.attr.color || label.attr.fill).getHSL();
                                label.setAttributes({
                                    text: topText,
                                    style: config.font,
                                    fill: String(Color.fromHSL.apply({}, labelColor))
                                }, true);
                                me.onPlaceLabel(label, storeItem, item, i, 'over', animate, index);
                                groupIndex ++;
                            }

                            if (bottomText) {
                                label = group.getAt(groupIndex);
                                if (!label) {
                                    label = me.onCreateLabel(storeItem, item, i, 'under');
                                }
                                labelColor = Color.fromString(label.attr.color || label.attr.fill).getHSL();
                                label.setAttributes({
                                    text: bottomText,
                                    style: config.font,
                                    fill: String(Color.fromHSL.apply({}, labelColor))
                                }, true);
                                me.onPlaceLabel(label, storeItem, item, i, 'under', animate, index);
                                groupIndex ++;
                            }
                        }
                    }
                    count++;
                    index++;
                }
            }
            groupLength = group.length;
        
            while(groupLength > groupIndex){
                hides.push(groupIndex);
                groupIndex++;
           }
        }
        me.hideLabels(hides);
    },

    hideLabels: function(hides){
        var labelsGroup = this.labelsGroup,
            hlen = !!hides && hides.length;

        if (!labelsGroup) {
            return;
        }

        if (hlen === false) {
            hlen = labelsGroup.getCount();
            while (hlen--) {
              labelsGroup.getAt(hlen).hide(true);
            }
        } else {
            while(hlen--) {
                labelsGroup.getAt(hides[hlen]).hide(true);
            }
        }
    }
});


Ext.define('Ext.chart.TipSurface', {

    

    extend:  Ext.draw.Component ,

    

    spriteArray: false,
    renderFirst: true,

    constructor: function(config) {
        this.callParent([config]);
        if (config.sprites) {
            this.spriteArray = [].concat(config.sprites);
            delete config.sprites;
        }
    },

    onRender: function() {
        var me = this,
            i = 0,
            l = 0,
            sp,
            sprites;
            this.callParent(arguments);
        sprites = me.spriteArray;
        if (me.renderFirst && sprites) {
            me.renderFirst = false;
            for (l = sprites.length; i < l; i++) {
                sp = me.surface.add(sprites[i]);
                sp.setAttributes({
                    hidden: false
                },
                true);
            }
        }
    }
});


Ext.define('Ext.chart.Tip', {

    

                                                          

    

    constructor: function(config) {
        var me = this,
            surface,
            sprites,
            tipSurface;
        if (config.tips) {
            me.tipTimeout = null;
            me.tipConfig = Ext.apply({}, config.tips, {
                renderer: Ext.emptyFn,
                constrainPosition: true,
                autoHide: true
            });
            me.tooltip = new Ext.tip.ToolTip(me.tipConfig);
            me.chart.surface.on('mousemove', me.tooltip.onMouseMove, me.tooltip);
            me.chart.surface.on('mouseleave', function() {
                me.hideTip();
            });
            if (me.tipConfig.surface) {
                
                surface = me.tipConfig.surface;
                sprites = surface.sprites;
                tipSurface = new Ext.chart.TipSurface({
                    id: 'tipSurfaceComponent',
                    sprites: sprites
                });
                if (surface.width && surface.height) {
                    tipSurface.setSize(surface.width, surface.height);
                }
                me.tooltip.add(tipSurface);
                me.spriteTip = tipSurface;
            }
        }
    },

    showTip: function(item) {
        var me = this,
            tooltip,
            spriteTip,
            tipConfig,
            trackMouse,
            sprite,
            surface,
            surfaceExt,
            pos,
            x,
            y;
        if (!me.tooltip) {
            return;
        }
        clearTimeout(me.tipTimeout);
        tooltip = me.tooltip;
        spriteTip = me.spriteTip;
        tipConfig = me.tipConfig;
        trackMouse = tooltip.trackMouse;
        if (!trackMouse) {
            tooltip.trackMouse = true;
            sprite = item.sprite;
            surface = sprite.surface;
            surfaceExt = Ext.get(surface.getId());
            if (surfaceExt) {
                pos = surfaceExt.getXY();
                x = pos[0] + (sprite.attr.x || 0) + (sprite.attr.translation && sprite.attr.translation.x || 0);
                y = pos[1] + (sprite.attr.y || 0) + (sprite.attr.translation && sprite.attr.translation.y || 0);
                tooltip.targetXY = [x, y];
            }
        }
        if (spriteTip) {
            tipConfig.renderer.call(tooltip, item.storeItem, item, spriteTip.surface);
        } else {
            tipConfig.renderer.call(tooltip, item.storeItem, item);
        }
        tooltip.show();
        tooltip.trackMouse = trackMouse;
    },

    hideTip: function(item) {
        var tooltip = this.tooltip;
        if (!tooltip) {
            return;
        }
        clearTimeout(this.tipTimeout);
        this.tipTimeout = setTimeout(function() {
            tooltip.hide();
        }, 0);
    }
});


Ext.define('Ext.chart.axis.Abstract', {

    

                                  

    
    
    

    

    
    constructor: function(config) {
        config = config || {};

        var me = this,
            pos = config.position || 'left';

        pos = pos.charAt(0).toUpperCase() + pos.substring(1);
        
        config.label = Ext.apply(config['axisLabel' + pos + 'Style'] || {}, config.label || {});
        config.axisTitleStyle = Ext.apply(config['axisTitle' + pos + 'Style'] || {}, config.labelTitle || {});
        Ext.apply(me, config);
        me.fields = Ext.Array.from(me.fields);
        this.callParent();
        me.labels = [];
        me.getId();
        me.labelGroup = me.chart.surface.getGroup(me.axisId + "-labels");
    },

    alignment: null,
    grid: false,
    steps: 10,
    x: 0,
    y: 0,
    minValue: 0,
    maxValue: 0,

    getId: function() {
        return this.axisId || (this.axisId = Ext.id(null, 'ext-axis-'));
    },

    
    processView: Ext.emptyFn,

    drawAxis: Ext.emptyFn,
    addDisplayAndLabels: Ext.emptyFn
});


Ext.define('Ext.chart.axis.Axis', {

    

    extend:  Ext.chart.axis.Abstract ,

    alternateClassName: 'Ext.chart.Axis',

                                

    

    

    

    

    

     
     hidden: false,

    
    forceMinMax: false,

    
    dashSize: 3,

    
    position: 'bottom',

    
    skipFirst: false,

    
    length: 0,

    
    width: 0,

    
    adjustEnd: true,

    majorTickSteps: false,
    
    nullGutters: { lower: 0, upper: 0, verticalAxis: undefined },

    
    applyData: Ext.emptyFn,

    getRange: function () {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            series = chart.series.items,
            position = me.position,
            axes,
            seriesClasses = Ext.chart.series,
            aggregations = [],
            min = Infinity, max = -Infinity,
            vertical = me.position === 'left' || me.position === 'right' || me.position === 'radial',
            i, ln, ln2, j, k, dataLength = data.length, aggregates,
            countedFields = {},
            allFields = {},
            excludable = true,
            fields, fieldMap, record, field, value;

        fields = me.fields;
        for (j = 0, ln = fields.length; j < ln; j++) {
            allFields[fields[j]] = true;
        }

        for (i = 0, ln = series.length; i < ln; i++) {
            if (series[i].seriesIsHidden) {
                continue;
            }
            if (!series[i].getAxesForXAndYFields) {
                continue;
            }
            axes = series[i].getAxesForXAndYFields();
            if (axes.xAxis && axes.xAxis !== position && axes.yAxis && axes.yAxis !== position) {
                
                continue;
            }

            if (seriesClasses.Bar && series[i] instanceof seriesClasses.Bar && !series[i].column) {
                
                fields = vertical ? Ext.Array.from(series[i].xField) : Ext.Array.from(series[i].yField);
            } else {
                fields = vertical ? Ext.Array.from(series[i].yField) : Ext.Array.from(series[i].xField);
            }

            if (me.fields.length) {
                for (j = 0, ln2 = fields.length; j < ln2; j++) {
                    if (allFields[fields[j]]) {
                        break;
                    }
                }
                if (j == ln2) {
                    
                    continue;
                }
            }

            if (aggregates = series[i].stacked) {
                
                if (seriesClasses.Bar && series[i] instanceof seriesClasses.Bar) {
                    if (series[i].column != vertical) {
                        aggregates = false;
                        excludable = false;
                    }
                }
                
                else if (!vertical) {
                    aggregates = false;
                    excludable = false;
                }
            }


            if (aggregates) {
                fieldMap = {};
                for (j = 0; j < fields.length; j++) {
                    if (excludable && series[i].__excludes && series[i].__excludes[j]) {
                        continue;
                    }
                    if (!allFields[fields[j]]) {
                        Ext.Logger.warn('Field `' + fields[j] + '` is not included in the ' + position + ' axis config.');
                    }
                    allFields[fields[j]] = fieldMap[fields[j]] = true;
                }
                aggregations.push({
                    fields: fieldMap,
                    positiveValue: 0,
                    negativeValue: 0
                });
            } else {

                if (!fields || fields.length == 0) {
                    fields = me.fields;
                }
                for (j = 0; j < fields.length; j++) {
                    if (excludable && series[i].__excludes && series[i].__excludes[j]) {
                        continue;
                    }
                    allFields[fields[j]] = countedFields[fields[j]] = true;
                }
            }
        }

        for (i = 0; i < dataLength; i++) {
            record = data[i];
            for (k = 0; k < aggregations.length; k++) {
                aggregations[k].positiveValue = 0;
                aggregations[k].negativeValue = 0;
            }
            for (field in allFields) {
                value = record.get(field);
                if (me.type == 'Time' && typeof value == "string") {
                    value = Date.parse(value);
                }
                if (isNaN(value)) {
                    continue;
                }
                if (value === undefined) {
                    value = 0;
                } else {
                    value = Number(value);
                }
                if (countedFields[field]) {
                    if (min > value) {
                        min = value;
                    }
                    if (max < value) {
                        max = value;
                    }
                }
                for (k = 0; k < aggregations.length; k++) {
                    if (aggregations[k].fields[field]) {

                        if (value >= 0) {
                            aggregations[k].positiveValue += value;
                            if (max < aggregations[k].positiveValue) {
                                max = aggregations[k].positiveValue;
                            }
                            
                            if (min > 0) {
                                min = 0;
                            }
                        } else {
                            aggregations[k].negativeValue += value;
                            if (min > aggregations[k].negativeValue) {
                                min = aggregations[k].negativeValue;
                            }
                            
                            if (max < 0) {
                                max = 0;
                            }
                        }
                    }
                }
            }
        }

        if (!isFinite(max)) {
            max = me.prevMax || 0;
        }
        if (!isFinite(min)) {
            min = me.prevMin || 0;
        }

        if (typeof min === 'number') {
            min = Ext.Number.correctFloat(min);
        }
         
        if (typeof max === 'number') {
            max = Ext.Number.correctFloat(max);
        }
        
        
        if (min != max && (max != Math.floor(max) || min != Math.floor(min))) {
            min = Math.floor(min);
            max = Math.floor(max) + 1;
        }

        if (!isNaN(me.minimum)) {
            min = me.minimum;
        }

        if (!isNaN(me.maximum)) {
            max = me.maximum;
        }

        if (min >= max) {
            
            min = Math.floor(min);
            max = min + 1;                
        }

        return {min: min, max: max};
    },

    
    calcEnds: function () {
        var me = this,
            range = me.getRange(),
            min = range.min,
            max = range.max,
            steps, prettyNumbers, out, changedRange;

        steps = (Ext.isNumber(me.majorTickSteps) ? me.majorTickSteps + 1 : me.steps);
        prettyNumbers = !(Ext.isNumber(me.maximum) && Ext.isNumber(me.minimum) && Ext.isNumber(me.majorTickSteps) && me.majorTickSteps > 0);

        out = Ext.draw.Draw.snapEnds(min, max, steps, prettyNumbers);

        if (Ext.isNumber(me.maximum)) {
            out.to = me.maximum;
            changedRange = true;
        }
        if (Ext.isNumber(me.minimum)) {
            out.from = me.minimum;
            changedRange = true;
        }
        if (me.adjustMaximumByMajorUnit) {
            out.to = Math.ceil(out.to / out.step) * out.step;
            changedRange = true;
        }
        if (me.adjustMinimumByMajorUnit) {
            out.from = Math.floor(out.from / out.step) * out.step;
            changedRange = true;
        }

        if (changedRange) {
            out.steps = Math.ceil((out.to - out.from) / out.step);            
        }

        me.prevMin = (min == max ? 0 : min);
        me.prevMax = max;
        return out;
    },


    
    drawAxis: function (init) {
        var me = this,
            i, 
            x = me.x,
            y = me.y,
            dashSize = me.dashSize,
            length = me.length,
            position = me.position,
            verticalAxis = (position == 'left' || position == 'right'),
            inflections = [],
            calcLabels = (me.isNumericAxis),
            stepCalcs = me.applyData(),
            step = stepCalcs.step,
            steps = stepCalcs.steps,
            stepsArray = Ext.isArray(steps),
            from = stepCalcs.from,
            to = stepCalcs.to,
            
            axisRange = (to - from) || 1,
            trueLength,
            currentX,
            currentY,
            path,
            subDashesX = me.minorTickSteps || 0,
            subDashesY = me.minorTickSteps || 0,
            dashesX = Math.max(subDashesX + 1, 0),
            dashesY = Math.max(subDashesY + 1, 0),
            dashDirection = (position == 'left' || position == 'top' ? -1 : 1),
            dashLength = dashSize * dashDirection,
            series = me.chart.series.items,
            firstSeries = series[0],
            gutters = firstSeries ? firstSeries.nullGutters : me.nullGutters,
            padding,
            subDashes,
            subDashValue,
            delta = 0,
            stepCount = 0,
            tick, axes, ln, val, begin, end;

        me.from = from;
        me.to = to;
        
        
        if (me.hidden || (from > to)) {
            return;
        }

        
        if ((stepsArray && (steps.length == 0)) || (!stepsArray && isNaN(step))) {
            return;
        }

        if (stepsArray) {
            
            
            steps = Ext.Array.filter(steps, function(elem, index, array) {
                return (+elem > +me.from && +elem < +me.to);
            }, this);

            
            steps = Ext.Array.union([me.from], steps, [me.to]);
        }
        else {
            
            steps = new Array;
            for (val = +me.from; val < +me.to; val += step) {
                steps.push(val);
            }
            steps.push(+me.to);
        }
        stepCount = steps.length;


        
        for (i = 0, ln = series.length; i < ln; i++) {
            if (series[i].seriesIsHidden) {
                continue;
            }
            if (!series[i].getAxesForXAndYFields) {
                continue;
            }
            axes = series[i].getAxesForXAndYFields();
            if (!axes.xAxis || !axes.yAxis || (axes.xAxis === position) || (axes.yAxis === position)) {
                gutters = series[i].getGutters();
                if ((gutters.verticalAxis !== undefined) && (gutters.verticalAxis != verticalAxis)) {
                    
                    
                    
                    
                    padding = series[i].getPadding();
                    if (verticalAxis) {
                        gutters = { lower: padding.bottom, upper: padding.top, verticalAxis: true };
                    } else {
                        gutters = { lower: padding.left, upper: padding.right, verticalAxis: false };
                    }
                }
                break;
            }
        }

        

        if (calcLabels) {
            me.labels = [];
        }

        if (gutters) {
            if (verticalAxis) {
                currentX = Math.floor(x);
                path = ["M", currentX + 0.5, y, "l", 0, -length];
                trueLength = length - (gutters.lower + gutters.upper);

                for (tick = 0; tick < stepCount; tick++) {
                    currentY = y - gutters.lower - (steps[tick] - steps[0]) * trueLength / axisRange;
                    path.push("M", currentX, Math.floor(currentY) + 0.5, "l", dashLength * 2, 0);

                    inflections.push([ currentX, Math.floor(currentY) ]);

                    if (calcLabels) {
                        me.labels.push(steps[tick]);
                    }
                }
            } else {
                currentY = Math.floor(y);
                path = ["M", x, currentY + 0.5, "l", length, 0];
                trueLength = length - (gutters.lower + gutters.upper);

                for (tick = 0; tick < stepCount; tick++) {
                    currentX = x + gutters.lower + (steps[tick] - steps[0]) * trueLength / axisRange;
                    path.push("M", Math.floor(currentX) + 0.5, currentY, "l", 0, dashLength * 2 + 1);

                    inflections.push([ Math.floor(currentX), currentY ]);

                    if (calcLabels) {
                        me.labels.push(steps[tick]);
                    }
                }
            }
        }


        

        
        
        
        
        subDashes = (verticalAxis ? subDashesY : subDashesX);
        if (Ext.isArray(subDashes)) {
            if (subDashes.length == 2) {
                subDashValue = +Ext.Date.add(new Date(), subDashes[0], subDashes[1]) - Date.now();
            } else {
                subDashValue = subDashes[0];
            }
        }
        else {
            if (Ext.isNumber(subDashes) && subDashes > 0) {
                subDashValue = step / (subDashes + 1);
            }
        }

        if (gutters && subDashValue) {
            for (tick = 0; tick < stepCount - 1; tick++) {
                begin = +steps[tick];
                end = +steps[tick+1];
                if (verticalAxis) {
                    for (value = begin + subDashValue; value < end; value += subDashValue) {
                        currentY = y - gutters.lower - (value - steps[0]) * trueLength / axisRange;
                        path.push("M", currentX, Math.floor(currentY) + 0.5, "l", dashLength, 0);
                    }
                }
                else {
                    for (value = begin + subDashValue; value < end; value += subDashValue) {
                        currentX = x + gutters.upper + (value - steps[0]) * trueLength / axisRange;
                        path.push("M", Math.floor(currentX) + 0.5, currentY, "l", 0, dashLength + 1);
                    }
                }
            }            
        }


        

        if (!me.axis) {
            me.axis = me.chart.surface.add(Ext.apply({
                type: 'path',
                path: path
            }, me.axisStyle));
        }
        me.axis.setAttributes({
            path: path
        }, true);
        me.inflections = inflections;
        if (!init && me.grid) {
            me.drawGrid();
        }
        me.axisBBox = me.axis.getBBox();
        me.drawLabel();
    },

    
    drawGrid: function () {
        var me = this,
            surface = me.chart.surface,
            grid = me.grid,
            odd = grid.odd,
            even = grid.even,
            inflections = me.inflections,
            ln = inflections.length - ((odd || even) ? 0 : 1),
            position = me.position,
            maxGutters = me.chart.maxGutters,
            width = me.width - 2,
            point, prevPoint,
            i = 1,
            path = [], styles, lineWidth, dlineWidth,
            oddPath = [], evenPath = [];

        if (((maxGutters.bottom !== 0 || maxGutters.top !== 0) && (position == 'left' || position == 'right')) ||
            ((maxGutters.left !== 0 || maxGutters.right !== 0) && (position == 'top' || position == 'bottom'))) {
            i = 0;
            ln++;
        }
        for (; i < ln; i++) {
            point = inflections[i];
            prevPoint = inflections[i - 1];
            if (odd || even) {
                path = (i % 2) ? oddPath : evenPath;
                styles = ((i % 2) ? odd : even) || {};
                lineWidth = (styles.lineWidth || styles['stroke-width'] || 0) / 2;
                dlineWidth = 2 * lineWidth;
                if (position == 'left') {
                    path.push("M", prevPoint[0] + 1 + lineWidth, prevPoint[1] + 0.5 - lineWidth,
                        "L", prevPoint[0] + 1 + width - lineWidth, prevPoint[1] + 0.5 - lineWidth,
                        "L", point[0] + 1 + width - lineWidth, point[1] + 0.5 + lineWidth,
                        "L", point[0] + 1 + lineWidth, point[1] + 0.5 + lineWidth, "Z");
                }
                else if (position == 'right') {
                    path.push("M", prevPoint[0] - lineWidth, prevPoint[1] + 0.5 - lineWidth,
                        "L", prevPoint[0] - width + lineWidth, prevPoint[1] + 0.5 - lineWidth,
                        "L", point[0] - width + lineWidth, point[1] + 0.5 + lineWidth,
                        "L", point[0] - lineWidth, point[1] + 0.5 + lineWidth, "Z");
                }
                else if (position == 'top') {
                    path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + lineWidth,
                        "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + width - lineWidth,
                        "L", point[0] + 0.5 - lineWidth, point[1] + 1 + width - lineWidth,
                        "L", point[0] + 0.5 - lineWidth, point[1] + 1 + lineWidth, "Z");
                }
                else {
                    path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - lineWidth,
                        "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - width + lineWidth,
                        "L", point[0] + 0.5 - lineWidth, point[1] - width + lineWidth,
                        "L", point[0] + 0.5 - lineWidth, point[1] - lineWidth, "Z");
                }
            } else {
                if (position == 'left') {
                    path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", width, 0]);
                }
                else if (position == 'right') {
                    path = path.concat(["M", point[0] - 0.5, point[1] + 0.5, "l", -width, 0]);
                }
                else if (position == 'top') {
                    path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", 0, width]);
                }
                else {
                    path = path.concat(["M", point[0] + 0.5, point[1] - 0.5, "l", 0, -width]);
                }
            }
        }
        if (odd || even) {
            if (oddPath.length) {
                if (!me.gridOdd && oddPath.length) {
                    me.gridOdd = surface.add({
                        type: 'path',
                        path: oddPath
                    });
                }
                me.gridOdd.setAttributes(Ext.apply({
                    path: oddPath,
                    hidden: false
                }, odd || {}), true);
            }
            if (evenPath.length) {
                if (!me.gridEven) {
                    me.gridEven = surface.add({
                        type: 'path',
                        path: evenPath
                    });
                }
                me.gridEven.setAttributes(Ext.apply({
                    path: evenPath,
                    hidden: false
                }, even || {}), true);
            }
        }
        else {
            if (path.length) {
                if (!me.gridLines) {
                    me.gridLines = me.chart.surface.add({
                        type: 'path',
                        path: path,
                        "stroke-width": me.lineWidth || 1,
                        stroke: me.gridColor || '#ccc'
                    });
                }
                me.gridLines.setAttributes({
                    hidden: false,
                    path: path
                }, true);
            }
            else if (me.gridLines) {
                me.gridLines.hide(true);
            }
        }
    },

    
    getOrCreateLabel: function (i, text) {
        var me = this,
            labelGroup = me.labelGroup,
            textLabel = labelGroup.getAt(i),
            surface = me.chart.surface;
        if (textLabel) {
            if (text != textLabel.attr.text) {
                textLabel.setAttributes(Ext.apply({
                    text: text
                }, me.label), true);
                textLabel._bbox = textLabel.getBBox();
            }
        }
        else {
            textLabel = surface.add(Ext.apply({
                group: labelGroup,
                type: 'text',
                x: 0,
                y: 0,
                text: text
            }, me.label));
            surface.renderItem(textLabel);
            textLabel._bbox = textLabel.getBBox();
        }
        
        if (me.label.rotation) {
            textLabel.setAttributes({
                rotation: {
                    degrees: 0
                }
            }, true);
            textLabel._ubbox = textLabel.getBBox();
            textLabel.setAttributes(me.label, true);
        } else {
            textLabel._ubbox = textLabel._bbox;
        }
        return textLabel;
    },

    rect2pointArray: function (sprite) {
        var surface = this.chart.surface,
            rect = surface.getBBox(sprite, true),
            p1 = [rect.x, rect.y],
            p1p = p1.slice(),
            p2 = [rect.x + rect.width, rect.y],
            p2p = p2.slice(),
            p3 = [rect.x + rect.width, rect.y + rect.height],
            p3p = p3.slice(),
            p4 = [rect.x, rect.y + rect.height],
            p4p = p4.slice(),
            matrix = sprite.matrix;
        
        p1[0] = matrix.x.apply(matrix, p1p);
        p1[1] = matrix.y.apply(matrix, p1p);

        p2[0] = matrix.x.apply(matrix, p2p);
        p2[1] = matrix.y.apply(matrix, p2p);

        p3[0] = matrix.x.apply(matrix, p3p);
        p3[1] = matrix.y.apply(matrix, p3p);

        p4[0] = matrix.x.apply(matrix, p4p);
        p4[1] = matrix.y.apply(matrix, p4p);
        return [p1, p2, p3, p4];
    },

    intersect: function (l1, l2) {
        var r1 = this.rect2pointArray(l1),
            r2 = this.rect2pointArray(l2);
        return !!Ext.draw.Draw.intersect(r1, r2).length;
    },

    drawHorizontalLabels: function () {
        var me = this,
            labelConf = me.label,
            floor = Math.floor,
            max = Math.max,
            axes = me.chart.axes,
            insetPadding = me.chart.insetPadding,
            gutters = me.chart.maxGutters,
            position = me.position,
            inflections = me.inflections,
            ln = inflections.length,
            labels = me.labels,
            maxHeight = 0,
            ratio,
            bbox, point, prevLabel, prevLabelId,
            adjustEnd = me.adjustEnd,
            hasLeft = axes.findIndex('position', 'left') != -1,
            hasRight = axes.findIndex('position', 'right') != -1,
            textLabel, text,
            last, x, y, i, firstLabel;

        last = ln - 1;
        
        point = inflections[0];
        firstLabel = me.getOrCreateLabel(0, me.label.renderer(labels[0]));
        ratio = Math.floor(Math.abs(Math.sin(labelConf.rotate && (labelConf.rotate.degrees * Math.PI / 180) || 0)));

        for (i = 0; i < ln; i++) {
            point = inflections[i];
            text = me.label.renderer(labels[i]);
            textLabel = me.getOrCreateLabel(i, text);
            bbox = textLabel._bbox;
            maxHeight = max(maxHeight, bbox.height + me.dashSize + me.label.padding);
            x = floor(point[0] - (ratio ? bbox.height : bbox.width) / 2);
            if (adjustEnd && gutters.left == 0 && gutters.right == 0) {
                if (i == 0 && !hasLeft) {
                    x = point[0];
                }
                else if (i == last && !hasRight) {
                    x = Math.min(x, point[0] - bbox.width + insetPadding);
                }
            }
            if (position == 'top') {
                y = point[1] - (me.dashSize * 2) - me.label.padding - (bbox.height / 2);
            }
            else {
                y = point[1] + (me.dashSize * 2) + me.label.padding + (bbox.height / 2);
            }

            textLabel.setAttributes({
                hidden: false,
                x: x,
                y: y
            }, true);

            
            if (i != 0 && (me.intersect(textLabel, prevLabel)
                || me.intersect(textLabel, firstLabel))) {
                if (i === last && prevLabelId !== 0) {
                    prevLabel.hide(true);
                } else {
                    textLabel.hide(true);
                    continue;
                }
            }

            prevLabel = textLabel;
            prevLabelId = i;
        }

        return maxHeight;
    },

    drawVerticalLabels: function () {
        var me = this,
            inflections = me.inflections,
            position = me.position,
            ln = inflections.length,
            chart = me.chart,
            insetPadding = chart.insetPadding,
            labels = me.labels,
            maxWidth = 0,
            max = Math.max,
            floor = Math.floor,
            ceil = Math.ceil,
            axes = me.chart.axes,
            gutters = me.chart.maxGutters,
            bbox, point, prevLabel, prevLabelId,
            hasTop = axes.findIndex('position', 'top') != -1,
            hasBottom = axes.findIndex('position', 'bottom') != -1,
            adjustEnd = me.adjustEnd,
            textLabel, text,
            last = ln - 1, x, y, i;

        for (i = 0; i < ln; i++) {
            point = inflections[i];
            text = me.label.renderer(labels[i]);
            textLabel = me.getOrCreateLabel(i, text);
            bbox = textLabel._bbox;

            maxWidth = max(maxWidth, bbox.width + me.dashSize + me.label.padding);
            y = point[1];
            if (adjustEnd && (gutters.bottom + gutters.top) < bbox.height / 2) {
                if (i == last && !hasTop) {
                    y = Math.max(y, me.y - me.length + ceil(bbox.height / 2) - insetPadding);
                }
                else if (i == 0 && !hasBottom) {
                    y = me.y + gutters.bottom - floor(bbox.height / 2);
                }
            }
            if (position == 'left') {
                x = point[0] - bbox.width - me.dashSize - me.label.padding - 2;
            }
            else {
                x = point[0] + me.dashSize + me.label.padding + 2;
            }
            textLabel.setAttributes(Ext.apply({
                hidden: false,
                x: x,
                y: y
            }, me.label), true);
            
            if (i != 0 && me.intersect(textLabel, prevLabel)) {
                if (i === last && prevLabelId !== 0) {
                    prevLabel.hide(true);
                } else {
                    textLabel.hide(true);
                    continue;
                }
            }
            prevLabel = textLabel;
            prevLabelId = i;
        }

        return maxWidth;
    },

    
    drawLabel: function () {
        var me = this,
            position = me.position,
            labelGroup = me.labelGroup,
            inflections = me.inflections,
            maxWidth = 0,
            maxHeight = 0,
            ln, i;

        if (position == 'left' || position == 'right') {
            maxWidth = me.drawVerticalLabels();
        } else {
            maxHeight = me.drawHorizontalLabels();
        }

        
        ln = labelGroup.getCount();
        i = inflections.length;
        for (; i < ln; i++) {
            labelGroup.getAt(i).hide(true);
        }

        me.bbox = {};
        Ext.apply(me.bbox, me.axisBBox);
        me.bbox.height = maxHeight;
        me.bbox.width = maxWidth;
        if (Ext.isString(me.title)) {
            me.drawTitle(maxWidth, maxHeight);
        }
    },

    
    setTitle: function (title) {
        this.title = title;
        this.drawLabel();
    },

    
    drawTitle: function (maxWidth, maxHeight) {
        var me = this,
            position = me.position,
            surface = me.chart.surface,
            displaySprite = me.displaySprite,
            title = me.title,
            rotate = (position == 'left' || position == 'right'),
            x = me.x,
            y = me.y,
            base, bbox, pad;

        if (displaySprite) {
            displaySprite.setAttributes({text: title}, true);
        } else {
            base = {
                type: 'text',
                x: 0,
                y: 0,
                text: title
            };
            displaySprite = me.displaySprite = surface.add(Ext.apply(base, me.axisTitleStyle, me.labelTitle));
            surface.renderItem(displaySprite);
        }
        bbox = displaySprite.getBBox();
        pad = me.dashSize + me.label.padding;

        if (rotate) {
            y -= ((me.length / 2) - (bbox.height / 2));
            if (position == 'left') {
                x -= (maxWidth + pad + (bbox.width / 2));
            }
            else {
                x += (maxWidth + pad + bbox.width - (bbox.width / 2));
            }
            me.bbox.width += bbox.width + 10;
        }
        else {
            x += (me.length / 2) - (bbox.width * 0.5);
            if (position == 'top') {
                y -= (maxHeight + pad + (bbox.height * 0.3));
            }
            else {
                y += (maxHeight + pad + (bbox.height * 0.8));
            }
            me.bbox.height += bbox.height + 10;
        }
        displaySprite.setAttributes({
            translate: {
                x: x,
                y: y
            }
        }, true);
    }
});


Ext.define('Ext.chart.axis.Category', {

    

    extend:  Ext.chart.axis.Axis ,

    alternateClassName: 'Ext.chart.CategoryAxis',

    alias: 'axis.category',

    

    
    categoryNames: null,

    
    calculateCategoryCount: false,

    
    doConstrain: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            items = store.data.items,
            series = chart.series.items,
            seriesLength = series.length,
            data = [], i

        for (i = 0; i < seriesLength; i++) {
            if (series[i].type === 'bar' && series[i].stacked) {
                
                return;
            }
        }

        for (i = me.minimum; i < me.maximum; i++) {
            data.push(items[i]);
        }
        
        chart.setSubStore(new Ext.data.Store({
            model: store.model,
            data: data
        }));
    },

    
    setLabels: function() {
        var store = this.chart.getChartStore(),
            data = store.data.items,
            d, dLen, record,
            fields = this.fields,
            ln = fields.length,
            labels,
            name,
            i;

        labels = this.labels = [];
        for (d = 0, dLen = data.length; d < dLen; d++) {
            record = data[d];
            for (i = 0; i < ln; i++) {
                name = record.get(fields[i]);
                labels.push(name);
            }
        }
    },

    
    applyData: function() {
        this.callParent();
        this.setLabels();
        var count = this.chart.getChartStore().getCount();
        return {
            from: 0,
            to: count - 1,
            power: 1,
            step: 1,
            steps: count - 1
        };
    }
});


Ext.define('Ext.chart.axis.Gauge', {

    

    extend:  Ext.chart.axis.Abstract ,

    

    

    

    

    

    

    position: 'gauge',

    alias: 'axis.gauge',

    drawAxis: function(init) {
        var chart = this.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + bbox.height,
            margin = this.margin || 10,
            rho = Math.min(bbox.width, 2 * bbox.height) /2 + margin,
            sprites = [], sprite,
            steps = this.steps,
            i, pi = Math.PI,
            cos = Math.cos,
            sin = Math.sin;

        if (this.sprites && !chart.resizing) {
            this.drawLabel();
            return;
        }

        if (this.margin >= 0) {
            if (!this.sprites) {
                
                for (i = 0; i <= steps; i++) {
                    sprite = surface.add({
                        type: 'path',
                        path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi),
                                    centerY + (rho - margin) * sin(i / steps * pi - pi),
                                    'L', centerX + rho * cos(i / steps * pi - pi),
                                    centerY + rho * sin(i / steps * pi - pi), 'Z'],
                        stroke: '#ccc'
                    });
                    sprite.setAttributes({
                        hidden: false
                    }, true);
                    sprites.push(sprite);
                }
            } else {
                sprites = this.sprites;
                
                for (i = 0; i <= steps; i++) {
                    sprites[i].setAttributes({
                        path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi),
                                    centerY + (rho - margin) * sin(i / steps * pi - pi),
                               'L', centerX + rho * cos(i / steps * pi - pi),
                                    centerY + rho * sin(i / steps * pi - pi), 'Z'],
                        stroke: '#ccc'
                    }, true);
                }
            }
        }
        this.sprites = sprites;
        this.drawLabel();
        if (this.title) {
            this.drawTitle();
        }
    },

    drawTitle: function() {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            labelSprite = me.titleSprite,
            labelBBox;

        if (!labelSprite) {
            me.titleSprite = labelSprite = surface.add(Ext.apply({
                type: 'text',
                zIndex: 2
            }, me.axisTitleStyle, me.labelTitle));
        }
        labelSprite.setAttributes(Ext.apply({
            text: me.title
        }, me.label || {}), true);
        labelBBox = labelSprite.getBBox();
        labelSprite.setAttributes({
            x: bbox.x + (bbox.width / 2) - (labelBBox.width / 2),
            y: bbox.y + bbox.height - (labelBBox.height / 2) - 4
        }, true);
    },

    
    setTitle: function(title) {
        this.title = title;
        this.drawTitle();
    },

    drawLabel: function() {
        var chart = this.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + bbox.height,
            margin = this.margin || 10,
            rho = Math.min(bbox.width, 2 * bbox.height) /2 + 2 * margin,
            round = Math.round,
            labelArray = [], label,
            maxValue = this.maximum || 0,
            minValue = this.minimum || 0,
            steps = this.steps, i = 0,
            adjY,
            pi = Math.PI,
            cos = Math.cos,
            sin = Math.sin,
            labelConf = this.label,
            renderer = labelConf.renderer || Ext.identityFn;

        if (!this.labelArray) {
            
            for (i = 0; i <= steps; i++) {
                
                adjY = (i === 0 || i === steps) ? 7 : 0;
                label = surface.add({
                    type: 'text',
                    text: renderer(round(minValue + i / steps * (maxValue - minValue))),
                    x: centerX + rho * cos(i / steps * pi - pi),
                    y: centerY + rho * sin(i / steps * pi - pi) - adjY,
                    'text-anchor': 'middle',
                    'stroke-width': 0.2,
                    zIndex: 10,
                    stroke: '#333'
                });
                label.setAttributes({
                    hidden: false
                }, true);
                labelArray.push(label);
            }
        }
        else {
            labelArray = this.labelArray;
            
            for (i = 0; i <= steps; i++) {
                
                adjY = (i === 0 || i === steps) ? 7 : 0;
                labelArray[i].setAttributes({
                    text: renderer(round(minValue + i / steps * (maxValue - minValue))),
                    x: centerX + rho * cos(i / steps * pi - pi),
                    y: centerY + rho * sin(i / steps * pi - pi) - adjY
                }, true);
            }
        }
        this.labelArray = labelArray;
    }
});


Ext.define('Ext.chart.axis.Numeric', {

    

    extend:  Ext.chart.axis.Axis ,

    alternateClassName: 'Ext.chart.NumericAxis',

    

    type: 'Numeric',

    
    isNumericAxis: true,

    alias: 'axis.numeric',

                             

    constructor: function(config) {
        var me = this,
            hasLabel = !!(config.label && config.label.renderer),
            label;

        me.callParent([config]);
        label = me.label;

        if (config.constrain == null) {
            me.constrain = (config.minimum != null && config.maximum != null);
        }

        if (!hasLabel) {
            label.renderer = function(v) {
                return me.roundToDecimal(v, me.decimals);
            };
        }
    },

    roundToDecimal: function(v, dec) {
        var val = Math.pow(10, dec || 0);
        return Math.round(v * val) / val;
    },

    
    minimum: NaN,

    
    maximum: NaN,

    
    constrain: true,

    
    decimals: 2,

    
    scale: "linear",

    
    doConstrain: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            items = store.data.items,
            d, dLen, record,
            series = chart.series.items,
            fields = me.fields,
            ln = fields.length,
            range = me.calcEnds(),
            min = range.from, max = range.to, i, l,
            useAcum = false,
            value, data = [],
            addRecord;

        for (d = 0, dLen = items.length; d < dLen; d++) {
            addRecord = true;
            record = items[d];
            for (i = 0; i < ln; i++) {
                value = record.get(fields[i]);
                if (me.type == 'Time' && typeof value == "string") {
                    value = Date.parse(value);
                }
                if (+value < +min) {
                    addRecord = false;
                    break;
                }
                if (+value > +max) {
                    addRecord = false;
                    break;
                }
            }
            if (addRecord) {
                data.push(record);
            }
        }
        
        chart.setSubStore(new Ext.data.Store({
            model: store.model,
            data: data
        }));
    },
    
    position: 'left',

    
    adjustMaximumByMajorUnit: false,

    
    adjustMinimumByMajorUnit: false,

    
    processView: function() {
        var me = this,
            chart = me.chart,
            series = chart.series.items,
            i, l;

        for (i = 0, l = series.length; i < l; i++) {
            if (series[i].stacked) {
                
                delete me.minimum;
                delete me.maximum;
                me.constrain = false;
                break;
            }
        }

        if (me.constrain) {
            me.doConstrain();
        }
    },

    
    applyData: function() {
        this.callParent();
        return this.calcEnds();
    }
});


Ext.define('Ext.chart.axis.Radial', {

    

    extend:  Ext.chart.axis.Numeric ,

    

    position: 'radial',

    alias: 'axis.radial',

    

    

    drawAxis: function(init) {
        var chart = this.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            store = chart.getChartStore(),
            l = store.getCount(),
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + (bbox.height / 2),
            rho = Math.min(bbox.width, bbox.height) /2,
            sprites = [], sprite,
            steps = this.steps,
            i, j, pi2 = Math.PI * 2,
            cos = Math.cos, sin = Math.sin;

        if (this.sprites && !chart.resizing) {
            this.drawLabel();
            return;
        }

        if (!this.sprites) {
            
            for (i = 1; i <= steps; i++) {
                sprite = surface.add({
                    type: 'circle',
                    x: centerX,
                    y: centerY,
                    radius: Math.max(rho * i / steps, 0),
                    stroke: '#ccc'
                });
                sprite.setAttributes({
                    hidden: false
                }, true);
                sprites.push(sprite);
            }
            
            for (i = 0; i < l; i++) {
                sprite = surface.add({
                    type: 'path',
                    path: ['M', centerX, centerY, 'L', centerX + rho * cos(i / l * pi2), centerY + rho * sin(i / l * pi2), 'Z'],
                    stroke: '#ccc'
                });
                sprite.setAttributes({
                    hidden: false
                }, true);
                sprites.push(sprite);
            }
        } else {
            sprites = this.sprites;
            
            for (i = 0; i < steps; i++) {
                sprites[i].setAttributes({
                    x: centerX,
                    y: centerY,
                    radius: Math.max(rho * (i + 1) / steps, 0),
                    stroke: '#ccc'
                }, true);
            }
            
            for (j = 0; j < l; j++) {
                sprites[i + j].setAttributes({
                    path: ['M', centerX, centerY, 'L', centerX + rho * cos(j / l * pi2), centerY + rho * sin(j / l * pi2), 'Z'],
                    stroke: '#ccc'
                }, true);
            }
        }
        this.sprites = sprites;

        this.drawLabel();
    },

    drawLabel: function() {
        var chart = this.chart,
            seriesItems = chart.series.items,
            series,
            surface = chart.surface,
            bbox = chart.chartBBox,
            store = chart.getChartStore(),
            data = store.data.items,
            ln, record,
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + (bbox.height / 2),
            rho = Math.min(bbox.width, bbox.height) /2,
            max = Math.max, round = Math.round,
            labelArray = [], label,
            fields = [], nfields,
            categories = [], xField,
            aggregate = !this.maximum,
            maxValue = this.maximum || 0,
            steps = this.steps, i = 0, j, dx, dy,
            pi2 = Math.PI * 2,
            cos = Math.cos, sin = Math.sin,
            display = this.label.display,
            draw = display !== 'none',
            margin = 10;

        if (!draw) {
            return;
        }

        
        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            fields.push(series.yField);
            xField = series.xField;
        }
        
        
        for (j = 0, ln = data.length; j < ln; j++) {
            record = data[j];
            categories.push(record.get(xField));

            if (aggregate) {
                for (i = 0, nfields = fields.length; i < nfields; i++) {
                    maxValue = max(+record.get(fields[i]), maxValue);
                }
            }
        }
        if (!this.labelArray) {
            if (display != 'categories') {
                
                for (i = 1; i <= steps; i++) {
                    label = surface.add({
                        type: 'text',
                        text: round(i / steps * maxValue),
                        x: centerX,
                        y: centerY - rho * i / steps,
                        'text-anchor': 'middle',
                        'stroke-width': 0.1,
                        stroke: '#333'
                    });
                    label.setAttributes({
                        hidden: false
                    }, true);
                    labelArray.push(label);
                }
            }
            if (display != 'scale') {
                
                for (j = 0, steps = categories.length; j < steps; j++) {
                    dx = cos(j / steps * pi2) * (rho + margin);
                    dy = sin(j / steps * pi2) * (rho + margin);
                    label = surface.add({
                        type: 'text',
                        text: categories[j],
                        x: centerX + dx,
                        y: centerY + dy,
                        'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start')
                    });
                    label.setAttributes({
                        hidden: false
                    }, true);
                    labelArray.push(label);
                }
            }
        }
        else {
            labelArray = this.labelArray;
            if (display != 'categories') {
                
                for (i = 0; i < steps; i++) {
                    labelArray[i].setAttributes({
                        text: round((i + 1) / steps * maxValue),
                        x: centerX,
                        y: centerY - rho * (i + 1) / steps,
                        'text-anchor': 'middle',
                        'stroke-width': 0.1,
                        stroke: '#333'
                    }, true);
                }
            }
            if (display != 'scale') {
                
                for (j = 0, steps = categories.length; j < steps; j++) {
                    dx = cos(j / steps * pi2) * (rho + margin);
                    dy = sin(j / steps * pi2) * (rho + margin);
                    if (labelArray[i + j]) {
                        labelArray[i + j].setAttributes({
                            type: 'text',
                            text: categories[j],
                            x: centerX + dx,
                            y: centerY + dy,
                            'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start')
                        }, true);
                    }
                }
            }
        }
        this.labelArray = labelArray;
    },

    getRange: function () {
        var range = this.callParent();
        range.min = 0;  
        return range;
    },

    processView: function() {
        var me = this,
            seriesItems = me.chart.series.items,
            i, ln, series, ends, fields = [];

        for (i = 0, ln = seriesItems.length; i < ln; i++) {
            series = seriesItems[i];
            fields.push(series.yField);
        }
        me.fields = fields;

        ends = me.calcEnds();
        me.maximum = ends.to;
        me.steps = ends.steps;
    }
});


Ext.define('Ext.chart.axis.Time', {

    

    extend:  Ext.chart.axis.Numeric ,

    alternateClassName: 'Ext.chart.TimeAxis',

    type: 'Time',

    alias: 'axis.time',

                             

    

    
    dateFormat: false,

    
    fromDate: false,

    
    toDate: false,

    
    step: [Ext.Date.DAY, 1],

    
    constrain: false,

    constructor: function (config) {
        var me = this, label, f, df;
        me.callParent([config]);
        label = me.label || {};
        df = this.dateFormat;
        if (df) {
            if (label.renderer) {
                f = label.renderer;
                label.renderer = function(v) {
                    v = f(v);
                    return Ext.Date.format(new Date(f(v)), df);
                };
            } else {
                label.renderer = function(v) {
                    return Ext.Date.format(new Date(v >> 0), df);
                };
            }
        }
    },

    
    processView: function () {
        var me = this;
        if (me.fromDate) {
            me.minimum = +me.fromDate;
        }
        if (me.toDate) {
            me.maximum = +me.toDate;
        }
        if(me.constrain){
            me.doConstrain();
        }
     },

    
    calcEnds: function() {
        var me = this, range, step = me.step;
        if (step) {
            range = me.getRange();
            range = Ext.draw.Draw.snapEndsByDateAndStep(new Date(range.min), new Date(range.max), Ext.isNumber(step) ? [Date.MILLI, step]: step);
            if (me.minimum) {
                range.from = me.minimum;
            }
            if (me.maximum) {
                range.to = me.maximum;
            }
            return range;
        } else {
            return me.callParent(arguments);
        }
    }
 });



Ext.define('Ext.chart.series.Series', {

    

    mixins: {
        observable:  Ext.util.Observable ,
        labels:  Ext.chart.Label ,
        highlights:  Ext.chart.Highlight ,
        tips:  Ext.chart.Tip ,
        callouts:  Ext.chart.Callout 
    },

    

    

    

    
    type: null,

    
    title: null,

    
    showInLegend: true,

    
    renderer: function(sprite, record, attributes, index, store) {
        return attributes;
    },

    
    shadowAttributes: null,

    
    animating: false,

    
    nullGutters: { lower: 0, upper: 0, verticalAxis: undefined },

    
    nullPadding: { left:0, right:0, width:0, bottom:0, top:0, height:0 },

    

    constructor: function(config) {
        var me = this;
        if (config) {
            Ext.apply(me, config);
        }

        me.shadowGroups = [];

        me.mixins.labels.constructor.call(me, config);
        me.mixins.highlights.constructor.call(me, config);
        me.mixins.tips.constructor.call(me, config);
        me.mixins.callouts.constructor.call(me, config);

        me.addEvents({
            scope: me,
            itemclick: true,
            itemmouseover: true,
            itemmouseout: true,
            itemmousedown: true,
            itemmouseup: true,
            mouseleave: true,
            afterdraw: true,

            
            titlechange: true
        });

        me.mixins.observable.constructor.call(me, config);

        me.on({
            scope: me,
            itemmouseover: me.onItemMouseOver,
            itemmouseout: me.onItemMouseOut,
            mouseleave: me.onMouseLeave
        });
        
        if (me.style) {
            Ext.apply(me.seriesStyle, me.style);
        }
    },
    
    onRedraw: Ext.emptyFn,
    
    
    eachRecord: function(fn, scope) {
        var chart = this.chart;
        chart.getChartStore().each(fn, scope);
    },

    
    getRecordCount: function() {
        var chart = this.chart,
            store = chart.getChartStore();
        return store ? store.getCount() : 0;
    },

    
    isExcluded: function(index) {
        var excludes = this.__excludes;
        return !!(excludes && excludes[index]);
    },

    
    setBBox: function(noGutter) {
        var me = this,
            chart = me.chart,
            chartBBox = chart.chartBBox,
            maxGutters = noGutter ? { left: 0, right: 0, bottom: 0, top: 0 } : chart.maxGutters,
            clipBox, bbox;

        clipBox = {
            x: chartBBox.x,
            y: chartBBox.y,
            width: chartBBox.width,
            height: chartBBox.height
        };
        me.clipBox = clipBox;

        bbox = {
            x: (clipBox.x + maxGutters.left) - (chart.zoom.x * chart.zoom.width),
            y: (clipBox.y + maxGutters.bottom) - (chart.zoom.y * chart.zoom.height),
            width: (clipBox.width - (maxGutters.left + maxGutters.right)) * chart.zoom.width,
            height: (clipBox.height - (maxGutters.bottom + maxGutters.top)) * chart.zoom.height
        };
        me.bbox = bbox;
    },

    
    onAnimate: function(sprite, attr) {
        var me = this;
        sprite.stopAnimation();
        if (me.animating) {
            return sprite.animate(Ext.applyIf(attr, me.chart.animate));
        } else {
            me.animating = true;
            return sprite.animate(Ext.apply(Ext.applyIf(attr, me.chart.animate), {
                
                callback: function() {
                    me.animating = false;
                    me.fireEvent('afterrender');
                }
            }));
        }
    },

    
    getGutters: function() {
        return this.nullGutters;
    },

    
    getPadding: function() {
        return this.nullPadding;
    },

    
    onItemMouseOver: function(item) {
        var me = this;
        if (item.series === me) {
            if (me.highlight) {
                me.highlightItem(item);
            }
            if (me.tooltip) {
                me.showTip(item);
            }
        }
    },

    
    onItemMouseOut: function(item) {
        var me = this;
        if (item.series === me) {
            me.unHighlightItem();
            if (me.tooltip) {
                me.hideTip(item);
            }
        }
    },

    
    onMouseLeave: function() {
        var me = this;
        me.unHighlightItem();
        if (me.tooltip) {
            me.hideTip();
        }
    },

    
    getItemForPoint: function(x, y) {
        
        if (!this.items || !this.items.length || this.seriesIsHidden) {
            return null;
        }
        var me = this,
            items = me.items,
            bbox = me.bbox,
            item, i, ln;
        
        if (!Ext.draw.Draw.withinBox(x, y, bbox)) {
            return null;
        }
        for (i = 0, ln = items.length; i < ln; i++) {
            if (items[i] && this.isItemInPoint(x, y, items[i], i)) {
                return items[i];
            }
        }

        return null;
    },

    isItemInPoint: function(x, y, item, i) {
        return false;
    },

    
    hideAll: function() {
        var me = this,
            items = me.items,
            item, len, i, j, l, sprite, shadows;

        me.seriesIsHidden = true;
        me._prevShowMarkers = me.showMarkers;

        me.showMarkers = false;
        
        me.hideLabels(0);
        
        for (i = 0, len = items.length; i < len; i++) {
            item = items[i];
            sprite = item.sprite;
            if (sprite) {
                sprite.setAttributes({
                    hidden: true
                }, true);
            }

            if (sprite && sprite.shadows) {
                shadows = sprite.shadows;
                for (j = 0, l = shadows.length; j < l; ++j) {
                    shadows[j].setAttributes({
                        hidden: true
                    }, true);
                }
            }
        }
    },

    
    showAll: function() {
        var me = this,
            prevAnimate = me.chart.animate;
        me.chart.animate = false;
        me.seriesIsHidden = false;
        me.showMarkers = me._prevShowMarkers;
        me.drawSeries();
        me.chart.animate = prevAnimate;
    },
    
    hide: function() {
        if (this.items) {
            var me = this,
                items = me.items,
                i, j, lsh, ln, shadows;
            
            if (items && items.length) {
                for (i = 0, ln = items.length; i < ln; ++i) {
                    if (items[i].sprite) {
                        items[i].sprite.hide(true);

                        shadows = items[i].shadows || items[i].sprite.shadows;
                        if (shadows) {
                            for (j = 0, lsh = shadows.length; j < lsh; ++j) {
                                shadows[j].hide(true);
                            }
                        }
                    }
                }
                me.hideLabels();
            }
        }
    },

    
    getLegendColor: function(index) {
        var me = this, fill, stroke;
        if (me.seriesStyle) {
            fill = me.seriesStyle.fill;
            stroke = me.seriesStyle.stroke;
            if (fill && fill != 'none') {
                return fill;
            }
            if(stroke){
                return stroke;
            }
        }
        return (me.colorArrayStyle)?me.colorArrayStyle[me.themeIdx % me.colorArrayStyle.length]:'#000';
    },

    
    visibleInLegend: function(index){
        var excludes = this.__excludes;
        if (excludes) {
            return !excludes[index];
        }
        return !this.seriesIsHidden;
    },

    
    setTitle: function(index, title) {
        var me = this,
            oldTitle = me.title;

        if (Ext.isString(index)) {
            title = index;
            index = 0;
        }

        if (Ext.isArray(oldTitle)) {
            oldTitle[index] = title;
        } else {
            me.title = title;
        }

        me.fireEvent('titlechange', title, index);
    }
});


Ext.define('Ext.chart.series.Cartesian', {

    

    extend:  Ext.chart.series.Series ,

    alternateClassName: ['Ext.chart.CartesianSeries', 'Ext.chart.CartesianChart'],

    

    
    xField: null,

    
    yField: null,

    
    axis: 'left',

    getLegendLabels: function() {
        var me = this,
            labels = [],
            fields, i, ln,
            combinations = me.combinations,
            title,
            combo, label0, label1;

        fields = [].concat(me.yField);
        for (i = 0, ln = fields.length; i < ln; i++) {
            title = me.title;
            
            labels.push((Ext.isArray(title) ? title[i] : title) || fields[i]);
        }

        
        
        if (combinations) {
            combinations = Ext.Array.from(combinations);
            for (i = 0, ln = combinations.length; i < ln; i++) {
                combo = combinations[i];
                label0 = labels[combo[0]];
                label1 = labels[combo[1]];
                labels[combo[1]] = label0 + ' & ' + label1;
                labels.splice(combo[0], 1);
            }
        }

        return labels;
    },

    
    eachYValue: function(record, fn, scope) {
        var me = this,
            yValueAccessors = me.getYValueAccessors(),
            i, ln, accessor;
        
        for (i = 0, ln = yValueAccessors.length; i < ln; i++) {
            accessor = yValueAccessors[i];
            fn.call(scope, accessor(record), i);
        }
    },

    
    getYValueCount: function() {
        return this.getYValueAccessors().length;
    },

    combine: function(index1, index2) {
        var me = this,
            accessors = me.getYValueAccessors(),
            accessor1 = accessors[index1],
            accessor2 = accessors[index2];

        
        accessors[index2] = function(record) {
            return accessor1(record) + accessor2(record);
        };
        accessors.splice(index1, 1);

        me.callParent([index1, index2]);
    },

    clearCombinations: function() {
        
        delete this.yValueAccessors;
        this.callParent();
    },

    
    getYValueAccessors: function() {
        var me = this,
            accessors = me.yValueAccessors,
            yFields, yField, i, ln;
        if (!accessors) {
            accessors = me.yValueAccessors = [];
            yFields = [].concat(me.yField);
            
            for (i = 0, ln = yFields.length; i < ln; i++) {
                yField = yFields[i];
                accessors.push(function(record) {
                    return record.get(yField);
                });
            }
        }
        return accessors;
    },

    
    getMinMaxXValues: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            count = me.getRecordCount(),
            i, ln, record,
            min, max,
            xField = me.xField,
            xValue;

        if (count > 0) {
            min = Infinity;
            max = -min;
                
            for (i = 0, ln = data.length; i < ln; i++) {
                record = data[i];
                xValue = record.get(xField);
                if (xValue > max) {
                    max = xValue;
                }
                if (xValue < min) {
                    min = xValue;
                }
            }
            
            
            if (min == Infinity) {
                min = 0;
            }
            
            if (max == -Infinity) {
                max = count - 1;
            }
        } else {
            min = max = 0;
        }
        return [min, max];
    },

    
    getMinMaxYValues: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            count = me.getRecordCount(),
            i, ln, record,
            stacked = me.stacked,
            min, max,
            positiveTotal, negativeTotal;

        function eachYValueStacked(yValue, i) {
            if (!me.isExcluded(i)) {
                if (yValue < 0) {
                    negativeTotal += yValue;
                } else {
                    positiveTotal += yValue;
                }
            }
        }

        function eachYValue(yValue, i) {
            if (!me.isExcluded(i)) {
                if (yValue > max) {
                    max = yValue;
                }
                if (yValue < min) {
                    min = yValue;
                }
            }
        }

        if (count > 0) {
            min = Infinity;
            max = -min;
            
            for (i = 0, ln = data.length; i < ln; i++) {
                record = data[i];
                if (stacked) {
                    positiveTotal = 0;
                    negativeTotal = 0;
                    me.eachYValue(record, eachYValueStacked);
                    if (positiveTotal > max) {
                        max = positiveTotal;
                    }
                    if (negativeTotal < min) {
                        min = negativeTotal;
                    }
                } else {
                    me.eachYValue(record, eachYValue);
                }
            }
            
            
            if (min == Infinity) {
                min = 0;
            }
            
            if (max == -Infinity) {
                max = count - 1;
            }
        } else {
            min = max = 0;
        }
        return [min, max];
    },

    getAxesForXAndYFields: function() {
        var me = this,
            axes = me.chart.axes,
            axis = [].concat(me.axis),
            yFields = {}, yFieldList = [].concat(me.yField),
            xFields = {}, xFieldList = [].concat(me.xField),
            fields, xAxis, yAxis, i, ln, flipXY;

        
        flipXY = me.type === 'bar' && me.column === false;
        if(flipXY) {
            fields = yFieldList;
            yFieldList = xFieldList;
            xFieldList = fields;
        }
        if (Ext.Array.indexOf(axis, 'top') > -1) {
            xAxis = 'top';
        } else if (Ext.Array.indexOf(axis, 'bottom') > -1) {
            xAxis = 'bottom';
        } else {
            if (axes.get('top') && axes.get('bottom')) {
                for (i = 0, ln = xFieldList.length; i < ln; i++) {
                    xFields[xFieldList[i]] = true;
                }
                fields = [].concat(axes.get('bottom').fields);
                for (i = 0, ln = fields.length; i < ln; i++) {
                    if (xFields[fields[i]]) {
                        xAxis = 'bottom';
                        break
                    }
                }
                fields = [].concat(axes.get('top').fields);
                for (i = 0, ln = fields.length; i < ln; i++) {
                    if (xFields[fields[i]]) {
                        xAxis = 'top';
                        break
                    }
                }
            } else if (axes.get('top')) {
                xAxis = 'top';
            } else if (axes.get('bottom')) {
                xAxis = 'bottom';
            }
        }
        if (Ext.Array.indexOf(axis, 'left') > -1) {
            yAxis = 'left';
        } else if (Ext.Array.indexOf(axis, 'right') > -1) {
            yAxis = 'right';
        } else {
            if (axes.get('left') && axes.get('right')) {
                for (i = 0, ln = yFieldList.length; i < ln; i++) {
                    yFields[yFieldList[i]] = true;
                }
                fields = [].concat(axes.get('right').fields);
                for (i = 0, ln = fields.length; i < ln; i++) {
                    if (yFields[fields[i]]) {

                        break
                    }
                }
                fields = [].concat(axes.get('left').fields);
                for (i = 0, ln = fields.length; i < ln; i++) {
                    if (yFields[fields[i]]) {
                        yAxis = 'left';
                        break
                    }
                }
            } else if (axes.get('left')) {
                yAxis = 'left';
            } else if (axes.get('right')) {
                yAxis = 'right';
            }
        }

        return flipXY ? {
            xAxis: yAxis,
            yAxis: xAxis
        }: {
            xAxis: xAxis,
            yAxis: yAxis
        };
    }


});


Ext.define('Ext.chart.series.Area', {

    

    extend:  Ext.chart.series.Cartesian ,

    alias: 'series.area',

                                                                       

    

    type: 'area',

    
    stacked: true,

    
    style: {},

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface,
            i, l;
        config.highlightCfg = Ext.Object.merge({}, {
            lineWidth: 3,
            stroke: '#55c',
            opacity: 0.8,
            color: '#f00'
        }, config.highlightCfg);

        Ext.apply(me, config, {
            __excludes: []
        });
        if (me.highlight) {
            me.highlightSprite = surface.add({
                type: 'path',
                path: ['M', 0, 0],
                zIndex: 1000,
                opacity: 0.3,
                lineWidth: 5,
                hidden: true,
                stroke: '#444'
            });
        }
        me.group = surface.getGroup(me.seriesId);
    },

    
    shrink: function(xValues, yValues, size) {
        var len = xValues.length,
            ratio = Math.floor(len / size),
            i, j,
            xSum = 0,
            yCompLen = this.areas.length,
            ySum = [],
            xRes = [],
            yRes = [];
        
        for (j = 0; j < yCompLen; ++j) {
            ySum[j] = 0;
        }
        for (i = 0; i < len; ++i) {
            xSum += +xValues[i];
            for (j = 0; j < yCompLen; ++j) {
                ySum[j] += +yValues[i][j];
            }
            if (i % ratio == 0) {
                
                xRes.push(xSum/ratio);
                for (j = 0; j < yCompLen; ++j) {
                    ySum[j] /= ratio;
                }
                yRes.push(ySum);
                
                xSum = 0;
                for (j = 0, ySum = []; j < yCompLen; ++j) {
                    ySum[j] = 0;
                }
            }
        }
        return {
            x: xRes,
            y: yRes
        };
    },

    
    getBounds: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            i, l, record,
            areas = [].concat(me.yField),
            areasLen = areas.length,
            xValues = [],
            yValues = [],
            infinity = Infinity,
            minX = infinity,
            minY = infinity,
            maxX = -infinity,
            maxY = -infinity,
            math = Math,
            mmin = math.min,
            mmax = math.max,
            boundAxis = me.getAxesForXAndYFields(),
            boundXAxis = boundAxis.xAxis,
            boundYAxis = boundAxis.yAxis,
            ends, allowDate, tmp,
            bbox, xScale, yScale, xValue, yValue, areaIndex, acumY, ln, sumValues, clipBox, areaElem, axis, out;

        me.setBBox();
        bbox = me.bbox;

        if (axis = chart.axes.get(boundXAxis)) {
            if (axis.type === 'Time') {
                allowDate = true;
            }
            ends = axis.applyData();
            minX = ends.from;
            maxX = ends.to;
        }

        if (axis = chart.axes.get(boundYAxis)) {
            ends = axis.applyData();
            minY = ends.from;
            maxY = ends.to;
        }

        
        if (me.xField && !Ext.isNumber(minX)) {
            axis = me.getMinMaxXValues();
            allowDate = true;
            minX = axis[0];
            maxX = axis[1];
        }

        if (me.yField && !Ext.isNumber(minY)) {
            axis = me.getMinMaxYValues();
            minY = axis[0];
            maxY = axis[1];
        }

        if (!Ext.isNumber(minY)) {
            minY = 0;
        }
        if (!Ext.isNumber(maxY)) {
            maxY = 0;
        }

        l = data.length;
        if (l > 0 && allowDate) {
            tmp = data[0].get(me.xField);
            if (typeof tmp != 'number') {
                tmp = +tmp;
                if (isNaN(tmp)) {
                    allowDate = false;
                }
            } 
        }
        for (i = 0; i < l; i++) {
            record = data[i];
            xValue = record.get(me.xField);
            yValue = [];
            if (typeof xValue != 'number') {
                if (allowDate) {
                    xValue = +xValue;
                } else {
                    xValue = i;
                }
            }
            xValues.push(xValue);
            acumY = 0;
            for (areaIndex = 0; areaIndex < areasLen; areaIndex++) {
                
                if (me.__excludes[areaIndex]) {
                    continue;
                }
                areaElem = record.get(areas[areaIndex]);
                if (typeof areaElem == 'number') {
                    yValue.push(areaElem);
                }
            }
            yValues.push(yValue);
        }

        xScale = bbox.width / ((maxX - minX) || 1);
        yScale = bbox.height / ((maxY - minY) || 1);

        ln = xValues.length;
        if ((ln > bbox.width) && me.areas) {
            sumValues = me.shrink(xValues, yValues, bbox.width);
            xValues = sumValues.x;
            yValues = sumValues.y;
        }

        return {
            bbox: bbox,
            minX: minX,
            minY: minY,
            xValues: xValues,
            yValues: yValues,
            xScale: xScale,
            yScale: yScale,
            areasLen: areasLen
        };
    },

    
    getPaths: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            first = true,
            bounds = me.getBounds(),
            bbox = bounds.bbox,
            items = me.items = [],
            componentPaths = [],
            componentPath,
            count = 0,
            paths = [],
            i, ln, x, y, xValue, yValue, acumY, areaIndex, prevAreaIndex, areaElem, path, startX;

        ln = bounds.xValues.length;
        
        for (i = 0; i < ln; i++) {
            xValue = bounds.xValues[i];
            yValue = bounds.yValues[i];
            x = bbox.x + (xValue - bounds.minX) * bounds.xScale;
            if (startX === undefined) {
                startX = x;
            }
            acumY = 0;
            count = 0;
            for (areaIndex = 0; areaIndex < bounds.areasLen; areaIndex++) {
                
                if (me.__excludes[areaIndex]) {
                    continue;
                }
                if (!componentPaths[areaIndex]) {
                    componentPaths[areaIndex] = [];
                }
                areaElem = yValue[count];
                acumY += areaElem;
                y = bbox.y + bbox.height - (acumY - bounds.minY) * bounds.yScale;
                if (!paths[areaIndex]) {
                    paths[areaIndex] = ['M', x, y];
                    componentPaths[areaIndex].push(['L', x, y]);
                } else {
                    paths[areaIndex].push('L', x, y);
                    componentPaths[areaIndex].push(['L', x, y]);
                }
                if (!items[areaIndex]) {
                    items[areaIndex] = {
                        pointsUp: [],
                        pointsDown: [],
                        series: me
                    };
                }
                items[areaIndex].pointsUp.push([x, y]);
                count++;
            }
        }

        
        for (areaIndex = 0; areaIndex < bounds.areasLen; areaIndex++) {
            
            if (me.__excludes[areaIndex]) {
                continue;
            }
            path = paths[areaIndex];

            
            if (areaIndex == 0 || first) {
                first = false;

                path.push('L', x, bbox.y + bbox.height,
                          'L', startX, bbox.y + bbox.height,
                          'Z');
            }
            
            else {
                componentPath = componentPaths[prevAreaIndex];
                componentPath.reverse();
                path.push('L', x, componentPath[0][2]);
                for (i = 0; i < ln; i++) {
                    path.push(componentPath[i][0],
                              componentPath[i][1],
                              componentPath[i][2]);
                    items[areaIndex].pointsDown[ln -i -1] = [componentPath[i][1], componentPath[i][2]];
                }
                path.push('L', startX, path[2], 'Z');
            }
            prevAreaIndex = areaIndex;
        }
        return {
            paths: paths,
            areasLen: bounds.areasLen
        };
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            surface = chart.surface,
            animate = chart.animate,
            group = me.group,
            endLineStyle = Ext.apply(me.seriesStyle, me.style),
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            themeIndex = me.themeIdx,
            areaIndex, areaElem, paths, path, rendererAttributes, idx;
        
        me.unHighlightItem();
        me.cleanHighlights();

        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            return;
        }

        paths = me.getPaths();

        if (!me.areas) {
            me.areas = [];
        }

        for (areaIndex = 0; areaIndex < paths.areasLen; areaIndex++) {
            
            if (me.__excludes[areaIndex]) {
                continue;
            }
            idx = themeIndex + areaIndex;
            if (!me.areas[areaIndex]) {
                me.items[areaIndex].sprite = me.areas[areaIndex] = surface.add(Ext.apply({}, {
                    type: 'path',
                    group: group,
                    
                    path: paths.paths[areaIndex],
                    stroke: endLineStyle.stroke || colorArrayStyle[idx % colorArrayLength],
                    fill: colorArrayStyle[idx % colorArrayLength]
                }, endLineStyle || {}));
            }
            areaElem = me.areas[areaIndex];
            path = paths.paths[areaIndex];
            if (animate) {
                
                rendererAttributes = me.renderer(areaElem, false, {
                    path: path,
                    
                    fill: colorArrayStyle[areaIndex % colorArrayLength],
                    stroke: endLineStyle.stroke || colorArrayStyle[areaIndex % colorArrayLength]
                }, areaIndex, store);
                
                me.animation = me.onAnimate(areaElem, {
                    to: rendererAttributes
                });
            } else {
                rendererAttributes = me.renderer(areaElem, false, {
                    path: path,
                    
                    hidden: false,
                    fill: colorArrayStyle[idx % colorArrayLength],
                    stroke: endLineStyle.stroke || colorArrayStyle[idx % colorArrayLength]
                }, areaIndex, store);
                me.areas[areaIndex].setAttributes(rendererAttributes, true);
            }
        }
        me.renderLabels();
        me.renderCallouts();
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        
        
        
        
        
        
        
        
        
        return null;

        var me = this,
            group = me.labelsGroup,
            config = me.label,
            bbox = me.bbox,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {});

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': Number(item.point[0]),
            'y': bbox.y + bbox.height / 2
        }, endLabelStyle || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            bbox = me.bbox,
            x = Number(item.point[i][0]),
            y = Number(item.point[i][1]),
            labelBox, width, height;

        label.setAttributes({
            text: format(storeItem.get(field[index]), label, storeItem, item, i, display, animate, index),
            hidden: true
        }, true);

        labelBox = label.getBBox();
        width = labelBox.width / 2;
        height = labelBox.height / 2;

        
        if (x < bbox.x + width) {
            x = bbox.x + width;
        } else if (x + width > bbox.x + bbox.width) {
            x = bbox.x + bbox.width - width;
        }

        y = y - height;
        if (y < bbox.y + height) {
            y += 2 * height;
        } else if (y + height > bbox.y + bbox.height) {
            y -= 2 * height;
        }

        if (me.chart.animate && !me.chart.resizing) {
            label.show(true);
            me.onAnimate(label, {
                to: {
                    x: x,
                    y: y
                }
            });
        } else {
            label.setAttributes({
                x: x,
                y: y
            }, true);
            if (resizing && me.animation) {
                me.animation.on('afteranimate', function() {
                    label.show(true);
                });
            } else {
                label.show(true);
            }
        }
    },

    
    onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            resizing = chart.resizing,
            config = me.callouts,
            items = me.items,
            prev = (i == 0) ? false : items[i -1].point,
            next = (i == items.length -1) ? false : items[i +1].point,
            cur = item.point,
            dir, norm, normal, a, aprev, anext,
            bbox = (callout && callout.label ? callout.label.getBBox() : {width:0,height:0}),
            offsetFromViz = 30,
            offsetToSide = 10,
            offsetBox = 3,
            boxx, boxy, boxw, boxh,
            p, clipRect = me.clipRect,
            x, y;

        if (!bbox.width || !bbox.height) {
            return;
        }

        
        if (!prev) {
            prev = cur;
        }
        if (!next) {
            next = cur;
        }
        a = (next[1] - prev[1]) / (next[0] - prev[0]);
        aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]);
        anext = (next[1] - cur[1]) / (next[0] - cur[0]);

        norm = Math.sqrt(1 + a * a);
        dir = [1 / norm, a / norm];
        normal = [-dir[1], dir[0]];

        
        if (aprev > 0 && anext < 0 && normal[1] < 0 || aprev < 0 && anext > 0 && normal[1] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0 || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        
        
        if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
            normal[0] *= -1;
        }
        if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        
        callout.lines.setAttributes({
            path: ["M", cur[0], cur[1], "L", x, y, "Z"]
        }, true);
        
        callout.box.setAttributes({
            x: boxx,
            y: boxy,
            width: boxw,
            height: boxh
        }, true);
        
        callout.label.setAttributes({
            x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
            y: y
        }, true);
        for (p in callout) {
            callout[p].show(true);
        }
    },

    isItemInPoint: function(x, y, item, i) {
        var me = this,
            pointsUp = item.pointsUp,
            pointsDown = item.pointsDown,
            abs = Math.abs,
            distChanged = false,
            last = false,
            dist = Infinity, p, pln, point;

        for (p = 0, pln = pointsUp.length; p < pln; p++) {
            point = [pointsUp[p][0], pointsUp[p][1]];
            
            distChanged = false;
            last = p == pln -1;

            if (dist > abs(x - point[0])) {
                dist = abs(x - point[0]);
                distChanged = true;
                if (last) {
                    ++p;
                }
            }
            
            if (!distChanged || (distChanged && last)) {
                point = pointsUp[p -1];
                if (y >= point[1] && (!pointsDown.length || y <= (pointsDown[p -1][1]))) {
                    item.storeIndex = p -1;
                    item.storeField = me.yField[i];
                    item.storeItem = me.chart.getChartStore().getAt(p -1);
                    item._points = pointsDown.length? [point, pointsDown[p -1]] : [point];
                    return true;
                } else {
                    break;
                }
            }
        }
        return false;
    },

    
    highlightSeries: function() {
        var area, to, fillColor;
        if (this._index !== undefined) {
            area = this.areas[this._index];
            if (area.__highlightAnim) {
                area.__highlightAnim.paused = true;
            }
            area.__highlighted = true;
            area.__prevOpacity = area.__prevOpacity || area.attr.opacity || 1;
            area.__prevFill = area.__prevFill || area.attr.fill;
            area.__prevLineWidth = area.__prevLineWidth || area.attr.lineWidth;
            fillColor = Ext.draw.Color.fromString(area.__prevFill);
            to = {
                lineWidth: (area.__prevLineWidth || 0) + 2
            };
            if (fillColor) {
                to.fill = fillColor.getLighter(0.2).toString();
            }
            else {
                to.opacity = Math.max(area.__prevOpacity - 0.3, 0);
            }
            if (this.chart.animate) {
                area.__highlightAnim = new Ext.fx.Anim(Ext.apply({
                    target: area,
                    to: to
                }, this.chart.animate));
            }
            else {
                area.setAttributes(to, true);
            }
        }
    },

    
    unHighlightSeries: function() {
        var area;
        if (this._index !== undefined) {
            area = this.areas[this._index];
            if (area.__highlightAnim) {
                area.__highlightAnim.paused = true;
            }
            if (area.__highlighted) {
                area.__highlighted = false;
                area.__highlightAnim = new Ext.fx.Anim({
                    target: area,
                    to: {
                        fill: area.__prevFill,
                        opacity: area.__prevOpacity,
                        lineWidth: area.__prevLineWidth
                    }
                });
            }
        }
    },

    
    highlightItem: function(item) {
        var me = this,
            points, path;
        if (!item) {
            this.highlightSeries();
            return;
        }
        points = item._points;
        path = points.length == 2? ['M', points[0][0], points[0][1], 'L', points[1][0], points[1][1]]
                : ['M', points[0][0], points[0][1], 'L', points[0][0], me.bbox.y + me.bbox.height];
        me.highlightSprite.setAttributes({
            path: path,
            hidden: false
        }, true);
    },

    
    unHighlightItem: function(item) {
        if (!item) {
            this.unHighlightSeries();
        }

        if (this.highlightSprite) {
            this.highlightSprite.hide(true);
        }
    },

    
    hideAll: function(index) {
        var me = this;
        index = (isNaN(me._index) ? index : me._index) || 0;
        me.__excludes[index] = true;
        me.areas[index].hide(true);
        me.redraw();
    },

    
    showAll: function(index) {
        var me = this;
        index = (isNaN(me._index) ? index : me._index) || 0;
        me.__excludes[index] = false;
        me.areas[index].show(true);
        me.redraw();
    },

    redraw: function() {
        
        
        
        var me = this,
            prevLegendConfig;
        prevLegendConfig = me.chart.legend.rebuild;
        me.chart.legend.rebuild = false;
        me.chart.redraw();
        me.chart.legend.rebuild = prevLegendConfig;
    },
    
    hide: function() {
        if (this.areas) {
            var me = this,
                areas = me.areas,
                i, j, l, ln, shadows;
            
            if (areas && areas.length) {
                for (i = 0, ln = areas.length; i < ln; ++i) {
                    if (areas[i]) {
                        areas[i].hide(true);
                    }
                }
                me.hideLabels();
            }
        }
    },

    
    getLegendColor: function(index) {
        var me = this;
        index += me.themeIdx;
        return me.colorArrayStyle[index % me.colorArrayStyle.length];
    }
});


Ext.define('Ext.chart.series.Bar', {

    

    extend:  Ext.chart.series.Cartesian ,

    alternateClassName: ['Ext.chart.BarSeries', 'Ext.chart.BarChart', 'Ext.chart.StackedBarChart'],

                                                     

    

    type: 'bar',

    alias: 'series.bar',
    
    column: false,

    
    style: {},

    
    gutter: 38.2,

    
    groupGutter: 38.2,

    
    xPadding: 0,

    
    yPadding: 10,

    

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface,
            shadow = me.chart.shadow,
            i, l;
        config.highlightCfg = Ext.Object.merge({
            lineWidth: 3,
            stroke: '#55c',
            opacity: 0.8,
            color: '#f00'
        }, config.highlightCfg);
        Ext.apply(me, config, {
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 0.05,
                stroke: 'rgb(200, 200, 200)',
                translate: {
                    x: 1.2,
                    y: 1.2
                }
            }, {
                "stroke-width": 4,
                "stroke-opacity": 0.1,
                stroke: 'rgb(150, 150, 150)',
                translate: {
                    x: 0.9,
                    y: 0.9
                }
            }, {
                "stroke-width": 2,
                "stroke-opacity": 0.15,
                stroke: 'rgb(100, 100, 100)',
                translate: {
                    x: 0.6,
                    y: 0.6
                }
            }]
        });

        me.group = surface.getGroup(me.seriesId + '-bars');
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
    },

    
    getPadding: function() {
        var me = this,
            xPadding = me.xPadding,
            yPadding = me.yPadding,
            padding = { };

        if (Ext.isNumber(xPadding)) {
            padding.left = xPadding;
            padding.right = xPadding;
        } else if (Ext.isObject(xPadding)) {
            padding.left = xPadding.left;
            padding.right = xPadding.right;
        } else {
            padding.left = 0;
            padding.right = 0;
        }
        padding.width = padding.left + padding.right;

        if (Ext.isNumber(yPadding)) {
            padding.bottom = yPadding;
            padding.top = yPadding;
        } else if (Ext.isObject(yPadding)) {
            padding.bottom = yPadding.bottom;
            padding.top = yPadding.top;
        } else {
            padding.bottom = 0;
            padding.top = 0;
        }
        padding.height = padding.bottom + padding.top;

        return padding;
    },

    
    getBarGirth: function() {
        var me = this,
            store = me.chart.getChartStore(),
            column = me.column,
            ln = store.getCount(),
            gutter = me.gutter / 100,
            padding,
            property;

        if (me.style && me.style.width) {
            return me.style.width;
        }
        padding = me.getPadding();
        property = (column ? 'width' : 'height');
        return (me.chart.chartBBox[property] - padding[property]) / (ln * (gutter + 1) - gutter);
    },

    
    getGutters: function() {
        var me = this,
            column = me.column,
            padding = me.getPadding(),
            halfBarGirth = me.getBarGirth() / 2,
            lowerGutter = Math.ceil((column ? padding.left : padding.bottom) + halfBarGirth),
            upperGutter = Math.ceil((column ? padding.right : padding.top) + halfBarGirth);

        return {
            lower: lowerGutter,
            upper: upperGutter,
            verticalAxis: !column
        };
    },

    
    getBounds: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            i, ln, record,
            bars = [].concat(me.yField),
            barsLoc,
            barsLen = bars.length,
            groupBarsLen = barsLen,
            groupGutter = me.groupGutter / 100,
            column = me.column,
            padding = me.getPadding(),
            stacked = me.stacked,
            barWidth = me.getBarGirth(),
            barWidthProperty = column ? 'width' : 'height',
            math = Math,
            mmin = math.min,
            mmax = math.max,
            mabs = math.abs,
            boundAxes = me.getAxesForXAndYFields(),
            boundYAxis = boundAxes.yAxis,
            minX, maxX, colsScale, colsZero, gutters,
            ends, shrunkBarWidth, groupBarWidth, bbox, minY, maxY, axis, out,
            scale, zero, total, rec, j, plus, minus;

        me.setBBox(true);
        bbox = me.bbox;

        
        if (me.__excludes) {
            for (j = 0, total = me.__excludes.length; j < total; j++) {
                if (me.__excludes[j]) {
                    groupBarsLen--;
                }
            }
        }
        axis = chart.axes.get(boundYAxis);
        if (axis) {
            ends = axis.applyData();
            minY = ends.from;
            maxY = ends.to;
        }

        if (me.yField && !Ext.isNumber(minY)) {
            out = me.getMinMaxYValues();
            minY = out[0];
            maxY = out[1];
        }

        if (!Ext.isNumber(minY)) {
            minY = 0;
        }
        if (!Ext.isNumber(maxY)) {
            maxY = 0;
        }
        scale = (column ? bbox.height - padding.height : bbox.width - padding.width) / (maxY - minY);
        shrunkBarWidth = barWidth;
        groupBarWidth = (barWidth / ((stacked ? 1 : groupBarsLen) * (groupGutter + 1) - groupGutter));
        
        if (barWidthProperty in me.style) {
            groupBarWidth = mmin(groupBarWidth, me.style[barWidthProperty]);
            shrunkBarWidth = groupBarWidth * ((stacked ? 1 : groupBarsLen) * (groupGutter + 1) - groupGutter);
        }
        zero = (column) ? bbox.y + bbox.height - padding.bottom : bbox.x + padding.left;

        if (stacked) {
            total = [[], []];
            for (i = 0, ln = data.length; i < ln; i++) {
                record = data[i];
                total[0][i] = total[0][i] || 0;
                total[1][i] = total[1][i] || 0;
                for (j = 0; j < barsLen; j++) {
                    if (me.__excludes && me.__excludes[j]) {
                        continue;
                    }
                    rec = record.get(bars[j]);
                    total[+(rec > 0)][i] += mabs(rec);
                }
            }
            total[+(maxY > 0)].push(mabs(maxY));
            total[+(minY > 0)].push(mabs(minY));
            minus = mmax.apply(math, total[0]);
            plus = mmax.apply(math, total[1]);
            scale = (column ? bbox.height - padding.height : bbox.width - padding.width) / (plus + minus);
            zero = zero + minus * scale * (column ? -1 : 1);
        }
        else if (minY / maxY < 0) {
            zero = zero - minY * scale * (column ? -1 : 1);
        }

        
        if (me.boundColumn) {
            axis = chart.axes.get(boundAxes.xAxis);
            if (axis) {
                ends = axis.applyData();
                minX = ends.from;
                maxX = ends.to;
            }
            if (me.xField && !Ext.isNumber(minX)) {
                out = me.getMinMaxYValues();
                minX = out[0];
                maxX = out[1];
            }
            if (!Ext.isNumber(minX)) {
                minX = 0;
            }
            if (!Ext.isNumber(maxX)) {
                maxX = 0;
            }
            gutters = me.getGutters();
            colsScale = (bbox.width - (gutters.lower + gutters.upper)) / ((maxX - minX) || 1);

            colsZero = bbox.x + gutters.lower;
        
            barsLoc = [];
            for (i = 0, ln = data.length; i < ln; i++) {
                record = data[i];
                rec = record.get(me.xField);
                barsLoc[i] = colsZero + (rec - minX) * colsScale - (groupBarWidth / 2);
            }
        }

        return {
            bars: bars,
            barsLoc: barsLoc,
            bbox: bbox,
            shrunkBarWidth: shrunkBarWidth,
            barsLen: barsLen,
            groupBarsLen: groupBarsLen,
            barWidth: barWidth,
            groupBarWidth: groupBarWidth,
            scale: scale,
            zero: zero,
            padding: padding,
            signed: minY / maxY < 0,
            minY: minY,
            maxY: maxY
        };
    },

    
    getPaths: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            data = store.data.items,
            i, total, record,
            bounds = me.bounds = me.getBounds(),
            items = me.items = [],
            yFields = Ext.isArray(me.yField) ? me.yField : [me.yField],
            gutter = me.gutter / 100,
            groupGutter = me.groupGutter / 100,
            animate = chart.animate,
            column = me.column,
            group = me.group,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            shadowGroupsLn = shadowGroups.length,
            bbox = bounds.bbox,
            barWidth = bounds.barWidth,
            shrunkBarWidth = bounds.shrunkBarWidth,
            padding = me.getPadding(),
            stacked = me.stacked,
            barsLen = bounds.barsLen,
            colors = me.colorArrayStyle,
            colorLength = colors && colors.length || 0,
            themeIndex = me.themeIdx,
            math = Math,
            mmax = math.max,
            mmin = math.min,
            mabs = math.abs,
            j, yValue, height, totalDim, totalNegDim, bottom, top, hasShadow, barAttr, attrs, counter,
            totalPositiveValues, totalNegativeValues,
            shadowIndex, shadow, sprite, offset, floorY, idx;

        for (i = 0, total = data.length; i < total; i++) {
            record = data[i];
            bottom = bounds.zero;
            top = bounds.zero;
            totalDim = 0;
            totalNegDim = 0;
            totalPositiveValues = totalNegativeValues = 0;
            hasShadow = false;
            for (j = 0, counter = 0; j < barsLen; j++) {
                
                if (me.__excludes && me.__excludes[j]) {
                    continue;
                }
                yValue = record.get(bounds.bars[j]);
                if (yValue >= 0) {
                    totalPositiveValues += yValue;
                }
                else {
                    totalNegativeValues += yValue;
                }
                height = Math.round((yValue - mmax(bounds.minY, 0)) * bounds.scale);
                idx = themeIndex + (barsLen > 1 ? j : 0);
                barAttr = {
                    fill: colors[idx % colorLength]
                };
                if (column) {
                    Ext.apply(barAttr, {
                        height: height,
                        width: mmax(bounds.groupBarWidth, 0),
                        x: (me.boundColumn ? bounds.barsLoc[i] 
                                           : (bbox.x + padding.left 
                                                + (barWidth - shrunkBarWidth) * 0.5
                                                + i * barWidth * (1 + gutter) 
                                                + counter * bounds.groupBarWidth * (1 + groupGutter) * !stacked)),
                        y: bottom - height
                    });
                }
                else {
                    
                    offset = (total - 1) - i;
                    Ext.apply(barAttr, {
                        height: mmax(bounds.groupBarWidth, 0),
                        width: height + (bottom == bounds.zero),
                        x: bottom + (bottom != bounds.zero),
                        y: (bbox.y + padding.top 
                            + (barWidth - shrunkBarWidth) * 0.5 
                            + offset * barWidth * (1 + gutter) 
                            + counter * bounds.groupBarWidth * (1 + groupGutter) * !stacked + 1)
                    });
                }
                if (height < 0) {
                    if (column) {
                        barAttr.y = top;
                        barAttr.height = mabs(height);
                    } else {
                        barAttr.x = top + height;
                        barAttr.width = mabs(height);
                    }
                }
                if (stacked) {
                    if (height < 0) {
                        top += height * (column ? -1 : 1);
                    } else {
                        bottom += height * (column ? -1 : 1);
                    }
                    totalDim += mabs(height);
                    if (height < 0) {
                        totalNegDim += mabs(height);
                    }
                }
                barAttr.x = Math.floor(barAttr.x) + 1;
                floorY = Math.floor(barAttr.y);
                if (Ext.isIE8m && barAttr.y > floorY) {
                    floorY--;
                }
                barAttr.y = floorY;
                barAttr.width = Math.floor(barAttr.width);
                barAttr.height = Math.floor(barAttr.height);
                items.push({
                    series: me,
                    yField: yFields[j],
                    storeItem: record,
                    value: [record.get(me.xField), yValue],
                    attr: barAttr,
                    point: column ? [barAttr.x + barAttr.width / 2, yValue >= 0 ? barAttr.y : barAttr.y + barAttr.height] :
                                    [yValue >= 0 ? barAttr.x + barAttr.width : barAttr.x, barAttr.y + barAttr.height / 2]
                });
                
                if (animate && chart.resizing) {
                    attrs = column ? {
                        x: barAttr.x,
                        y: bounds.zero,
                        width: barAttr.width,
                        height: 0
                    } : {
                        x: bounds.zero,
                        y: barAttr.y,
                        width: 0,
                        height: barAttr.height
                    };
                    if (enableShadows && (stacked && !hasShadow || !stacked)) {
                        hasShadow = true;
                        
                        for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                            shadow = shadowGroups[shadowIndex].getAt(stacked ? i : (i * barsLen + j));
                            if (shadow) {
                                shadow.setAttributes(attrs, true);
                            }
                        }
                    }
                    
                    sprite = group.getAt(i * barsLen + j);
                    if (sprite) {
                        sprite.setAttributes(attrs, true);
                    }
                }
                counter++;
            }
            if (stacked && items.length) {
                items[i * counter].totalDim = totalDim;
                items[i * counter].totalNegDim = totalNegDim;
                items[i * counter].totalPositiveValues = totalPositiveValues;
                items[i * counter].totalNegativeValues = totalNegativeValues;
            }
        }
        if (stacked && counter == 0) {
            
            for (i = 0, total = data.length; i < total; i++) {
                for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                    shadow = shadowGroups[shadowIndex].getAt(i);
                    if (shadow) {
                        shadow.hide(true);
                    }
                }
            }
        }
    },

    
    renderShadows: function(i, barAttr, baseAttrs, bounds) {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            animate = chart.animate,
            stacked = me.stacked,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            shadowGroupsLn = shadowGroups.length,
            store = chart.getChartStore(),
            column = me.column,
            items = me.items,
            shadows = [],
            zero = bounds.zero,
            shadowIndex, shadowBarAttr, shadow, totalDim, totalNegDim, j, rendererAttributes;

        if ((stacked && (i % bounds.groupBarsLen === 0)) || !stacked) {
            j = i / bounds.groupBarsLen;
            
            for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                shadowBarAttr = Ext.apply({}, shadowAttributes[shadowIndex]);
                shadow = shadowGroups[shadowIndex].getAt(stacked ? j : i);
                Ext.copyTo(shadowBarAttr, barAttr, 'x,y,width,height');
                if (!shadow) {
                    shadow = surface.add(Ext.apply({
                        type: 'rect',
                        group: shadowGroups[shadowIndex]
                    }, Ext.apply({}, baseAttrs, shadowBarAttr)));
                }
                if (stacked) {
                    totalDim = items[i].totalDim;
                    totalNegDim = items[i].totalNegDim;
                    if (column) {
                        shadowBarAttr.y = zero + totalNegDim - totalDim - 1;
                        shadowBarAttr.height = totalDim;
                    }
                    else {
                        shadowBarAttr.x = zero - totalNegDim;
                        shadowBarAttr.width = totalDim;
                    }
                }
                
                rendererAttributes = me.renderer(shadow, store.getAt(j), shadowBarAttr, i, store);
                rendererAttributes.hidden = !!barAttr.hidden;
                if (animate) {
                    me.onAnimate(shadow, { to: rendererAttributes });
                }
                else {
                    shadow.setAttributes(rendererAttributes, true);
                }
                shadows.push(shadow);
            }
        }
        return shadows;
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            surface = chart.surface,
            animate = chart.animate,
            stacked = me.stacked,
            column = me.column,
            chartAxes = chart.axes,
            boundAxes = me.getAxesForXAndYFields(),
            boundXAxis = boundAxes.xAxis,
            boundYAxis = boundAxes.yAxis,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowGroupsLn = shadowGroups.length,
            group = me.group,
            seriesStyle = me.seriesStyle,
            items, ln, i, j, baseAttrs, sprite, rendererAttributes, shadowIndex, shadowGroup,
            bounds, endSeriesStyle, barAttr, attrs, anim;

        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            return;
        }

        
        endSeriesStyle = Ext.apply({}, this.style, seriesStyle);
        delete endSeriesStyle.fill;
        delete endSeriesStyle.x;
        delete endSeriesStyle.y;
        delete endSeriesStyle.width;
        delete endSeriesStyle.height;
        
        me.unHighlightItem();
        me.cleanHighlights();
        
        me.boundColumn = (boundXAxis && Ext.Array.contains(me.axis,boundXAxis) 
                            && chartAxes.get(boundXAxis) 
                            && chartAxes.get(boundXAxis).isNumericAxis);

        me.getPaths();
        bounds = me.bounds;
        items = me.items;

        baseAttrs = column ? {
            y: bounds.zero,
            height: 0
        } : {
            x: bounds.zero,
            width: 0
        };
        ln = items.length;

        
        for (i = 0; i < ln; i++) {
            sprite = group.getAt(i);
            barAttr = items[i].attr;

            if (enableShadows) {
                items[i].shadows = me.renderShadows(i, barAttr, baseAttrs, bounds);
            }

            
            if (!sprite) {
                attrs = Ext.apply({}, baseAttrs, barAttr);
                attrs = Ext.apply(attrs, endSeriesStyle || {});
                sprite = surface.add(Ext.apply({}, {
                    type: 'rect',
                    group: group
                }, attrs));
            }
            if (animate) {
                rendererAttributes = me.renderer(sprite, store.getAt(i), barAttr, i, store);
                sprite._to = rendererAttributes;
                anim = me.onAnimate(sprite, { to: Ext.apply(rendererAttributes, endSeriesStyle) });
                if (enableShadows && stacked && (i % bounds.barsLen === 0)) {
                    j = i / bounds.barsLen;
                    for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                        anim.on('afteranimate', function() {
                            this.show(true);
                        }, shadowGroups[shadowIndex].getAt(j));
                    }
                }
            }
            else {
                rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply(barAttr, { hidden: false }), i, store);
                sprite.setAttributes(Ext.apply(rendererAttributes, endSeriesStyle), true);
            }
            items[i].sprite = sprite;
        }

        
        ln = group.getCount();
        for (j = i; j < ln; j++) {
            group.getAt(j).hide(true);
        }
        
        if (me.stacked) {
            
            i = store.getCount();    
        }
        
        
        if (enableShadows) {
            for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                shadowGroup = shadowGroups[shadowIndex];
                ln = shadowGroup.getCount();
                for (j = i; j < ln; j++) {
                    shadowGroup.getAt(j).hide(true);
                }
            }
        }
        me.renderLabels();
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            surface = me.chart.surface,
            group = me.labelsGroup,
            config = me.label,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {}),
            sprite;

        return surface.add(Ext.apply({
            type: 'text',
            group: group
        }, endLabelStyle || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        
        
        var me = this,
            opt = me.bounds,
            groupBarWidth = opt.groupBarWidth,
            column = me.column,
            chart = me.chart,
            chartBBox = chart.chartBBox,
            resizing = chart.resizing,
            xValue = item.value[0],
            yValue = item.value[1],
            attr = item.attr,
            config = me.label,
            stacked = me.stacked,
            stackedDisplay = config.stackedDisplay,
            rotate = (config.orientation == 'vertical'),
            field = [].concat(config.field),
            format = config.renderer,
            text, size, width, height,
            zero = opt.zero,
            insideStart = 'insideStart',
            insideEnd = 'insideEnd',
            outside = 'outside',
            over = 'over',
            under = 'under',
            labelMarginX = 4,   
            labelMarginY = 2,
            signed = opt.signed,
            x, y, finalAttr;

        if (display == insideStart || display == insideEnd || display == outside) {
            if (stacked && (display == outside)) {
                
                
                label.hide(true);
                return;
            }
            label.setAttributes({
                
                
                style: undefined
            });
            text = (Ext.isNumber(index) ? format(storeItem.get(field[index]), label, storeItem, item, i, display, animate, index) : '');
            label.setAttributes({
                
                text: text
            });
            size = me.getLabelSize(text, label.attr.style);
            width = size.width;
            height = size.height;
            if (column) {
                
                
            
                
                
                if (!width || !height || (stacked && (attr.height < height))) {
                    label.hide(true);
                    return;
                }
                
                
                x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2);
                
                
                if (display == outside) {
                    var free = (yValue >= 0 ? (attr.y - chartBBox.y) : (chartBBox.y + chartBBox.height - attr.y - attr.height));
                    if (free < height + labelMarginY) {
                        display = insideEnd;
                    }
                }
    
                
                
                if (!stacked && (display != outside)) {
                    if (height + labelMarginY > attr.height) {
                        display = outside;
                    }
                }
    
                
                
                if (!y) {
                    y = attr.y;
                    if (yValue >= 0) {
                        switch (display) {
                            case insideStart: y += attr.height + (rotate ? -labelMarginY : -height/2);  break;
                            case insideEnd:   y += (rotate ? height + labelMarginX : height/2);         break;
                            case outside:     y += (rotate ? -labelMarginY : -height/2);                break;
                        }
                    } else {
                        switch (display) {
                            case insideStart: y += (rotate ? height + labelMarginY : height/2);                             break;
                            case insideEnd:   y += (rotate ? attr.height - labelMarginY : attr.height - height/2);          break;
                            case outside:     y += (rotate ? attr.height + height + labelMarginY : attr.height + height/2); break;
                        }
                    }
                }
            }
            else {
                
                
    
                
                if (!width || !height || (stacked && !attr.width)) {
                    label.hide(true);
                    return;
                }
    
                
                y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2);
    
                
                if (display == outside) {
                    var free = (yValue >= 0 ? (chartBBox.x + chartBBox.width - attr.x - attr.width) :  (attr.x - chartBBox.x));
                    if (free < width + labelMarginX) {
                        display = insideEnd;
                    }
                }
    
                
                
                
                if ((display != outside) && !rotate) {
                    if (width + labelMarginX > attr.width) {
                        if (stacked) {
                            if (height > attr.width) {
                                label.hide(true);
                                return; 
                            }
                            x = attr.x + attr.width/2;
                            y = attr.y + attr.height - (attr.height - width)/2;
                            rotate = true;
                        } else {
                            display = outside;
                        }
                    }
                }
    
                
                
                if (!x) {
                    x = attr.x;
                    if (yValue >= 0) {
                        switch (display) {
                            case insideStart: x += (rotate ? width/2 : labelMarginX);                           break;
                            case insideEnd:   x += attr.width + (rotate ? -width/2 : -width - labelMarginX);    break;
                            case outside:     x += attr.width + (rotate ? width/2 : labelMarginX);              break;
                        }
                    } else {
                        switch (display) {
                            case insideStart: x += attr.width + (rotate ? -width/2 : -width - labelMarginX);    break;
                            case insideEnd:   x += (rotate ? width/2 : labelMarginX);                           break;
                            case outside:     x += (rotate ? -width/2 : -width - labelMarginX);                 break;
                        }
                    }
                }
            }
        } else if (display == over || display == under) {
            if (stacked && stackedDisplay) {
                
                
    
                text = label.attr.text;
                label.setAttributes({
                    
                    
                    style: Ext.applyIf((label.attr && label.attr.style) || {},
                        {
                            'font-weight':'bold',
                            'font-size':'14px'
                        }
                    )
                });

                size = me.getLabelSize(text, label.attr.style);
                width = size.width;
                height = size.height;
    
                switch (display) {
                    case over:
                        if (column) {
                            x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2);
                            y = zero - (item.totalDim - item.totalNegDim) - height/2 - labelMarginY;
                        } else {
                            x = zero + (item.totalDim - item.totalNegDim) + labelMarginX;
                            y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2);
                        }
                        break;
                    case under:
                        if (column) {
                            x = attr.x + (rotate ? groupBarWidth/2 : (groupBarWidth - width)/2);
                            y = zero + item.totalNegDim + height/2;
                        } else {
                            x = zero - item.totalNegDim - width - labelMarginX;
                            y = attr.y + (rotate ? (groupBarWidth + height)/2 : groupBarWidth/2);
                        }
                        break;
                }
            }
        }
        
        if (x == undefined || y == undefined) {
            
            label.hide(true);
            return;
        }

        label.isOutside = (display == outside);
        label.setAttributes({
            text: text
        });

        
        finalAttr = {
            x: x,
            y: y
        };
        
        if (rotate) {
            finalAttr.rotate = {
                x: x,
                y: y,
                degrees: 270
            };
        }
        
        if (animate && resizing) {
            if (column) {
                x = attr.x + attr.width / 2;
                y = zero;
            } else {
                x = zero;
                y = attr.y + attr.height / 2;
            }
            label.setAttributes({
                x: x,
                y: y
            }, true);
            if (rotate) {
                label.setAttributes({
                    rotate: {
                        x: x,
                        y: y,
                        degrees: 270
                    }
                }, true);
            }
        }
        
        if (animate) {
            me.onAnimate(label, { to: finalAttr });
        }
        else {
            label.setAttributes(Ext.apply(finalAttr, {
                hidden: false
            }), true);
        }
    },

    
    getLabelSize: function(value, labelStyle) {
        var tester = this.testerLabel,
            config = this.label,
            endLabelStyle = Ext.apply({}, config, labelStyle, this.seriesLabelStyle || {}),
            rotated = config.orientation === 'vertical',
            bbox, w, h,
            undef;
        if (!tester) {
            tester = this.testerLabel = this.chart.surface.add(Ext.apply({
                type: 'text',
                opacity: 0
            }, endLabelStyle));
        }
        tester.setAttributes({
            style: labelStyle,
            text: value
        }, true);

        
        bbox = tester.getBBox();
        w = bbox.width;
        h = bbox.height;
        return {
            width: rotated ? h : w,
            height: rotated ? w : h
        };
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item) {
        var bbox = item.sprite.getBBox();
        return bbox.x <= x && bbox.y <= y
            && (bbox.x + bbox.width) >= x
            && (bbox.y + bbox.height) >= y;
    },

    
    hideAll: function(index) {
        var axes      = this.chart.axes,
            axesItems = axes.items,
            ln        = axesItems.length,
            i         = 0;

        index = (isNaN(this._index) ? index : this._index) || 0;

        if (!this.__excludes) {
            this.__excludes = [];
        }

        this.__excludes[index] = true;
        this.drawSeries();

        for (i; i < ln; i++) {
            axesItems[i].drawAxis();
        }    
    },

    
    showAll: function(index) {
        var axes = this.chart.axes,
            axesItems = axes.items,
            ln        = axesItems.length,
            i         = 0;

        index = (isNaN(this._index) ? index : this._index) || 0;

        if (!this.__excludes) {
            this.__excludes = [];
        }

        this.__excludes[index] = false;
        this.drawSeries();

        for (i; i < ln; i++) {
            axesItems[i].drawAxis();
        }    
    },

    
    getLegendColor: function(index) {
        var me = this,
            colorLength = me.colorArrayStyle.length;

        if (me.style && me.style.fill) {
            return me.style.fill;
        } else {
            return me.colorArrayStyle[index % colorLength];
        }
    },

    highlightItem: function(item) {
        this.callParent(arguments);
        this.renderLabels();
    },

    unHighlightItem: function() {
        this.callParent(arguments);
        this.renderLabels();
    },

    cleanHighlights: function() {
        this.callParent(arguments);
        this.renderLabels();
    }
});


Ext.define('Ext.chart.series.Column', {

    

    alternateClassName: ['Ext.chart.ColumnSeries', 'Ext.chart.ColumnChart', 'Ext.chart.StackedColumnChart'],

    extend:  Ext.chart.series.Bar ,

    

    type: 'column',
    alias: 'series.column',

    column: true,

    
    boundColumn: false,

    

    
    xPadding: 10,

    
    yPadding: 0
});


Ext.define('Ext.chart.series.Gauge', {

    

    extend:  Ext.chart.series.Series ,

    

    type: "gauge",
    alias: 'series.gauge',

    rad: Math.PI / 180,

    
    highlightDuration: 150,

    
    angleField: false,

    
    needle: false,
    
    
    donut: false,

    
    showInLegend: false,

    
    style: {},
    
    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            store = chart.store,
            shadow = chart.shadow, i, l, cfg;
        Ext.apply(me, config, {
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 1,
                stroke: 'rgb(200, 200, 200)',
                translate: {
                    x: 1.2,
                    y: 2
                }
            },
            {
                "stroke-width": 4,
                "stroke-opacity": 1,
                stroke: 'rgb(150, 150, 150)',
                translate: {
                    x: 0.9,
                    y: 1.5
                }
            },
            {
                "stroke-width": 2,
                "stroke-opacity": 1,
                stroke: 'rgb(100, 100, 100)',
                translate: {
                    x: 0.6,
                    y: 1
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
        surface.customAttributes.segment = function(opt) {
            return me.getSegment(opt);
        };
    },
    
    
    initialize: function() {
        var me = this,
            store = me.chart.getChartStore(),
            data = store.data.items,
            label = me.label,
            ln = data.length;
          
        me.yField = [];
        if (label && label.field && ln > 0) {
            me.yField.push(data[0].get(label.field));
        }
    },

    
    getSegment: function(opt) {
        var me = this,
            rad = me.rad,
            cos = Math.cos,
            sin = Math.sin,
            abs = Math.abs,
            x = me.centerX,
            y = me.centerY,
            x1 = 0, x2 = 0, x3 = 0, x4 = 0,
            y1 = 0, y2 = 0, y3 = 0, y4 = 0,
            delta = 1e-2,
            r = opt.endRho - opt.startRho,
            startAngle = opt.startAngle,
            endAngle = opt.endAngle,
            midAngle = (startAngle + endAngle) / 2 * rad,
            margin = opt.margin || 0,
            flag = abs(endAngle - startAngle) > 180,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            singleSlice = false;

        x += margin * cos(midAngle);
        y += margin * sin(midAngle);

        x1 = x + opt.startRho * cos(a1);
        y1 = y + opt.startRho * sin(a1);

        x2 = x + opt.endRho * cos(a1);
        y2 = y + opt.endRho * sin(a1);

        x3 = x + opt.startRho * cos(a2);
        y3 = y + opt.startRho * sin(a2);

        x4 = x + opt.endRho * cos(a2);
        y4 = y + opt.endRho * sin(a2);

        if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) {
            singleSlice = true;
        }
        
        if (singleSlice) {
            return {
                path: [
                ["M", x1, y1],
                ["L", x2, y2],
                ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
                ["Z"]]
            };
        } else {
            return {
                path: [
                ["M", x1, y1],
                ["L", x2, y2],
                ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
                ["L", x3, y3],
                ["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
                ["Z"]]
            };
        }
    },

    
    calcMiddle: function(item) {
        var me = this,
            rad = me.rad,
            slice = item.slice,
            x = me.centerX,
            y = me.centerY,
            startAngle = slice.startAngle,
            endAngle = slice.endAngle,
            radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin),
            donut = +me.donut,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            midAngle = -(a1 + (a2 - a1) / 2),
            xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
            ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);

        item.middle = {
            x: xm,
            y: ym
        };
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            group = me.group,
            animate = me.chart.animate,
            axis = me.chart.axes.get(0),
            minimum = axis && axis.minimum || me.minimum || 0,
            maximum = axis && axis.maximum || me.maximum || 0,
            field = me.angleField || me.field || me.xField,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            rad = me.rad,
            donut = +me.donut,
            values = {},
            items = [],
            seriesStyle = me.seriesStyle,
            seriesLabelStyle = me.seriesLabelStyle,
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            cos = Math.cos,
            sin = Math.sin,
            rendererAttributes, centerX, centerY, slice, slices, sprite, value,
            item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path,
            p, spriteOptions, bbox, splitAngle, sliceA, sliceB;
        
        Ext.apply(seriesStyle, me.style || {});

        me.setBBox();
        bbox = me.bbox;

        
        if (me.colorSet) {
            colorArrayStyle = me.colorSet;
            colorArrayLength = colorArrayStyle.length;
        }
        
        
        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            return;
        }
        
        centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY = me.centerY = chartBBox.y + chartBBox.height;
        me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
        me.slices = slices = [];
        me.items = items = [];
        
        if (!me.value) {
            record = store.getAt(0);
            me.value = record.get(field);
        }
        
        value = me.value;
        if (me.needle) {
            sliceA = {
                series: me,
                value: value,
                startAngle: -180,
                endAngle: 0,
                rho: me.radius
            };
            splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
            slices.push(sliceA);
        } else {
            splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
            sliceA = {
                series: me,
                value: value,
                startAngle: -180,
                endAngle: splitAngle,
                rho: me.radius
            };
            sliceB = {
                series: me,
                value: me.maximum - value,
                startAngle: splitAngle,
                endAngle: 0,
                rho: me.radius
            };
            slices.push(sliceA, sliceB);
        }
        
        
        for (i = 0, ln = slices.length; i < ln; i++) {
            slice = slices[i];
            sprite = group.getAt(i);
            
            rendererAttributes = Ext.apply({
                segment: {
                    startAngle: slice.startAngle,
                    endAngle: slice.endAngle,
                    margin: 0,
                    rho: slice.rho,
                    startRho: slice.rho * +donut / 100,
                    endRho: slice.rho
                } 
            }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));

            item = Ext.apply({},
            rendererAttributes.segment, {
                slice: slice,
                series: me,
                storeItem: record,
                index: i
            });
            items[i] = item;
            
            if (!sprite) {
                spriteOptions = Ext.apply({
                    type: "path",
                    group: group
                }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
                sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
            }
            slice.sprite = slice.sprite || [];
            item.sprite = sprite;
            slice.sprite.push(sprite);
            if (animate) {
                rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store);
                sprite._to = rendererAttributes;
                me.onAnimate(sprite, {
                    to: rendererAttributes
                });
            } else {
                rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, {
                    hidden: false
                }), i, store);
                sprite.setAttributes(rendererAttributes, true);
            }
        }
        
        if (me.needle) {
            splitAngle = splitAngle * Math.PI / 180;
            
            if (!me.needleSprite) {
                me.needleSprite = me.chart.surface.add({
                    type: 'path',
                    path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                           'L', centerX + me.radius * cos(splitAngle),
                                centerY + -Math.abs(me.radius * sin(splitAngle))],
                    'stroke-width': 4,
                    'stroke': '#222'
                });
            } else {
                if (animate) {
                    me.onAnimate(me.needleSprite, {
                        to: {
                        path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                    centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                               'L', centerX + me.radius * cos(splitAngle),
                                    centerY + -Math.abs(me.radius * sin(splitAngle))]
                        }
                    });
                } else {
                    me.needleSprite.setAttributes({
                        type: 'path',
                        path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                    centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                               'L', centerX + me.radius * cos(splitAngle),
                                    centerY + -Math.abs(me.radius * sin(splitAngle))]
                    });
                }
            }
            me.needleSprite.setAttributes({
                hidden: false    
            }, true);
        }
        
        delete me.value;
    },
    
    
    setValue: function (value) {
        this.value = value;
        this.drawSeries();
    },

    
    onCreateLabel: function(storeItem, item, i, display) {},

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {},

    
    onPlaceCallout: function() {},

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item, i) {
        var me = this,
            cx = me.centerX,
            cy = me.centerY,
            abs = Math.abs,
            dx = abs(x - cx),
            dy = abs(y - cy),
            startAngle = item.startAngle,
            endAngle = item.endAngle,
            rho = Math.sqrt(dx * dx + dy * dy),
            angle = Math.atan2(y - cy, x - cx) / me.rad;

        
        return (i === 0) && (angle >= startAngle && angle < endAngle && 
                             rho >= item.startRho && rho <= item.endRho);
    },
    
    
    getLegendColor: function(index) {
        var colors = this.colorSet || this.colorArrayStyle;
        return colors[index % colors.length];
    }
});



Ext.define('Ext.chart.series.Line', {

    

    extend:  Ext.chart.series.Cartesian ,

    alternateClassName: ['Ext.chart.LineSeries', 'Ext.chart.LineChart'],

                                                                                         

    

    type: 'line',

    alias: 'series.line',

    
    selectionTolerance: 20,

    
    showMarkers: true,

    
    markerConfig: {},

    
    style: {},

    
    smooth: false,

    
    defaultSmoothness: 3,

    
    fill: false,

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface,
            shadow = me.chart.shadow,
            i, l;
        config.highlightCfg = Ext.Object.merge({ 'stroke-width': 3 }, config.highlightCfg);
        Ext.apply(me, config, {
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 0.05,
                stroke: 'rgb(0, 0, 0)',
                translate: {
                    x: 1,
                    y: 1
                }
            }, {
                "stroke-width": 4,
                "stroke-opacity": 0.1,
                stroke: 'rgb(0, 0, 0)',
                translate: {
                    x: 1,
                    y: 1
                }
            }, {
                "stroke-width": 2,
                "stroke-opacity": 0.15,
                stroke: 'rgb(0, 0, 0)',
                translate: {
                    x: 1,
                    y: 1
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (me.showMarkers) {
            me.markerGroup = surface.getGroup(me.seriesId + '-markers');
        }
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
    },

    
    shrink: function(xValues, yValues, size) {
        
        var len = xValues.length,
            ratio = Math.floor(len / size),
            i = 1,
            xSum = 0,
            ySum = 0,
            xRes = [+xValues[0]],
            yRes = [+yValues[0]];

        for (; i < len; ++i) {
            xSum += +xValues[i] || 0;
            ySum += +yValues[i] || 0;
            if (i % ratio == 0) {
                xRes.push(xSum/ratio);
                yRes.push(ySum/ratio);
                xSum = 0;
                ySum = 0;
            }
        }
        return {
            x: xRes,
            y: yRes
        };
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            chartAxes = chart.axes,
            store = chart.getChartStore(),
            data = store.data.items,
            record,
            storeCount = store.getCount(),
            surface = me.chart.surface,
            bbox = {},
            group = me.group,
            showMarkers = me.showMarkers,
            markerGroup = me.markerGroup,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            smooth = me.smooth,
            lnsh = shadowGroups.length,
            dummyPath = ["M"],
            path = ["M"],
            renderPath = ["M"],
            smoothPath = ["M"],
            markerIndex = chart.markerIndex,
            axes = [].concat(me.axis),
            shadowBarAttr,
            xValues = [],
            xValueMap = {},
            yValues = [],
            yValueMap = {},
            onbreak = false,
            storeIndices = [],
            markerStyle = Ext.apply({}, me.markerStyle),
            seriesStyle = me.seriesStyle,
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            isNumber = Ext.isNumber,
            seriesIdx = me.seriesIdx, 
            boundAxes = me.getAxesForXAndYFields(),
            boundXAxis = boundAxes.xAxis,
            boundYAxis = boundAxes.yAxis,
            xAxisType = boundXAxis ? chartAxes.get(boundXAxis).type : '',
            yAxisType = boundYAxis ? chartAxes.get(boundYAxis).type : '',
            shadows, shadow, shindex, fromPath, fill, fillPath, rendererAttributes,
            x, y, prevX, prevY, firstX, firstY, markerCount, i, j, ln, axis, ends, marker, markerAux, item, xValue,
            yValue, coords, xScale, yScale, minX, maxX, minY, maxY, line, animation, endMarkerStyle,
            endLineStyle, type, count, opacity, lineOpacity, fillOpacity, fillDefaultValue;

        if (me.fireEvent('beforedraw', me) === false) {
            return;
        }

        
        if (!storeCount || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            if (me.line) {
                me.line.hide(true);
                if (me.line.shadows) {
                    shadows = me.line.shadows;
                    for (j = 0, lnsh = shadows.length; j < lnsh; j++) {
                        shadow = shadows[j];
                        shadow.hide(true);
                    }
                }
                if (me.fillPath) {
                    me.fillPath.hide(true);
                }
            }
            me.line = null;
            me.fillPath = null;
            return;
        }

        
        endMarkerStyle = Ext.apply(markerStyle || {}, me.markerConfig, {
            fill: me.seriesStyle.fill || colorArrayStyle[me.themeIdx % colorArrayStyle.length]
        });
        type = endMarkerStyle.type;
        delete endMarkerStyle.type;
        endLineStyle = seriesStyle;
        
        
        if (!endLineStyle['stroke-width']) {
            endLineStyle['stroke-width'] = 0.5;
        }
        
        
        opacity = 'opacity' in endLineStyle ? endLineStyle.opacity : 1;
        fillDefaultValue = 'opacity' in endLineStyle ? endLineStyle.opacity : 0.3;
        lineOpacity = 'lineOpacity' in endLineStyle ? endLineStyle.lineOpacity : opacity;
        fillOpacity = 'fillOpacity' in endLineStyle ? endLineStyle.fillOpacity : fillDefaultValue;

        
        
        if (markerIndex && markerGroup && markerGroup.getCount()) {
            for (i = 0; i < markerIndex; i++) {
                marker = markerGroup.getAt(i);
                markerGroup.remove(marker);
                markerGroup.add(marker);
                markerAux = markerGroup.getAt(markerGroup.getCount() - 2);
                marker.setAttributes({
                    x: 0,
                    y: 0,
                    translate: {
                        x: markerAux.attr.translation.x,
                        y: markerAux.attr.translation.y
                    }
                }, true);
            }
        }

        me.unHighlightItem();
        me.cleanHighlights();

        me.setBBox();
        bbox = me.bbox;
        me.clipRect = [bbox.x, bbox.y, bbox.width, bbox.height];

        if (axis = chartAxes.get(boundXAxis)) {
            ends = axis.applyData();
            minX = ends.from;
            maxX = ends.to;
        }

        if (axis = chartAxes.get(boundYAxis)) {
            ends = axis.applyData();
            minY = ends.from;
            maxY = ends.to;
        }

        
        if (me.xField && !Ext.isNumber(minX)) {
            axis = me.getMinMaxXValues();
            minX = axis[0];
            maxX = axis[1];
        }

        if (me.yField && !Ext.isNumber(minY)) {
            axis = me.getMinMaxYValues();
            minY = axis[0];
            maxY = axis[1];
        }
        
        if (isNaN(minX)) {
            minX = 0;
            xScale = bbox.width / ((storeCount - 1) || 1);
        }
        else {
            xScale = bbox.width / ((maxX - minX) || (storeCount -1) || 1);
        }

        if (isNaN(minY)) {
            minY = 0;
            yScale = bbox.height / ((storeCount - 1) || 1);
        }
        else {
            yScale = bbox.height / ((maxY - minY) || (storeCount - 1) || 1);
        }
        
        for (i = 0, ln = data.length; i < ln; i++) {
            record = data[i];
            xValue = record.get(me.xField);
            if (xAxisType == 'Time' && typeof xValue == "string") {
                xValue = Date.parse(xValue);
            }
            
            if (typeof xValue == 'string' || typeof xValue == 'object' && !Ext.isDate(xValue)
                
                || boundXAxis && chartAxes.get(boundXAxis) && chartAxes.get(boundXAxis).type == 'Category') {
                    if (xValue in xValueMap) {
                        xValue = xValueMap[xValue];
                    } else {
                        xValue = xValueMap[xValue] = i;
                    }
            }

            
            yValue = record.get(me.yField);
            if (yAxisType == 'Time' && typeof yValue == "string") {
                yValue = Date.parse(yValue);
            }
            
            if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue)) {
                continue;
            }
            
            if (typeof yValue == 'string' || typeof yValue == 'object' && !Ext.isDate(yValue)
                
                || boundYAxis && chartAxes.get(boundYAxis) && chartAxes.get(boundYAxis).type == 'Category') {
                yValue = i;
            }
            storeIndices.push(i);
            xValues.push(xValue);
            yValues.push(yValue);
        }

        ln = xValues.length;
        if (ln > bbox.width) {
            coords = me.shrink(xValues, yValues, bbox.width);
            xValues = coords.x;
            yValues = coords.y;
        }

        me.items = [];

        count = 0;
        ln = xValues.length;
        for (i = 0; i < ln; i++) {
            xValue = xValues[i];
            yValue = yValues[i];
            if (yValue === false) {
                if (path.length == 1) {
                    path = [];
                }
                onbreak = true;
                me.items.push(false);
                continue;
            } else {
                x = (bbox.x + (xValue - minX) * xScale).toFixed(2);
                y = ((bbox.y + bbox.height) - (yValue - minY) * yScale).toFixed(2);
                if (onbreak) {
                    onbreak = false;
                    path.push('M');
                }
                path = path.concat([x, y]);
            }
            if ((typeof firstY == 'undefined') && (typeof y != 'undefined')) {
                firstY = y;
                firstX = x;
            }
            
            if (!me.line || chart.resizing) {
                dummyPath = dummyPath.concat([x, bbox.y + bbox.height / 2]);
            }

            
            if (chart.animate && chart.resizing && me.line) {
                me.line.setAttributes({
                    path: dummyPath,
                    opacity: lineOpacity
                }, true);
                if (me.fillPath) {
                    me.fillPath.setAttributes({
                        path: dummyPath,
                        opacity: fillOpacity
                    }, true);
                }
                if (me.line.shadows) {
                    shadows = me.line.shadows;
                    for (j = 0, lnsh = shadows.length; j < lnsh; j++) {
                        shadow = shadows[j];
                        shadow.setAttributes({
                            path: dummyPath
                        }, true);
                    }
                }
            }
            if (showMarkers) {
                marker = markerGroup.getAt(count++);
                if (!marker) {
                    marker = Ext.chart.Shape[type](surface, Ext.apply({
                        group: [group, markerGroup],
                        x: 0, y: 0,
                        translate: {
                            x: +(prevX || x),
                            y: prevY || (bbox.y + bbox.height / 2)
                        },
                        value: '"' + xValue + ', ' + yValue + '"',
                        zIndex: 4000
                    }, endMarkerStyle));
                    marker._to = {
                        translate: {
                            x: +x,
                            y: +y
                        }
                    };
                } else {
                    marker.setAttributes({
                        value: '"' + xValue + ', ' + yValue + '"',
                        x: 0, y: 0,
                        hidden: false
                    }, true);
                    marker._to = {
                        translate: {
                            x: +x, 
                            y: +y
                        }
                    };
                }
            }
            me.items.push({
                series: me,
                value: [xValue, yValue],
                point: [x, y],
                sprite: marker,
                storeItem: store.getAt(storeIndices[i])
            });
            prevX = x;
            prevY = y;
        }

        if (path.length <= 1) {
            
            return;
        }

        if (me.smooth) {
            smoothPath = Ext.draw.Draw.smooth(path, isNumber(smooth) ? smooth : me.defaultSmoothness);
        }

        renderPath = smooth ? smoothPath : path;

        
        if (chart.markerIndex && me.previousPath) {
            fromPath = me.previousPath;
            if (!smooth) {
                Ext.Array.erase(fromPath, 1, 2);
            }
        } else {
            fromPath = path;
        }

        
        if (!me.line) {
            me.line = surface.add(Ext.apply({
                type: 'path',
                group: group,
                path: dummyPath,
                stroke: endLineStyle.stroke || endLineStyle.fill
            }, endLineStyle || {}));
            me

            
            me.line.setAttributes({
                opacity: lineOpacity
            }, true);

            if (enableShadows) {
                me.line.setAttributes(Ext.apply({}, me.shadowOptions), true);
            }

            
            me.line.setAttributes({
                fill: 'none',
                zIndex: 3000
            });
            if (!endLineStyle.stroke && colorArrayLength) {
                me.line.setAttributes({
                    stroke: colorArrayStyle[me.themeIdx % colorArrayLength]
                }, true);
            }
            if (enableShadows) {
                
                shadows = me.line.shadows = [];
                for (shindex = 0; shindex < lnsh; shindex++) {
                    shadowBarAttr = shadowAttributes[shindex];
                    shadowBarAttr = Ext.apply({}, shadowBarAttr, { path: dummyPath });
                    shadow = surface.add(Ext.apply({}, {
                        type: 'path',
                        group: shadowGroups[shindex]
                    }, shadowBarAttr));
                    shadows.push(shadow);
                }
            }
        }
        if (me.fill) {
            fillPath = renderPath.concat([
                ["L", x, bbox.y + bbox.height],
                ["L", firstX, bbox.y + bbox.height],
                ["L", firstX, firstY]
            ]);
            if (!me.fillPath) {
                me.fillPath = surface.add({
                    group: group,
                    type: 'path',
                    fill: endLineStyle.fill || colorArrayStyle[me.themeIdx % colorArrayLength],
                    path: dummyPath
                });
            }
        }
        markerCount = showMarkers && markerGroup.getCount();
        if (chart.animate) {
            fill = me.fill;
            line = me.line;
            
            rendererAttributes = me.renderer(line, false, { path: renderPath }, i, store);
            Ext.apply(rendererAttributes, endLineStyle || {}, {
                stroke: endLineStyle.stroke || endLineStyle.fill
            });
            
            delete rendererAttributes.fill;
            line.show(true);
            if (chart.markerIndex && me.previousPath) {
                me.animation = animation = me.onAnimate(line, {
                    to: rendererAttributes,
                    from: {
                        path: fromPath
                    }
                });
            } else {
                me.animation = animation = me.onAnimate(line, {
                    to: rendererAttributes
                });
            }
            
            if (enableShadows) {
                shadows = line.shadows;
                for(j = 0; j < lnsh; j++) {
                    shadows[j].show(true);
                    if (chart.markerIndex && me.previousPath) {
                        me.onAnimate(shadows[j], {
                            to: { path: renderPath },
                            from: { path: fromPath }
                        });
                    } else {
                        me.onAnimate(shadows[j], {
                            to: { path: renderPath }
                        });
                    }
                }
            }
            
            if (fill) {
                me.fillPath.show(true);
                me.onAnimate(me.fillPath, {
                    to: Ext.apply({}, {
                        path: fillPath,
                        fill: endLineStyle.fill || colorArrayStyle[me.themeIdx % colorArrayLength],
                        'stroke-width': 0,
                        opacity: fillOpacity
                    }, endLineStyle || {})
                });
            }
            
            if (showMarkers) {
                count = 0;
                for(i = 0; i < ln; i++) {
                    if (me.items[i]) {
                        item = markerGroup.getAt(count++);
                        if (item) {
                            rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
                            me.onAnimate(item, {
                                to: Ext.applyIf(rendererAttributes, endMarkerStyle || {})
                            });
                            item.show(true);
                        }
                    }
                }
                for(; count < markerCount; count++) {
                    item = markerGroup.getAt(count);
                    item.hide(true);
                }




            }
        } else {
            rendererAttributes = me.renderer(me.line, false, { path: renderPath, hidden: false }, i, store);
            Ext.apply(rendererAttributes, endLineStyle || {}, {
                stroke: endLineStyle.stroke || endLineStyle.fill
            });
            
            delete rendererAttributes.fill;
            me.line.setAttributes(rendererAttributes, true);
            me.line.setAttributes({
                opacity: lineOpacity
            }, true);
            
            if (enableShadows) {
                shadows = me.line.shadows;
                for(j = 0; j < lnsh; j++) {
                    shadows[j].setAttributes({
                        path: renderPath,
                        hidden: false
                    }, true);
                }
            }
            if (me.fill) {
                me.fillPath.setAttributes({
                    path: fillPath,
                    hidden: false,
                    opacity: fillOpacity
                }, true);
            }
            if (showMarkers) {
                count = 0;
                for(i = 0; i < ln; i++) {
                    if (me.items[i]) {
                        item = markerGroup.getAt(count++);
                        if (item) {
                            rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
                            item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true);
                            if (!item.attr.hidden) {
                                item.show(true);
                            }
                        }
                    }
                }
                for(; count < markerCount; count++) {
                    item = markerGroup.getAt(count);
                    item.hide(true);
                }
            }
        }

        if (chart.markerIndex) {
            if (me.smooth) {
                Ext.Array.erase(path, 1, 2);
            } else {
                Ext.Array.splice(path, 1, 0, path[1], path[2]);
            }
            me.previousPath = path;
        }
        me.renderLabels();
        me.renderCallouts();

        me.fireEvent('draw', me);
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            bbox = me.bbox,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {});

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': Number(item.point[0]),
            'y': bbox.y + bbox.height / 2
        }, endLabelStyle || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            bbox = me.bbox,
            x = Number(item.point[0]),
            y = Number(item.point[1]),
            radius = item.sprite.attr.radius,
            labelBox, markerBox, width, height, xOffset, yOffset;

        label.setAttributes({
            text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index),
            hidden: true
        }, true);

        
        markerBox = item.sprite.getBBox();
        markerBox.width = markerBox.width || (radius * 2);
        markerBox.height = markerBox.height || (radius * 2);

        labelBox = label.getBBox();
        width = labelBox.width/2;
        height = labelBox.height/2;

        if (display == 'rotate') {
            
            xOffset = markerBox.width/2 + width + height/2;
            if (x + xOffset + width > bbox.x + bbox.width) {
                x -= xOffset;
            } else {
                x += xOffset;
            }
            label.setAttributes({
                'rotation': {
                    x: x,
                    y: y,
                    degrees: -45
                }
            }, true);
        } else if (display == 'under' || display == 'over') {
            label.setAttributes({
                'rotation': {
                    degrees: 0
                }
            }, true);

            
            if (x < bbox.x + width) {
                x = bbox.x + width;
            } else if (x + width > bbox.x + bbox.width) {
                x = bbox.x + bbox.width - width;
            }

            yOffset = markerBox.height/2 + height;
            y = y + (display == 'over' ? -yOffset : yOffset);
            if (y < bbox.y + height) {
                y += 2 * yOffset;
            } else if (y + height > bbox.y + bbox.height) {
                y -= 2 * yOffset;
            }
        }

        if (me.chart.animate && !me.chart.resizing) {
            label.show(true);
            me.onAnimate(label, {
                to: {
                    x: x,
                    y: y
                }
            });
        } else {
            label.setAttributes({
                x: x,
                y: y
            }, true);
            if (resizing && me.animation) {
                me.animation.on('afteranimate', function() {
                    label.show(true);
                });
            } else {
                label.show(true);
            }
        }
    },

    
    highlightItem: function() {
        var me = this,
            line = me.line;
                
        me.callParent(arguments);
        if (line && !me.highlighted) {
            if (!('__strokeWidth' in line)) {
                line.__strokeWidth = parseFloat(line.attr['stroke-width']) || 0;
            }
            if (line.__anim) {
                line.__anim.paused = true;
            }
            
            line.__anim = new Ext.fx.Anim({
                target: line,
                to: {
                    'stroke-width': line.__strokeWidth + 3
                }
            });
            me.highlighted = true;
        }
    },

    
    unHighlightItem: function() {
        var me = this,
            line = me.line,
            width;
            
        me.callParent(arguments);
        if (line && me.highlighted) {
            width = line.__strokeWidth || parseFloat(line.attr['stroke-width']) || 0;
            line.__anim = new Ext.fx.Anim({
                target: line,
                to: {
                    'stroke-width': width
                }
            });
            me.highlighted = false;
        }
    },

    
    onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) {
        if (!display) {
            return;
        }

        var me = this,
            chart = me.chart,
            surface = chart.surface,
            resizing = chart.resizing,
            config = me.callouts,
            items = me.items,
            prev = i == 0? false : items[i -1].point,
            next = (i == items.length -1)? false : items[i +1].point,
            cur = [+item.point[0], +item.point[1]],
            dir, norm, normal, a, aprev, anext,
            offsetFromViz = config.offsetFromViz || 30,
            offsetToSide = config.offsetToSide || 10,
            offsetBox = config.offsetBox || 3,
            boxx, boxy, boxw, boxh,
            p, clipRect = me.clipRect,
            bbox = {
                width: config.styles.width || 10,
                height: config.styles.height || 10
            },
            x, y;

        
        if (!prev) {
            prev = cur;
        }
        if (!next) {
            next = cur;
        }
        a = (next[1] - prev[1]) / (next[0] - prev[0]);
        aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]);
        anext = (next[1] - cur[1]) / (next[0] - cur[0]);

        norm = Math.sqrt(1 + a * a);
        dir = [1 / norm, a / norm];
        normal = [-dir[1], dir[0]];

        
        if (aprev > 0 && anext < 0 && normal[1] < 0
            || aprev < 0 && anext > 0 && normal[1] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0
                   || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        }
        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        
        
        if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
            normal[0] *= -1;
        }
        if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        if (chart.animate) {
            
            me.onAnimate(callout.lines, {
                to: {
                    path: ["M", cur[0], cur[1], "L", x, y, "Z"]
                }
            });
            
            if (callout.panel) {
                callout.panel.setPosition(boxx, boxy, true);
            }
        }
        else {
            
            callout.lines.setAttributes({
                path: ["M", cur[0], cur[1], "L", x, y, "Z"]
            }, true);
            
            if (callout.panel) {
                callout.panel.setPosition(boxx, boxy);
            }
        }
        for (p in callout) {
            callout[p].show(true);
        }
    },

    isItemInPoint: function(x, y, item, i) {
        var me = this,
            items = me.items,
            tolerance = me.selectionTolerance,
            result = null,
            prevItem,
            nextItem,
            prevPoint,
            nextPoint,
            ln,
            x1,
            y1,
            x2,
            y2,
            xIntersect,
            yIntersect,
            dist1, dist2, dist, midx, midy,
            sqrt = Math.sqrt, abs = Math.abs;

        nextItem = items[i];
        prevItem = i && items[i - 1];

        if (i >= ln) {
            prevItem = items[ln - 1];
        }
        prevPoint = prevItem && prevItem.point;
        nextPoint = nextItem && nextItem.point;
        x1 = prevItem ? prevPoint[0] : nextPoint[0] - tolerance;
        y1 = prevItem ? prevPoint[1] : nextPoint[1];
        x2 = nextItem ? nextPoint[0] : prevPoint[0] + tolerance;
        y2 = nextItem ? nextPoint[1] : prevPoint[1];
        dist1 = sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
        dist2 = sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
        dist = Math.min(dist1, dist2);

        if (dist <= tolerance) {
            return dist == dist1? prevItem : nextItem;
        }
        return false;
    },

    
    toggleAll: function(show) {
        var me = this,
            i, ln, shadow, shadows;
        if (!show) {
            Ext.chart.series.Cartesian.prototype.hideAll.call(me);
        }
        else {
            Ext.chart.series.Cartesian.prototype.showAll.call(me);
        }
        if (me.line) {
            me.line.setAttributes({
                hidden: !show
            }, true);
            
            if (me.line.shadows) {
                for (i = 0, shadows = me.line.shadows, ln = shadows.length; i < ln; i++) {
                    shadow = shadows[i];
                    shadow.setAttributes({
                        hidden: !show
                    }, true);
                }
            }
        }
        if (me.fillPath) {
            me.fillPath.setAttributes({
                hidden: !show
            }, true);
        }
    },

    
    hideAll: function() {
        this.toggleAll(false);
    },

    
    showAll: function() {
        this.toggleAll(true);
    }
});


Ext.define('Ext.chart.series.Pie', {

    

    alternateClassName: ['Ext.chart.PieSeries', 'Ext.chart.PieChart'],

    extend:  Ext.chart.series.Series ,

    

    type: "pie",

    alias: 'series.pie',

    accuracy: 100000,

    rad: Math.PI * 2 / 100000,

    
    highlightDuration: 150,

    
    angleField: false,

    

    

    
    lengthField: false,

    
    donut: false,

    
    showInLegend: false,

    

    
    style: {},

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            store = chart.store,
            shadow = chart.shadow, i, l, cfg;
        config.highlightCfg = Ext.merge({
            segment: {
                margin: 20
            }
        }, config.highlightCfg);
        Ext.apply(me, config, {
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 1,
                stroke: 'rgb(200, 200, 200)',
                translate: {
                    x: 1.2,
                    y: 2
                }
            },
            {
                "stroke-width": 4,
                "stroke-opacity": 1,
                stroke: 'rgb(150, 150, 150)',
                translate: {
                    x: 0.9,
                    y: 1.5
                }
            },
            {
                "stroke-width": 2,
                "stroke-opacity": 1,
                stroke: 'rgb(100, 100, 100)',
                translate: {
                    x: 0.6,
                    y: 1
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
        surface.customAttributes.segment = function(opt) {
            
            
            
            var ans = me.getSegment(opt);
            if (!ans.path || ans.path.length === 0) {
                ans.path = ['M', 0, 0];
            }
            return ans;
        };
        me.__excludes = me.__excludes || [];
    },
    
    onRedraw: function(){
        this.initialize();    
    },

    
    initialize: function() {
        var me = this,
            store = me.chart.getChartStore(),
            data = store.data.items,
            i, ln, rec;
        
        me.yField = [];
        if (me.label.field) {
            for (i = 0, ln = data.length; i < ln; i++) {
                rec = data[i];
                me.yField.push(rec.get(me.label.field));
            }
        }
    },

    
    getSegment: function(opt) {
        var me = this,
            rad = me.rad,
            cos = Math.cos,
            sin = Math.sin,
            x = me.centerX,
            y = me.centerY,
            x1 = 0, x2 = 0, x3 = 0, x4 = 0,
            y1 = 0, y2 = 0, y3 = 0, y4 = 0,
            x5 = 0, y5 = 0, x6 = 0, y6 = 0,
            delta = 1e-2,
            startAngle = opt.startAngle,
            endAngle = opt.endAngle,
            midAngle = (startAngle + endAngle) / 2 * rad,
            margin = opt.margin || 0,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            c1 = cos(a1), s1 = sin(a1),
            c2 = cos(a2), s2 = sin(a2),
            cm = cos(midAngle), sm = sin(midAngle),
            flag = 0, hsqr2 = 0.7071067811865476; 

        if (a2 - a1 < delta) {
            return {path: ""};
        }

        if (margin !== 0) {
            x += margin * cm;
            y += margin * sm;
        }

        x2 = x + opt.endRho * c1;
        y2 = y + opt.endRho * s1;

        x4 = x + opt.endRho * c2;
        y4 = y + opt.endRho * s2;

        x6 = x + opt.endRho * cm;
        y6 = y + opt.endRho * sm;

        if (opt.startRho !== 0) {
            x1 = x + opt.startRho * c1;
            y1 = y + opt.startRho * s1;
    
            x3 = x + opt.startRho * c2;
            y3 = y + opt.startRho * s2;
    
            x5 = x + opt.startRho * cm;
            y5 = y + opt.startRho * sm;

            return {
                path: [
                    ["M", x2, y2],
                    ["A", opt.endRho, opt.endRho, 0, 0, 1, x6, y6], ["L", x6, y6],
                    ["A", opt.endRho, opt.endRho, 0, flag, 1, x4, y4], ["L", x4, y4],
                    ["L", x3, y3],
                    ["A", opt.startRho, opt.startRho, 0, flag, 0, x5, y5], ["L", x5, y5],
                    ["A", opt.startRho, opt.startRho, 0, 0, 0, x1, y1], ["L", x1, y1],
                    ["Z"]
                ]
            };
        } else {
            return {
                path: [
                    ["M", x, y],
                    ["L", x2, y2],
                    ["A", opt.endRho, opt.endRho, 0, 0, 1, x6, y6], ["L", x6, y6],
                    ["A", opt.endRho, opt.endRho, 0, flag, 1, x4, y4], ["L", x4, y4],
                    ["L", x, y],
                    ["Z"]
                ]
            };
        }
    },

    
    calcMiddle: function(item) {
        var me = this,
            rad = me.rad,
            slice = item.slice,
            x = me.centerX,
            y = me.centerY,
            startAngle = slice.startAngle,
            endAngle = slice.endAngle,
            donut = +me.donut,
            midAngle = -(startAngle + endAngle) * rad / 2,
            r = (item.endRho + item.startRho) / 2,
            xm = x + r * Math.cos(midAngle),
            ym = y - r * Math.sin(midAngle);

        item.middle = {
            x: xm,
            y: ym
        };
    },

    
    drawSeries: function() {
        var me = this,
            store = me.chart.getChartStore(),
            data = store.data.items,
            record,
            group = me.group,
            animate = me.chart.animate,
            field = me.angleField || me.field || me.xField,
            lenField = [].concat(me.lengthField),
            totalLenField = 0,
            chart = me.chart,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            lnsh = shadowGroups.length,
            layers = lenField.length,
            rhoAcum = 0,
            donut = +me.donut,
            layerTotals = [],
            items = [],
            totalField = 0,
            maxLenField = 0,
            angle = 0,
            seriesStyle = me.seriesStyle,
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            rendererAttributes,
            shadowAttr,
            shadows,
            shadow,
            shindex,
            centerX,
            centerY,
            deltaRho,
            first = 0,
            slice,
            slices,
            sprite,
            value,
            item,
            lenValue,
            ln,
            i,
            j,
            endAngle,
            path,
            p,
            spriteOptions, bbox;

        Ext.apply(seriesStyle, me.style || {});

        me.setBBox();
        bbox = me.bbox;

        
        if (me.colorSet) {
            colorArrayStyle = me.colorSet;
            colorArrayLength = colorArrayStyle.length;
        }

        
        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            return;
        }

        me.unHighlightItem();
        me.cleanHighlights();

        centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY = me.centerY = chartBBox.y + (chartBBox.height / 2);
        me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
        me.slices = slices = [];
        me.items = items = [];

        for (i = 0, ln = data.length; i < ln; i++) {
            record = data[i];
            if (this.__excludes && this.__excludes[i]) {
                
                continue;
            }
            totalField += +record.get(field);
            if (lenField[0]) {
                for (j = 0, totalLenField = 0; j < layers; j++) {
                    totalLenField += +record.get(lenField[j]);
                }
                layerTotals[i] = totalLenField;
                maxLenField = Math.max(maxLenField, totalLenField);
            }
        }

        totalField = totalField || 1;
        for (i = 0, ln = data.length; i < ln; i++) {
            record = data[i];
            if (this.__excludes && this.__excludes[i]) {
                value = 0;
            } else {
                value = record.get(field);
                if (first == 0) {
                    first = 1;
                }
            }

            
            if (first == 1) {
                first = 2;
                me.firstAngle = angle = me.accuracy * value / totalField / 2;
                for (j = 0; j < i; j++) {
                    slices[j].startAngle = slices[j].endAngle = me.firstAngle;
                }
            }

            endAngle = angle - me.accuracy * value / totalField;
            slice = {
                series: me,
                value: value,
                startAngle: angle,
                endAngle: endAngle,
                storeItem: record
            };
            if (lenField[0]) {
                lenValue = +layerTotals[i];
                
                slice.rho = Math.floor(me.radius / maxLenField * lenValue);
            } else {
                slice.rho = me.radius;
            }
            slices[i] = slice;
            
            (function () {
                angle = endAngle;
            })();
        }

        
        if (enableShadows) {
            for (i = 0, ln = slices.length; i < ln; i++) {
                slice = slices[i];
                slice.shadowAttrs = [];
                for (j = 0, rhoAcum = 0, shadows = []; j < layers; j++) {
                    sprite = group.getAt(i * layers + j);
                    deltaRho = lenField[j] ? store.getAt(i).get(lenField[j]) / layerTotals[i] * slice.rho: slice.rho;
                    
                    rendererAttributes = {
                        segment: {
                            startAngle: slice.startAngle,
                            endAngle: slice.endAngle,
                            margin: 0,
                            rho: slice.rho,
                            startRho: rhoAcum + (deltaRho * donut / 100),
                            endRho: rhoAcum + deltaRho
                        },
                        hidden: !slice.value && (slice.startAngle % me.accuracy) == (slice.endAngle % me.accuracy)
                    };
                    
                    for (shindex = 0, shadows = []; shindex < lnsh; shindex++) {
                        shadowAttr = shadowAttributes[shindex];
                        shadow = shadowGroups[shindex].getAt(i);
                        if (!shadow) {
                            shadow = chart.surface.add(Ext.apply({}, {
                                type: 'path',
                                group: shadowGroups[shindex],
                                strokeLinejoin: "round"
                            }, rendererAttributes, shadowAttr));
                        }
                        shadowAttr = me.renderer(shadow, store.getAt(i), Ext.apply({}, rendererAttributes, shadowAttr), i, store);
                        if (animate) {
                            me.onAnimate(shadow, {
                                to: shadowAttr
                            });
                        } else {
                            shadow.setAttributes(shadowAttr, true);
                        }
                        shadows.push(shadow);
                    }
                    slice.shadowAttrs[j] = shadows;
                }
            }
        }
        
        for (i = 0, ln = slices.length; i < ln; i++) {
            slice = slices[i];
            for (j = 0, rhoAcum = 0; j < layers; j++) {
                sprite = group.getAt(i * layers + j);
                deltaRho = lenField[j] ? store.getAt(i).get(lenField[j]) / layerTotals[i] * slice.rho: slice.rho;
                
                rendererAttributes = Ext.apply({
                    segment: {
                        startAngle: slice.startAngle,
                        endAngle: slice.endAngle,
                        margin: 0,
                        rho: slice.rho,
                        startRho: rhoAcum + (deltaRho * donut / 100),
                        endRho: rhoAcum + deltaRho
                    },
                    hidden: (!slice.value && (slice.startAngle % me.accuracy) == (slice.endAngle % me.accuracy))
                }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {}));
                item = Ext.apply({},
                rendererAttributes.segment, {
                    slice: slice,
                    series: me,
                    storeItem: slice.storeItem,
                    index: i
                });
                me.calcMiddle(item);
                if (enableShadows) {
                    item.shadows = slice.shadowAttrs[j];
                }
                items[i] = item;
                
                if (!sprite) {
                    spriteOptions = Ext.apply({
                        type: "path",
                        group: group,
                        middle: item.middle
                    }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {}));
                    sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
                }
                slice.sprite = slice.sprite || [];
                item.sprite = sprite;
                slice.sprite.push(sprite);
                slice.point = [item.middle.x, item.middle.y];
                if (animate) {
                    rendererAttributes = me.renderer(sprite, store.getAt(i), rendererAttributes, i, store);
                    sprite._to = rendererAttributes;
                    sprite._animating = true;
                    me.onAnimate(sprite, {
                        to: rendererAttributes,
                        listeners: {
                            afteranimate: {
                                fn: function() {
                                    this._animating = false;
                                },
                                scope: sprite
                            }
                        }
                    });
                } else {
                    rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply(rendererAttributes, {
                        hidden: false
                    }), i, store);
                    sprite.setAttributes(rendererAttributes, true);
                }
                rhoAcum += deltaRho;
            }
        }

        
        ln = group.getCount();
        for (i = 0; i < ln; i++) {
            if (!slices[(i / layers) >> 0] && group.getAt(i)) {
                group.getAt(i).hide(true);
            }
        }
        if (enableShadows) {
            lnsh = shadowGroups.length;
            for (shindex = 0; shindex < ln; shindex++) {
                if (!slices[(shindex / layers) >> 0]) {
                    for (j = 0; j < lnsh; j++) {
                        if (shadowGroups[j].getAt(shindex)) {
                            shadowGroups[j].getAt(shindex).hide(true);
                        }
                    }
                }
            }
        }
        me.renderLabels();
        me.renderCallouts();
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            centerX = me.centerX,
            centerY = me.centerY,
            middle = item.middle,
            endLabelStyle = Ext.apply(me.seriesLabelStyle || {}, config || {});

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': middle.x,
            'y': middle.y
        }, endLabelStyle));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            centerX = me.centerX,
            centerY = me.centerY,
            middle = item.middle,
            opt = {
                x: middle.x,
                y: middle.y
            },
            x = middle.x - centerX,
            y = middle.y - centerY,
            from = {},
            rho = 1,
            theta = Math.atan2(y, x || 1),
            dg = theta * 180 / Math.PI,
            prevDg, labelBox, width, height;

        opt.hidden = false;

        if (this.__excludes && this.__excludes[i]) {
            opt.hidden = true;
        }

        function fixAngle(a) {
            if (a < 0) {
                a += 360;
            }
            return a % 360;
        }

        label.setAttributes({
            text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index)
        }, true);

        switch (display) {
        case 'outside':
            
            rho = Math.sqrt(x * x + y * y) * 2;

            
            label.setAttributes({rotation:{degrees: 0}}, true);
            labelBox = label.getBBox();
            width = labelBox.width/2 * Math.cos(theta) + 4;
            height = labelBox.height/2 * Math.sin(theta) + 4;

            rho += Math.sqrt(width*width + height*height);

            
            opt.x = rho * Math.cos(theta) + centerX;
            opt.y = rho * Math.sin(theta) + centerY;
            break;

        case 'rotate':
            dg = fixAngle(dg);
            dg = (dg > 90 && dg < 270) ? dg + 180: dg;

            prevDg = label.attr.rotation.degrees;
            if (prevDg != null && Math.abs(prevDg - dg) > 180 * 0.5) {
                if (dg > prevDg) {
                    dg -= 360;
                } else {
                    dg += 360;
                }
                dg = dg % 360;
            } else {
                dg = fixAngle(dg);
            }
            
            opt.rotate = {
                degrees: dg,
                x: opt.x,
                y: opt.y
            };
            break;

        default:
            break;
        }
        
        opt.translate = {
            x: 0, y: 0
        };
        if (animate && !resizing && (display != 'rotate' || prevDg != null)) {
            me.onAnimate(label, {
                to: opt
            });
        } else {
            label.setAttributes(opt, true);
        }
        label._from = from;
    },

    
    onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            centerX = me.centerX,
            centerY = me.centerY,
            middle = item.middle,
            opt = {
                x: middle.x,
                y: middle.y
            },
            x = middle.x - centerX,
            y = middle.y - centerY,
            rho = 1,
            rhoCenter,
            theta = Math.atan2(y, x || 1),
            bbox = (callout && callout.label ? callout.label.getBBox() : {width:0,height:0}),
            offsetFromViz = 20,
            offsetToSide = 10,
            offsetBox = 10,
            p;

        if (!bbox.width || !bbox.height) {
            return;
        }

        
        rho = item.endRho + offsetFromViz;
        rhoCenter = (item.endRho + item.startRho) / 2 + (item.endRho - item.startRho) / 3;
        
        opt.x = rho * Math.cos(theta) + centerX;
        opt.y = rho * Math.sin(theta) + centerY;

        x = rhoCenter * Math.cos(theta);
        y = rhoCenter * Math.sin(theta);

        if (chart.animate) {
            
            me.onAnimate(callout.lines, {
                to: {
                    path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"]
                }
            });
            
            me.onAnimate(callout.box, {
                to: {
                    x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)),
                    y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)),
                    width: bbox.width + 2 * offsetBox,
                    height: bbox.height + 2 * offsetBox
                }
            });
            
            me.onAnimate(callout.label, {
                to: {
                    x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)),
                    y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4)
                }
            });
        } else {
            
            callout.lines.setAttributes({
                path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"]
            },
            true);
            
            callout.box.setAttributes({
                x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)),
                y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)),
                width: bbox.width + 2 * offsetBox,
                height: bbox.height + 2 * offsetBox
            },
            true);
            
            callout.label.setAttributes({
                x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)),
                y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4)
            },
            true);
        }
        for (p in callout) {
            callout[p].show(true);
        }
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item, i) {
        var me = this,
            cx = me.centerX,
            cy = me.centerY,
            abs = Math.abs,
            dx = abs(x - cx),
            dy = abs(y - cy),
            startAngle = item.startAngle,
            endAngle = item.endAngle,
            rho = Math.sqrt(dx * dx + dy * dy),
            angle = Math.atan2(y - cy, x - cx) / me.rad;

        
        if (angle > me.firstAngle) {
            angle -= me.accuracy;
        }
        return (angle <= startAngle && angle > endAngle
                && rho >= item.startRho && rho <= item.endRho);
    },

    
    hideAll: function(index) {
        var i, l, shadow, shadows, sh, lsh, sprite;
        index = (isNaN(this._index) ? index : this._index) || 0;
        this.__excludes = this.__excludes || [];
        this.__excludes[index] = true;
        sprite = this.slices[index].sprite;
        for (sh = 0, lsh = sprite.length; sh < lsh; sh++) {
            sprite[sh].setAttributes({
                hidden: true
            }, true);
        }
        if (this.slices[index].shadowAttrs) {
            for (i = 0, shadows = this.slices[index].shadowAttrs, l = shadows.length; i < l; i++) {
                shadow = shadows[i];
                for (sh = 0, lsh = shadow.length; sh < lsh; sh++) {
                    shadow[sh].setAttributes({
                        hidden: true
                    }, true);
                }
            }
        }
        this.drawSeries();
    },

    
    showAll: function(index) {
        index = (isNaN(this._index) ? index : this._index) || 0;
        this.__excludes[index] = false;
        this.drawSeries();
    },

    
    highlightItem: function(item) {
        var me = this,
            rad = me.rad,
            highlightSegment,
            animate,
            attrs,
            i,
            shadows,
            shadow,
            ln,
            to,
            itemHighlightSegment,
            prop,
            group,
            display,
            label,
            middle,
            r,
            x,
            y;
        item = item || this.items[this._index];

        
        
        
        
        this.unHighlightItem();

        if (!item || me.animating || (item.sprite && item.sprite._animating)) {
            return;
        }
        me.callParent([item]);
        if (!me.highlight) {
            return;
        }
        if ('segment' in me.highlightCfg) {
            highlightSegment = me.highlightCfg.segment;
            animate = me.chart.animate;
            
            if (me.labelsGroup) {
                group = me.labelsGroup;
                display = me.label.display;
                label = group.getAt(item.index);
                middle = (item.startAngle + item.endAngle) / 2 * rad;
                r = highlightSegment.margin || 0;
                x = r * Math.cos(middle);
                y = r * Math.sin(middle);

                
                
                
                
                
                if (Math.abs(x) < 1e-10) {
                    x = 0;
                }
                if (Math.abs(y) < 1e-10) {
                    y = 0;
                }

                if (animate) {
                    label.stopAnimation();
                    label.animate({
                        to: {
                            translate: {
                                x: x,
                                y: y
                            }
                        },
                        duration: me.highlightDuration
                    });
                }
                else {
                    label.setAttributes({
                        translate: {
                            x: x,
                            y: y
                        }
                    }, true);
                }
            }
            
            if (me.chart.shadow && item.shadows) {
                i = 0;
                shadows = item.shadows;
                ln = shadows.length;
                for (; i < ln; i++) {
                    shadow = shadows[i];
                    to = {};
                    itemHighlightSegment = item.sprite._from.segment;
                    for (prop in itemHighlightSegment) {
                        if (! (prop in highlightSegment)) {
                            to[prop] = itemHighlightSegment[prop];
                        }
                    }
                    attrs = {
                        segment: Ext.applyIf(to, me.highlightCfg.segment)
                    };
                    if (animate) {
                        shadow.stopAnimation();
                        shadow.animate({
                            to: attrs,
                            duration: me.highlightDuration
                        });
                    }
                    else {
                        shadow.setAttributes(attrs, true);
                    }
                }
            }
        }
    },

    
    unHighlightItem: function() {
        var me = this,
            items,
            animate,
            shadowsEnabled,
            group,
            len,
            i,
            j,
            display,
            shadowLen,
            p,
            to,
            ihs,
            hs,
            sprite,
            shadows,
            shadow,
            item,
            label,
            attrs;
        if (!me.highlight) {
            return;
        }

        if (('segment' in me.highlightCfg) && me.items) {
            items = me.items;
            animate = me.chart.animate;
            shadowsEnabled = !!me.chart.shadow;
            group = me.labelsGroup;
            len = items.length;
            i = 0;
            j = 0;
            display = me.label.display;

            for (; i < len; i++) {
                item = items[i];
                if (!item) {
                    continue;
                }
                sprite = item.sprite;
                if (sprite && sprite._highlighted) {
                    
                    if (group) {
                        label = group.getAt(item.index);
                        attrs = Ext.apply({
                            translate: {
                                x: 0,
                                y: 0
                            }
                        },
                        display == 'rotate' ? {
                            rotate: {
                                x: label.attr.x,
                                y: label.attr.y,
                                degrees: label.attr.rotation.degrees
                            }
                        }: {});
                        if (animate) {
                            label.stopAnimation();
                            label.animate({
                                to: attrs,
                                duration: me.highlightDuration
                            });
                        }
                        else {
                            label.setAttributes(attrs, true);
                        }
                    }
                    if (shadowsEnabled) {
                        shadows = item.shadows;
                        shadowLen = shadows.length;
                        for (; j < shadowLen; j++) {
                            to = {};
                            ihs = item.sprite._to.segment;
                            hs = item.sprite._from.segment;
                            Ext.apply(to, hs);
                            for (p in ihs) {
                                if (! (p in hs)) {
                                    to[p] = ihs[p];
                                }
                            }
                            shadow = shadows[j];
                            if (animate) {
                                shadow.stopAnimation();
                                shadow.animate({
                                    to: {
                                        segment: to
                                    },
                                    duration: me.highlightDuration
                                });
                            }
                            else {
                                shadow.setAttributes({ segment: to }, true);
                            }
                        }
                    }
                }
            }
        }
        me.callParent(arguments);
    },

    
    getLegendColor: function(index) {
        var me = this;
        return (me.colorSet && me.colorSet[index % me.colorSet.length]) || me.colorArrayStyle[index % me.colorArrayStyle.length];
    }
});



Ext.define('Ext.chart.series.Radar', {

    

    extend:  Ext.chart.series.Series ,

                                                 

    

    type: "radar",
    alias: 'series.radar',


    rad: Math.PI / 180,

    showInLegend: false,

    
    style: {},

    

    

    

    

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface;
        me.group = surface.getGroup(me.seriesId);
        if (me.showMarkers) {
            me.markerGroup = surface.getGroup(me.seriesId + '-markers');
        }
    },

    
    drawSeries: function() {
        var me = this,
            store = me.chart.getChartStore(),
            data = store.data.items,
            d, record,
            group = me.group,
            chart = me.chart,
            seriesItems = chart.series.items,
            s, sLen, series,
            field = me.field || me.yField,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            colorArrayStyle = me.colorArrayStyle,
            centerX, centerY,
            items,
            radius,
            maxValue = 0,
            fields = [],
            max = Math.max,
            cos = Math.cos,
            sin = Math.sin,
            pi2 = Math.PI * 2,
            l = store.getCount(),
            startPath, path, x, y, rho,
            i, nfields,
            seriesStyle = me.seriesStyle,
            axis = chart.axes && chart.axes.get(0),
            aggregate = !(axis && axis.maximum);

        me.setBBox();
        
        maxValue = aggregate? 0 : (axis.maximum || 0);
        
        Ext.apply(seriesStyle, me.style || {});

        
        if (!store || !store.getCount() || me.seriesIsHidden) {
            me.hide();
            me.items = [];
            if (me.radar) {
                me.radar.hide(true);
            }
            me.radar = null;
            return;
        }
        
        if(!seriesStyle['stroke']){
            seriesStyle['stroke'] = colorArrayStyle[me.themeIdx % colorArrayStyle.length];
        }

        me.unHighlightItem();
        me.cleanHighlights();

        centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY = me.centerY = chartBBox.y + (chartBBox.height / 2);
        me.radius = radius = Math.min(chartBBox.width, chartBBox.height) /2;
        me.items = items = [];

        if (aggregate) {
            
            for (s = 0, sLen = seriesItems.length; s < sLen; s++) {
                series = seriesItems[s];
                fields.push(series.yField);
            }
            
            for (d = 0; d < l; d++) {
                record = data[d];
                for (i = 0, nfields = fields.length; i < nfields; i++) {
                    maxValue = max(+record.get(fields[i]), maxValue);
                }
            }
        }
        
        maxValue = maxValue || 1;
        
        startPath = []; path = [];
        for (i = 0; i < l; i++) {
            record = data[i];
            rho = radius * record.get(field) / maxValue;
            x = rho * cos(i / l * pi2);
            y = rho * sin(i / l * pi2);
            if (i == 0) {
                path.push('M', x + centerX, y + centerY);
                startPath.push('M', 0.01 * x + centerX, 0.01 * y + centerY);
            } else {
                path.push('L', x + centerX, y + centerY);
                startPath.push('L', 0.01 * x + centerX, 0.01 * y + centerY);
            }
            items.push({
                sprite: false, 
                point: [centerX + x, centerY + y],
                storeItem: record,
                series: me
            });
        }
        path.push('Z');
        
        if (!me.radar) {
            me.radar = surface.add(Ext.apply({
                type: 'path',
                group: group,
                path: startPath
            }, seriesStyle || {}));
        }
        
        if (chart.resizing) {
            me.radar.setAttributes({
                path: startPath
            }, true);
        }
        
        if (chart.animate) {
            me.onAnimate(me.radar, {
                to: Ext.apply({
                    path: path
                }, seriesStyle || {})
            });
        } else {
            me.radar.setAttributes(Ext.apply({
                path: path
            }, seriesStyle || {}), true);
        }
        
        if (me.showMarkers) {
            me.drawMarkers();
        }
        me.renderLabels();
        me.renderCallouts();
    },

    
    drawMarkers: function() {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            store = chart.getChartStore(),
            markerStyle = Ext.apply({}, me.markerStyle || {}),
            endMarkerStyle = Ext.apply(markerStyle, me.markerConfig, {
                fill: me.colorArrayStyle[me.themeIdx % me.colorArrayStyle.length]
            }),
            items = me.items,
            type = endMarkerStyle.type,
            markerGroup = me.markerGroup,
            centerX = me.centerX,
            centerY = me.centerY,
            item, i, l, marker, rendererAttributes;

        delete endMarkerStyle.type;

        for (i = 0, l = items.length; i < l; i++) {
            item = items[i];
            marker = markerGroup.getAt(i);
            if (!marker) {
                marker = Ext.chart.Shape[type](surface, Ext.apply({
                    group: markerGroup,
                    x: 0,
                    y: 0,
                    translate: {
                        x: centerX,
                        y: centerY
                    }
                }, endMarkerStyle));
            }
            else {
                marker.show();
            }

            item.sprite = marker;

            if (chart.resizing) {
                marker.setAttributes({
                    x: 0,
                    y: 0,
                    translate: {
                        x: centerX,
                        y: centerY
                    }
                }, true);
            }
            marker._to = {
                translate: {
                    x: item.point[0],
                    y: item.point[1]
                }
            };
            
            rendererAttributes = me.renderer(marker, store.getAt(i), marker._to, i, store);
            rendererAttributes = Ext.applyIf(rendererAttributes || {}, endMarkerStyle || {});
            if (chart.animate) {
                me.onAnimate(marker, {
                    to: rendererAttributes
                });
            }
            else {
                marker.setAttributes(rendererAttributes, true);
            }
        }
    },

    isItemInPoint: function(x, y, item) {
        var point,
            tolerance = 10,
            abs = Math.abs;
        point = item.point;
        return (abs(point[0] - x) <= tolerance &&
                abs(point[1] - y) <= tolerance);
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            centerX = me.centerX,
            centerY = me.centerY,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {});

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': centerX,
            'y': centerY
        }, endLabelStyle || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            centerX = me.centerX,
            centerY = me.centerY,
            opt = {
                x: Number(item.point[0]),
                y: Number(item.point[1])
            },
            x = opt.x - centerX,
            y = opt.y - centerY,
            theta = Math.atan2(y, x || 1),
            deg = theta * 180 / Math.PI,
            labelBox, direction;

            function fixAngle(a) {
                if (a < 0) {
                    a += 360;
                }
                return a % 360;
            }

        label.setAttributes({
            text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index),
            hidden: true
        },
        true);

        
        
        labelBox = label.getBBox();
        deg = fixAngle(deg);
        if ((deg > 45 && deg < 135) || (deg > 225 && deg < 315)) {
            direction = (deg > 45 && deg < 135 ? 1 : -1);
            opt.y += direction * labelBox.height/2;
        } else {
            direction = (deg >= 135 && deg <= 225 ? -1 : 1);
            opt.x += direction * labelBox.width/2;
        }

        if (resizing) {
            label.setAttributes({
                x: centerX,
                y: centerY
            }, true);
        }

        if (animate) {
            label.show(true);
            me.onAnimate(label, {
                to: opt
            });
        } else {
            label.setAttributes(opt, true);
            label.show(true);
        }
    },

    
    toggleAll: function(show) {
        var me = this,
            i, ln, shadow, shadows;
        if (!show) {
            Ext.chart.series.Radar.superclass.hideAll.call(me);
        }
        else {
            Ext.chart.series.Radar.superclass.showAll.call(me);
        }
        if (me.radar) {
            me.radar.setAttributes({
                hidden: !show
            }, true);
            
            if (me.radar.shadows) {
                for (i = 0, shadows = me.radar.shadows, ln = shadows.length; i < ln; i++) {
                    shadow = shadows[i];
                    shadow.setAttributes({
                        hidden: !show
                    }, true);
                }
            }
        }
    },

    
    hideAll: function() {
        this.toggleAll(false);
        this.hideMarkers(0);
    },

    
    showAll: function() {
        this.toggleAll(true);
    },

    
    hideMarkers: function(index) {
        var me = this,
            count = me.markerGroup && me.markerGroup.getCount() || 0,
            i = index || 0;
        for (; i < count; i++) {
            me.markerGroup.getAt(i).hide(true);
        }
    },

    
    
    getAxesForXAndYFields: function() {
        var me = this,
            chart = me.chart,
            axes = chart.axes,
            axis = [].concat(axes && axes.get(0));

        return {
            yAxis: axis
        };
    }
});



Ext.define('Ext.chart.series.Scatter', {

    

    extend:  Ext.chart.series.Cartesian ,

                                                                        

    

    type: 'scatter',
    alias: 'series.scatter',

    

    

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            shadow = me.chart.shadow,
            surface = me.chart.surface, i, l;
        Ext.apply(me, config, {
            style: {},
            markerConfig: {},
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 0.05,
                stroke: 'rgb(0, 0, 0)'
            }, {
                "stroke-width": 4,
                "stroke-opacity": 0.1,
                stroke: 'rgb(0, 0, 0)'
            }, {
                "stroke-width": 2,
                "stroke-opacity": 0.15,
                stroke: 'rgb(0, 0, 0)'
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
    },

    
    getBounds: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            chartAxes = chart.axes,
            boundAxes = me.getAxesForXAndYFields(),
            boundXAxis = boundAxes.xAxis,
            boundYAxis = boundAxes.yAxis,
            bbox, xScale, yScale, ln, minX, minY, maxX, maxY, i, axis, ends;

        me.setBBox();
        bbox = me.bbox;

        if (axis = chartAxes.get(boundXAxis)) {
            ends = axis.applyData();
            minX = ends.from;
            maxX = ends.to;
        }

        if (axis = chartAxes.get(boundYAxis)) {
            ends = axis.applyData();
            minY = ends.from;
            maxY = ends.to;
        }

        
        if (me.xField && !Ext.isNumber(minX)) {
            axis = me.getMinMaxXValues();
            minX = axis[0];
            maxX = axis[1];
        }

        if (me.yField && !Ext.isNumber(minY)) {
            axis = me.getMinMaxYValues();
            minY = axis[0];
            maxY = axis[1];
        }

        if (isNaN(minX)) {
            minX = 0;
            maxX = store.getCount() - 1;
            xScale = bbox.width / (store.getCount() - 1);
        }
        else {
            xScale = bbox.width / (maxX - minX);
        }

        if (isNaN(minY)) {
            minY = 0;
            maxY = store.getCount() - 1;
            yScale = bbox.height / (store.getCount() - 1);
        }
        else {
            yScale = bbox.height / (maxY - minY);
        }

        return {
            bbox: bbox,
            minX: minX,
            minY: minY,
            xScale: xScale,
            yScale: yScale
        };
    },

    
    getPaths: function() {
        var me = this,
            chart = me.chart,
            enableShadows = chart.shadow,
            store = chart.getChartStore(),
            data = store.data.items,
            i, ln, record,
            group = me.group,
            bounds = me.bounds = me.getBounds(),
            bbox = me.bbox,
            xScale = bounds.xScale,
            yScale = bounds.yScale,
            minX = bounds.minX,
            minY = bounds.minY,
            boxX = bbox.x,
            boxY = bbox.y,
            boxHeight = bbox.height,
            items = me.items = [],
            attrs = [],
            x, y, xValue, yValue, sprite;

        for (i = 0, ln = data.length; i < ln; i++) {
            record = data[i];
            xValue = record.get(me.xField);
            yValue = record.get(me.yField);
            
            if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue)
                || xValue == null || yValue == null) {
                continue;
            }
            
            if (typeof xValue == 'string' || typeof xValue == 'object' && !Ext.isDate(xValue)) {
                xValue = i;
            }
            if (typeof yValue == 'string' || typeof yValue == 'object' && !Ext.isDate(yValue)) {
                yValue = i;
            }
            x = boxX + (xValue - minX) * xScale;
            y = boxY + boxHeight - (yValue - minY) * yScale;
            attrs.push({
                x: x,
                y: y
            });

            me.items.push({
                series: me,
                value: [xValue, yValue],
                point: [x, y],
                storeItem: record
            });

            
            if (chart.animate && chart.resizing) {
                sprite = group.getAt(i);
                if (sprite) {
                    me.resetPoint(sprite);
                    if (enableShadows) {
                        me.resetShadow(sprite);
                    }
                }
            }
        }
        return attrs;
    },

    
    resetPoint: function(sprite) {
        var bbox = this.bbox;
        sprite.setAttributes({
            translate: {
                x: (bbox.x + bbox.width) / 2,
                y: (bbox.y + bbox.height) / 2
            }
        }, true);
    },

    
    resetShadow: function(sprite) {
        var me = this,
            shadows = sprite.shadows,
            shadowAttributes = me.shadowAttributes,
            ln = me.shadowGroups.length,
            bbox = me.bbox,
            i, attr;
        for (i = 0; i < ln; i++) {
            attr = Ext.apply({}, shadowAttributes[i]);
            
            if (attr.translate) {
                attr.translate.x += (bbox.x + bbox.width) / 2;
                attr.translate.y += (bbox.y + bbox.height) / 2;
            }
            else {
                attr.translate = {
                    x: (bbox.x + bbox.width) / 2,
                    y: (bbox.y + bbox.height) / 2
                };
            }
            shadows[i].setAttributes(attr, true);
        }
    },

    
    createPoint: function(attr, type) {
        var me = this,
            chart = me.chart,
            group = me.group,
            bbox = me.bbox;

        return Ext.chart.Shape[type](chart.surface, Ext.apply({}, {
            x: 0,
            y: 0,
            group: group,
            translate: {
                x: (bbox.x + bbox.width) / 2,
                y: (bbox.y + bbox.height) / 2
            }
        }, attr));
    },

    
    createShadow: function(sprite, endMarkerStyle, type) {
        var me = this,
            chart = me.chart,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            lnsh = shadowGroups.length,
            bbox = me.bbox,
            i, shadow, shadows, attr;

        sprite.shadows = shadows = [];

        for (i = 0; i < lnsh; i++) {
            attr = Ext.apply({}, shadowAttributes[i]);
            if (attr.translate) {
                attr.translate.x += (bbox.x + bbox.width) / 2;
                attr.translate.y += (bbox.y + bbox.height) / 2;
            }
            else {
                Ext.apply(attr, {
                    translate: {
                        x: (bbox.x + bbox.width) / 2,
                        y: (bbox.y + bbox.height) / 2
                    }
                });
            }
            Ext.apply(attr, endMarkerStyle);
            shadow = Ext.chart.Shape[type](chart.surface, Ext.apply({}, {
                x: 0,
                y: 0,
                group: shadowGroups[i]
            }, attr));
            shadows.push(shadow);
        }
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.getChartStore(),
            group = me.group,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            lnsh = shadowGroups.length,
            sprite, attrs, attr, ln, i, endMarkerStyle, shindex, type, shadows,
            rendererAttributes, shadowAttribute;

        endMarkerStyle = Ext.apply(me.markerStyle, me.markerConfig);
        type = endMarkerStyle.type || 'circle';
        delete endMarkerStyle.type;

        
        if (!store || !store.getCount()) {
            me.hide();
            me.items = [];
            return;
        }


        me.unHighlightItem();
        me.cleanHighlights();

        attrs = me.getPaths();
        ln = attrs.length;
        for (i = 0; i < ln; i++) {
            attr = attrs[i];
            sprite = group.getAt(i);
            Ext.apply(attr, endMarkerStyle);

            
            if (!sprite) {
                sprite = me.createPoint(attr, type);
                if (enableShadows) {
                    me.createShadow(sprite, endMarkerStyle, type);
                }
            }

            shadows = sprite.shadows;
            if (chart.animate) {
                rendererAttributes = me.renderer(sprite, store.getAt(i), { translate: attr }, i, store);
                sprite._to = rendererAttributes;
                me.onAnimate(sprite, {
                    to: rendererAttributes
                });
                
                for (shindex = 0; shindex < lnsh; shindex++) {
                    shadowAttribute = Ext.apply({}, shadowAttributes[shindex]);
                    rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({}, { 
                        hidden: false,
                        translate: {
                            x: attr.x + (shadowAttribute.translate? shadowAttribute.translate.x : 0),
                            y: attr.y + (shadowAttribute.translate? shadowAttribute.translate.y : 0)
                        }
                    }, shadowAttribute), i, store);
                    me.onAnimate(shadows[shindex], { to: rendererAttributes });
                }
            }
            else {
                rendererAttributes = me.renderer(sprite, store.getAt(i), { translate: attr }, i, store);
                sprite._to = rendererAttributes;
                sprite.setAttributes(rendererAttributes, true);
                
                for (shindex = 0; shindex < lnsh; shindex++) {
                    shadowAttribute = Ext.apply({}, shadowAttributes[shindex]);
                    rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({}, { 
                        hidden: false,
                        translate: {
                            x: attr.x + (shadowAttribute.translate? shadowAttribute.translate.x : 0),
                            y: attr.y + (shadowAttribute.translate? shadowAttribute.translate.y : 0)
                        } 
                    }, shadowAttribute), i, store);
                    shadows[shindex].setAttributes(rendererAttributes, true);
                }
            }
            me.items[i].sprite = sprite;
        }

        
        ln = group.getCount();
        for (i = attrs.length; i < ln; i++) {
            group.getAt(i).hide(true);
        }
        me.renderLabels();
        me.renderCallouts();
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle),
            bbox = me.bbox;

        return me.chart.surface.add(Ext.apply({
            type: 'text',
            'text-anchor': 'middle',
            group: group,
            x: Number(item.point[0]),
            y: bbox.y + bbox.height / 2
        }, endLabelStyle));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            bbox = me.bbox,
            x = Number(item.point[0]),
            y = Number(item.point[1]),
            radius = item.sprite.attr.radius,
            labelBox, markerBox, width, height, xOffset, yOffset,
            anim;

        label.setAttributes({
            text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index),
            hidden: true
        }, true);


        
        markerBox = item.sprite.getBBox();
        markerBox.width = markerBox.width || (radius * 2);
        markerBox.height = markerBox.height || (radius * 2);

        labelBox = label.getBBox();
        width = labelBox.width/2;
        height = labelBox.height/2;

        if (display == 'rotate') {
            
            xOffset = markerBox.width/2 + width + height/2;
            if (x + xOffset + width > bbox.x + bbox.width) {
                x -= xOffset;
            } else {
                x += xOffset;
            }
            label.setAttributes({
                'rotation': {
                    x: x,
                    y: y,
                    degrees: -45
                }
            }, true);
        } else if (display == 'under' || display == 'over') {
            label.setAttributes({
                'rotation': {
                    degrees: 0
                }
            }, true);

            
            if (x < bbox.x + width) {
                x = bbox.x + width;
            } else if (x + width > bbox.x + bbox.width) {
                x = bbox.x + bbox.width - width;
            }

            yOffset = markerBox.height/2 + height;
            y = y + (display == 'over' ? -yOffset : yOffset);
            if (y < bbox.y + height) {
                y += 2 * yOffset;
            } else if (y + height > bbox.y + bbox.height) {
                y -= 2 * yOffset;
            }
        }

        if (!chart.animate) {
            label.setAttributes({
                x: x,
                y: y
            }, true);
            label.show(true);
        }
        else {
            if (resizing) {
                anim = item.sprite.getActiveAnimation();
                if (anim) {
                    anim.on('afteranimate', function() {
                        label.setAttributes({
                            x: x,
                            y: y
                        }, true);
                        label.show(true);
                    });
                }
                else {
                    label.show(true);
                }
            }
            else {
                me.onAnimate(label, {
                    to: {
                        x: x,
                        y: y
                    }
                });
            }
        }
    },

    
    onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            resizing = chart.resizing,
            config = me.callouts,
            items = me.items,
            cur = item.point,
            normal,
            bbox = callout.label.getBBox(),
            offsetFromViz = 30,
            offsetToSide = 10,
            offsetBox = 3,
            boxx, boxy, boxw, boxh,
            p, clipRect = me.bbox,
            x, y;

        
        normal = [Math.cos(Math.PI /4), -Math.sin(Math.PI /4)];
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        
        
        if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
            normal[0] *= -1;
        }
        if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;

        if (chart.animate) {
            
            me.onAnimate(callout.lines, {
                to: {
                    path: ["M", cur[0], cur[1], "L", x, y, "Z"]
                }
            }, true);
            
            me.onAnimate(callout.box, {
                to: {
                    x: boxx,
                    y: boxy,
                    width: boxw,
                    height: boxh
                }
            }, true);
            
            me.onAnimate(callout.label, {
                to: {
                    x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
                    y: y
                }
            }, true);
        } else {
            
            callout.lines.setAttributes({
                path: ["M", cur[0], cur[1], "L", x, y, "Z"]
            }, true);
            
            callout.box.setAttributes({
                x: boxx,
                y: boxy,
                width: boxw,
                height: boxh
            }, true);
            
            callout.label.setAttributes({
                x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
                y: y
            }, true);
        }
        for (p in callout) {
            callout[p].show(true);
        }
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item) {
        var point,
            tolerance = 10,
            abs = Math.abs;

        function dist(point) {
            var dx = abs(point[0] - x),
                dy = abs(point[1] - y);
            return Math.sqrt(dx * dx + dy * dy);
        }
        point = item.point;
        return (point[0] - tolerance <= x && point[0] + tolerance >= x &&
            point[1] - tolerance <= y && point[1] + tolerance >= y);
    }
});



Ext.define('Ext.layout.container.Table', {

    

    alias: ['layout.table'],
    extend:  Ext.layout.container.Container ,
    alternateClassName: 'Ext.layout.TableLayout',

    

    

    
    monitorResize:false,

    type: 'table',
    
    createsInnerCt: true,

    targetCls: Ext.baseCSSPrefix + 'table-layout-ct',
    tableCls: Ext.baseCSSPrefix + 'table-layout',
    cellCls: Ext.baseCSSPrefix + 'table-layout-cell',

    
    tableAttrs: null,

    

    

    getItemSizePolicy: function (item) {
        return this.autoSizePolicy;
    },
    
    initHierarchyState: function (hierarchyStateInner) {    
        hierarchyStateInner.inShrinkWrapTable  = true;
    },

    getLayoutItems: function() {
        var me = this,
            result = [],
            items = me.callParent(),
            item,
            len = items.length, i;

        for (i = 0; i < len; i++) {
            item = items[i];
            if (!item.hidden) {
                result.push(item);
            }
        }
        return result;
    },
    
    getHiddenItems: function(){
        var result = [],
            items = this.owner.items.items,
            len = items.length,
            i = 0, item;
            
        for (; i < len; ++i) {
            item = items[i];
            if (item.rendered && item.hidden) {
                result.push(item);
            }
        }    
        return result;
    },

    
    renderChildren: function() {
        var me = this,
            items = me.getLayoutItems(),
            tbody = me.owner.getTargetEl().child('table', true).tBodies[0],
            rows = tbody.rows,
            i = 0,
            len = items.length,
            hiddenItems = me.getHiddenItems(),
            cells, curCell, rowIdx, cellIdx, item, trEl, tdEl, itemCt, el;

        
        cells = me.calculateCells(items);

        
        
        
        for (; i < len; i++) {
            curCell = cells[i];
            rowIdx = curCell.rowIdx;
            cellIdx = curCell.cellIdx;
            item = items[i];

            
            trEl = rows[rowIdx];
            if (!trEl) {
                trEl = tbody.insertRow(rowIdx);
                if (me.trAttrs) {
                    trEl.set(me.trAttrs);
                }
            }

            
            itemCt = tdEl = Ext.get(trEl.cells[cellIdx] || trEl.insertCell(cellIdx));
            if (me.needsDivWrap()) { 
                itemCt = tdEl.first() || tdEl.createChild({tag: 'div'});
                itemCt.setWidth(null);
            }

            
            if (!item.rendered) {
                me.renderItem(item, itemCt, 0);
            } else if (!me.isValidParent(item, itemCt, rowIdx, cellIdx, tbody)) {
                me.moveItem(item, itemCt, 0);
            }

            
            if (me.tdAttrs) {
                tdEl.set(me.tdAttrs);
            }
            if (item.tdAttrs) {
                tdEl.set(item.tdAttrs);
            }
            tdEl.set({
                colSpan: item.colspan || 1,
                rowSpan: item.rowspan || 1,
                id: item.cellId || '',
                cls: me.cellCls + ' ' + (item.cellCls || '')
            });

            
            if (!cells[i + 1] || cells[i + 1].rowIdx !== rowIdx) {
                cellIdx++;
                while (trEl.cells[cellIdx]) {
                    trEl.deleteCell(cellIdx);
                }
            }
        }

        
        rowIdx++;
        while (tbody.rows[rowIdx]) {
            tbody.deleteRow(rowIdx);
        }
        
        
        
        for (i = 0, len = hiddenItems.length; i < len; ++i) {
            me.ensureInDocument(hiddenItems[i].getEl());
        }
    },
    
    ensureInDocument: function(el){
        var dom = el.dom.parentNode;
        while (dom) {
            if (dom.tagName.toUpperCase() == 'BODY') {
                return;
            }
            dom = dom.parentNode;
        } 
        
        Ext.getDetachedBody().appendChild(el);
    },

    calculate: function (ownerContext) {
        if (!ownerContext.hasDomProp('containerChildrenSizeDone')) {
            this.done = false;
        } else {
            var targetContext = ownerContext.targetContext,
                widthShrinkWrap = ownerContext.widthModel.shrinkWrap,
                heightShrinkWrap = ownerContext.heightModel.shrinkWrap,
                shrinkWrap = heightShrinkWrap || widthShrinkWrap,
                table = shrinkWrap && targetContext.el.child('table', true),
                targetPadding = shrinkWrap && targetContext.getPaddingInfo();

            if (widthShrinkWrap) {
                ownerContext.setContentWidth(table.offsetWidth + targetPadding.width, true);
            }

            if (heightShrinkWrap) {
                ownerContext.setContentHeight(table.offsetHeight + targetPadding.height, true);
            }
        }
    },

    finalizeLayout: function() {
        if (this.needsDivWrap()) {
            
            var items = this.getLayoutItems(),
                i,
                iLen  = items.length,
                item;

            for (i = 0; i < iLen; i++) {
                item = items[i];

                Ext.fly(item.el.dom.parentNode).setWidth(item.getWidth());
            }
        }
        if (Ext.isIE6 || Ext.isIEQuirks) {
            
            this.owner.getTargetEl().child('table').repaint();
        }
    },

    
    calculateCells: function(items) {
        var cells = [],
            rowIdx = 0,
            colIdx = 0,
            cellIdx = 0,
            totalCols = this.columns || Infinity,
            rowspans = [], 
            i = 0, j,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];

            
            while (colIdx >= totalCols || rowspans[colIdx] > 0) {
                if (colIdx >= totalCols) {
                    
                    colIdx = 0;
                    cellIdx = 0;
                    rowIdx++;

                    
                    for (j = 0; j < totalCols; j++) {
                        if (rowspans[j] > 0) {
                            rowspans[j]--;
                        }
                    }
                } else {
                    colIdx++;
                }
            }

            
            cells.push({
                rowIdx: rowIdx,
                cellIdx: cellIdx
            });

            
            for (j = item.colspan || 1; j; --j) {
                rowspans[colIdx] = item.rowspan || 1;
                ++colIdx;
            }
            ++cellIdx;
        }

        return cells;
    },

    getRenderTree: function() {
        var me = this,
            items = me.getLayoutItems(),
            cells,
            rows = [],
            result = Ext.apply({
                tag: 'table',
                role: 'presentation',
                cls: me.tableCls,
                cellspacing: 0,
                cellpadding: 0,
                cn: {
                    tag: 'tbody',
                    cn: rows
                }
            }, me.tableAttrs),
            tdAttrs = me.tdAttrs,
            needsDivWrap = me.needsDivWrap(),
            i, len = items.length, item, curCell, tr, rowIdx, cellIdx, cell;

        
        cells = me.calculateCells(items);

        for (i = 0; i < len; i++) {
            item = items[i];
            
            curCell = cells[i];
            rowIdx = curCell.rowIdx;
            cellIdx = curCell.cellIdx;

            
            tr = rows[rowIdx];
            if (!tr) {
                tr = rows[rowIdx] = {
                    tag: 'tr',
                    cn: []
                };
                if (me.trAttrs) {
                    Ext.apply(tr, me.trAttrs);
                }
            }

            
            cell = tr.cn[cellIdx] = {
                tag: 'td'
            };
            if (tdAttrs) {
                Ext.apply(cell, tdAttrs);
            }
            Ext.apply(cell, {
                colSpan: item.colspan || 1,
                rowSpan: item.rowspan || 1,
                id: item.cellId || '',
                cls: me.cellCls + ' ' + (item.cellCls || '')
            });

            if (needsDivWrap) { 
                cell = cell.cn = {
                    tag: 'div'
                };
            }

            me.configureItem(item);
            
            cell.cn = item.getRenderTree();
        }
        return result;
    },

    isValidParent: function(item, target, rowIdx, cellIdx) {
        var tbody,
            correctCell,
            table;

        
        if (arguments.length === 3) {
            table = item.el.up('table');
            return table && table.dom.parentNode === target.dom;
        }
        tbody = this.owner.getTargetEl().child('table', true).tBodies[0];
        correctCell = tbody.rows[rowIdx].cells[cellIdx];
        return item.el.dom.parentNode === correctCell;
    },

    
    needsDivWrap: function() {
        return Ext.isOpera10_5;
    }
});


Ext.define('Ext.container.ButtonGroup', {
    extend:  Ext.panel.Panel ,
    alias: 'widget.buttongroup',
    alternateClassName: 'Ext.ButtonGroup',

                                             

    

    
    baseCls: Ext.baseCSSPrefix + 'btn-group',

    
    layout: {
        type: 'table'
    },

    defaultType: 'button',

    
    frame: true,

    

    frameHeader: false,

    titleAlign: 'center',

    noTitleCls: 'notitle',

    initComponent : function() {
        
        var me = this,
            cols = me.columns;

        if (cols) {
            me.layout = Ext.apply({}, {columns: cols}, me.layout);
        }

        if (!me.title) {
            me.addClsWithUI(me.noTitleCls);
        }
        me.callParent(arguments);
    },

    
    onBeforeAdd: function(component) {
        if (component.isButton) {
            if (this.defaultButtonUI && component.ui === 'default' &&
                !component.hasOwnProperty('ui')) {
                component.ui = this.defaultButtonUI;
            } else {
                component.ui = component.ui + '-toolbar';
            }
        }
        this.callParent(arguments);
    },

    
    applyDefaults: function(c) {
        if (!Ext.isString(c)) {
            c = this.callParent(arguments);
        }
        return c;
    }

    
    
    
    
    
});


Ext.define('Ext.container.Monitor', {
    target: null,
    selector: '',
    
    scope: null,
    addHandler: null,
    removeHandler: null,
    
    disabled: 0,
    
    constructor: function(config){
        Ext.apply(this, config);
    },
    
    bind: function(target){
        var me = this;
        
        me.target = target;
        target.on('beforedestroy', me.disable, me);
        me.onContainerAdd(target);
    },
    
    unbind: function() {
        var me = this,
            target = me.target;
            
        if (target) {
            target.un('beforedestroy', me.disable, me);
        }
        me.items = null;
    },
    
    disable: function(){
        ++this.disabled;    
    },
    
    enable: function(){
        if (this.disabled > 0) {
            --this.disabled;
        }
    },
    
    handleAdd: function(ct, comp) {
        if (!this.disabled) {
            if (comp.is(this.selector)) {
                this.onItemAdd(comp.ownerCt, comp);
            }
        
            if (comp.isQueryable) {
                this.onContainerAdd(comp);
            }
        }
    },
    
    onItemAdd: function(ct, comp){
        var me = this,
            items = me.items,
            handler = me.addHandler;
            
        if (!me.disabled) {
            if (handler) {
                handler.call(me.scope || comp, comp);
            }
            if (items) {
                items.add(comp);
            }
        }
    },
    
    onItemRemove: function(ct, comp){
        var me = this,
            items = me.items,
            handler = me.removeHandler;
            
        if (!me.disabled) {
            if (handler) {
                handler.call(me.scope || comp, comp);
            }
            if (items) {
                items.remove(comp);
            }
        }
    },
    
    onContainerAdd: function(ct, preventChildren) {
        var me = this,
            items, len,
            handleAdd = me.handleAdd,
            handleRemove = me.handleRemove,
            i, comp;
        
        if (ct.isContainer) {
            ct.on('add', handleAdd, me);
            ct.on('dockedadd', handleAdd, me);
            ct.on('remove', handleRemove, me);
            ct.on('dockedremove', handleRemove, me);
        }
        
        
        
        if (preventChildren !== true) {
            items = ct.query(me.selector);
            for (i = 0, len = items.length; i < len; ++i) {
                comp = items[i];
                me.onItemAdd(comp.ownerCt, comp);
            }
        }
         
        items = ct.query('container');
        for (i = 0, len = items.length; i < len; ++i) {
            me.onContainerAdd(items[i], true);
        }
        
    },
    
    handleRemove: function(ct, comp) {
        var me = this;
            
        
        
        if (!me.disabled) {
            if (comp.is(me.selector)) {
                me.onItemRemove(ct, comp);
            }
        
            if (comp.isQueryable) {
                me.onContainerRemove(ct, comp);
            }
        }
    },
    
    onContainerRemove: function(ct, comp){
        var me = this,
            items, i, len, item;
            
        
        
        if (!comp.isDestroyed && !comp.destroying && comp.isContainer) {
            me.removeCtListeners(comp);
            
            items = comp.query(me.selector);
            for (i = 0, len = items.length; i < len; ++i) {
                item = items[i];
                me.onItemRemove(item.ownerCt, item);
            }
            
            items = comp.query('container');
            for (i = 0, len = items.length; i < len; ++i) {
                me.removeCtListeners(items[i]);
            }
        } else {
            
            me.invalidateItems();
        }
    },
    
    removeCtListeners: function(comp){
        var me = this;
        comp.un('add', me.handleAdd, me);
        comp.un('dockedadd', me.handleAdd, me);
        comp.un('remove', me.handleRemove, me);
        comp.un('dockedremove', me.handleRemove, me);
    },
    
    getItems: function(){
        var me = this,
            items = me.items;
            
        if (!items) {
            items = me.items = new Ext.util.MixedCollection();
            items.addAll(me.target.query(me.selector));
        }
        return items;
    },
    
    invalidateItems: function(){
        this.items = null;
    }
});


Ext.define('Ext.container.Viewport', {
    extend:  Ext.container.Container ,
    alias: 'widget.viewport',
                                   
    alternateClassName: 'Ext.Viewport',

    
    
    

    

    

    

    

    

    
    isViewport: true,

    ariaRole: 'application',
    
    preserveElOnDestroy: true,
    
    viewportCls: Ext.baseCSSPrefix + 'viewport',

    initComponent : function() {
        var me = this,
            html = document.body.parentNode,
            el = me.el = Ext.getBody();

        
        Ext.getScrollbarSize();
        
        
        me.width = me.height = undefined;

        me.callParent(arguments);
        Ext.fly(html).addCls(me.viewportCls);
        if (me.autoScroll) {
            Ext.fly(html).setStyle(me.getOverflowStyle());
            delete me.autoScroll;
        }
        el.setHeight = el.setWidth = Ext.emptyFn;
        el.dom.scroll = 'no';
        me.allowDomMove = false;
        me.renderTo = me.el;
    },
    
    
    applyTargetCls: function(targetCls) {
        this.el.addCls(targetCls);
    },
    
    onRender: function() {
        var me = this;

        me.callParent(arguments);

        
        
        me.width = Ext.Element.getViewportWidth();
        me.height = Ext.Element.getViewportHeight();
    },

    afterFirstLayout: function() {
        var me = this;

        me.callParent(arguments);
        setTimeout(function() {
            Ext.EventManager.onWindowResize(me.fireResize, me);
        }, 1);
    },

    fireResize : function(width, height){
        
        
        if (width != this.width || height != this.height) {
            this.setSize(width, height);
        }
    },

    initHierarchyState: function(hierarchyState) {
        this.callParent([this.hierarchyState = Ext.rootHierarchyState]);
    },
    
    beforeDestroy: function(){
        var me = this;
        
        me.removeUIFromElement();
        me.el.removeCls(me.baseCls);
        Ext.fly(document.body.parentNode).removeCls(me.viewportCls);
        me.callParent();
    }
});


Ext.define('Ext.data.IdGenerator', {

    
    isGenerator: true,

    
    constructor: function(config) {
        var me = this;

        Ext.apply(me, config);

        if (me.id) {
            Ext.data.IdGenerator.all[me.id] = me;
        }
    },

    

    getRecId: function (rec) {
        return rec.modelName + '-' + rec.internalId;
    },

    

    statics: {
        
        all: {},

        
        get: function (config) {
            var generator,
                id,
                type;

            if (typeof config == 'string') {
                id = type = config;
                config = null;
            } else if (config.isGenerator) {
                return config;
            } else {
                id = config.id || config.type;
                type = config.type;
            }

            generator = this.all[id];
            if (!generator) {
                generator = Ext.create('idgen.' + type, config);
            }

            return generator;
        }
    }
});


Ext.define('Ext.data.SortTypes', {
    
    singleton: true,
    
    
    none : Ext.identityFn,

    
    stripTagsRE : /<\/?[^>]+>/gi,

    
    asText : function(s) {
        return String(s).replace(this.stripTagsRE, "");
    },

    
    asUCText : function(s) {
        return String(s).toUpperCase().replace(this.stripTagsRE, "");
    },

    
    asUCString : function(s) {
        return String(s).toUpperCase();
    },

    
    asDate : function(s) {
        if(!s){
            return 0;
        }
        if(Ext.isDate(s)){
            return s.getTime();
        }
        return Date.parse(String(s));
    },

    
    asFloat : function(s) {
        var val = parseFloat(String(s).replace(/,/g, ""));
        return isNaN(val) ? 0 : val;
    },

    
    asInt : function(s) {
        var val = parseInt(String(s).replace(/,/g, ""), 10);
        return isNaN(val) ? 0 : val;
    }
});


Ext.define('Ext.data.Types', {
    singleton: true 
                                    
}, function() {
    var st = Ext.data.SortTypes;

    Ext.apply(Ext.data.Types, {
        
        stripRe: /[\$,%]/g,

        
        AUTO: {
            sortType: st.none,
            type: 'auto'
        },

        
        STRING: {
            convert: function(v) {
                var defaultValue = this.useNull ? null : '';
                return (v === undefined || v === null) ? defaultValue : String(v);
            },
            sortType: st.asUCString,
            type: 'string'
        },

        
        INT: {
            convert: function(v) {
                
                
                
                if (typeof v == 'number') {
                    return parseInt(v);
                }
                return v !== undefined && v !== null && v !== '' ?
                    parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'int'
        },

        
        FLOAT: {
            convert: function(v) {
                if (typeof v === 'number') {
                    return v;
                }
                return v !== undefined && v !== null && v !== '' ?
                    parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'float'
        },

        
        BOOL: {
            convert: function(v) {
                if (typeof v === 'boolean') {
                    return v;
                }
                if (this.useNull && (v === undefined || v === null || v === '')) {
                    return null;
                }
                return v === 'true' || v == 1;
            },
            sortType: st.none,
            type: 'bool'
        },

        
        DATE: {
            convert: function(v) {
                var df = this.dateReadFormat || this.dateFormat,
                    parsed;

                if (!v) {
                    return null;
                }
                
                if (v instanceof Date) {
                    return v;
                }
                if (df) {
                    return Ext.Date.parse(v, df);
                }

                parsed = Date.parse(v);
                return parsed ? new Date(parsed) : null;
            },
            sortType: st.asDate,
            type: 'date'
        }
    });

    Ext.apply(Ext.data.Types, {
        
        BOOLEAN: this.BOOL,

        
        INTEGER: this.INT,

        
        NUMBER: this.FLOAT
    });
});


Ext.define('Ext.data.Field', {
                                                       
    alias: 'data.field',

    isField: true,
    
    constructor : function(config) {
        var me = this,
            types = Ext.data.Types,
            st;
        
        if (Ext.isString(config)) {
            config = {name: config};
        }
        Ext.apply(me, config);

        st = me.sortType;

        if (me.type) {
            if (Ext.isString(me.type)) {
                me.type = types[me.type.toUpperCase()] || types.AUTO;
            }
        } else {
            me.type = types.AUTO;
        }

        
        if (Ext.isString(st)) {
            me.sortType = Ext.data.SortTypes[st];
        } else if(Ext.isEmpty(st)) {
            me.sortType = me.type.sortType;
        }

        
        if (!config.hasOwnProperty('convert')) {
            me.convert = me.type.convert; 
        } else if (!me.convert && me.type.convert && !config.hasOwnProperty('defaultValue')) {
            
            
            me.defaultValue = me.type.convert(me.defaultValue);
        }

        if (config.convert) {
            me.hasCustomConvert = true;
        }
    },
    
    
    
    

    

    

    
    dateFormat: null,
    
    
    dateReadFormat: null,
    
    
    dateWriteFormat: null,
    
    
    useNull: false,
    
    
    defaultValue: "",

    
    mapping: null,

    
    sortType : null,

    
    sortDir : "ASC",

    
    allowBlank : true,

    
    persist: true
});


Ext.define('Ext.data.Errors', {
    extend:  Ext.util.MixedCollection ,

    
    isValid: function() {
        return this.length === 0;
    },

    
    getByField: function(fieldName) {
        var errors = [],
            error, i;

        for (i = 0; i < this.length; i++) {
            error = this.items[i];

            if (error.field == fieldName) {
                errors.push(error);
            }
        }

        return errors;
    }
});


Ext.define('Ext.data.validations', {
    singleton: true,
    
    
    presenceMessage: 'must be present',
    
    
    lengthMessage: 'is the wrong length',
    
    
    formatMessage: 'is the wrong format',
    
    
    inclusionMessage: 'is not included in the list of acceptable values',
    
    
    exclusionMessage: 'is not an acceptable value',
    
    
    emailMessage: 'is not a valid email address',
    
    
    emailRe: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
    
    
    presence: function(config, value) {
        
        if (arguments.length === 1) {
            value = config;
        }
        
        
        return !!value || value === 0 || value === false;
    },
    
    
    length: function(config, value) {
        if (value === undefined || value === null) {
            return false;
        }
        
        var length = value.length,
            min    = config.min,
            max    = config.max;
        
        if ((min && length < min) || (max && length > max)) {
            return false;
        } else {
            return true;
        }
    },
    
    
    email: function(config, email) {
        return Ext.data.validations.emailRe.test(email);
    },
    
    
    format: function(config, value) {
        return !!(config.matcher && config.matcher.test(value));
    },
    
    
    inclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) != -1;
    },
    
    
    exclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) == -1;
    }
});


Ext.define('Ext.data.Model', {
    alternateClassName: 'Ext.data.Record',

    mixins: {
        observable:  Ext.util.Observable 
    },

               
                           
                               
                         
                          
                             
                               
                                  
      

    compareConvertFields: function(f1, f2) {
        var f1SpecialConvert = f1.convert && f1.type && f1.convert !== f1.type.convert,
            f2SpecialConvert = f2.convert && f2.type && f2.convert !== f2.type.convert;

        if (f1SpecialConvert && !f2SpecialConvert) {
            return 1;
        }

        if (!f1SpecialConvert && f2SpecialConvert) {
            return -1;
        }
        return 0;
    },

    itemNameFn: function(item) {
        return item.name;
    },

    onClassExtended: function(cls, data, hooks) {
        var onBeforeClassCreated = hooks.onBeforeCreated;

        hooks.onBeforeCreated = function(cls, data) {
            var me = this,
                name = Ext.getClassName(cls),
                prototype = cls.prototype,
                superCls = cls.prototype.superclass,

                validations = data.validations || [],
                fields = data.fields || [],
                field,
                associationsConfigs = data.associations || [],
                addAssociations = function(items, type) {
                    var i = 0,
                        len,
                        item;

                    if (items) {
                        items = Ext.Array.from(items);

                        for (len = items.length; i < len; ++i) {
                            item = items[i];

                            if (!Ext.isObject(item)) {
                                item = {model: item};
                            }

                            item.type = type;
                            associationsConfigs.push(item);
                        }
                    }
                },
                idgen = data.idgen,

                fieldsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),

                associationsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),

                superValidations = superCls.validations,
                superFields = superCls.fields,
                superAssociations = superCls.associations,

                associationConfig, i, ln,
                dependencies = [],

                
                idProperty = 'idProperty' in data ? data.idProperty : prototype.idProperty,

                
                idField = idProperty ? (idProperty.isField ? idProperty : new Ext.data.Field(idProperty)) : null,

                
                idFieldDefined = false,

                
                onFieldAddReplace = function(arg0, arg1, arg2) {
                    var newField,
                        pos;

                    if (fieldsMixedCollection.events.add.firing) {
                        
                        pos = arg0;
                        newField  = arg1;
                    } else {
                        
                        newField = arg2;
                        pos = arg1.originalIndex;
                    }

                    
                    
                    newField.originalIndex = pos;

                    
                    
                    
                    if (idField && ((newField.mapping && (newField.mapping === idField.mapping)) || (newField.name === idField.name))) {
                        prototype.idField = newField;
                        idFieldDefined = true;
                        newField.defaultValue = undefined;
                    }
                },

                
                clsProxy = data.proxy,

                
                fieldConvertSortFn = function() {
                    fieldsMixedCollection.sortBy(prototype.compareConvertFields);
                };

            
            cls.modelName = name;
            prototype.modelName = name;

            
            if (superValidations) {
                validations = superValidations.concat(validations);
            }

            data.validations = validations;

            
            if (superFields) {
                fields = superFields.items.concat(fields);
            }

            fieldsMixedCollection.on({
                add:     onFieldAddReplace,
                replace: onFieldAddReplace
            });  

            for (i = 0, ln = fields.length; i < ln; ++i) {
                field = fields[i];
                fieldsMixedCollection.add(field.isField ? field : new Ext.data.Field(field));
            }

            
            
            
            if (idField && !idFieldDefined) {
                prototype.idField = idField;
                idField.defaultValue = undefined;
                fieldsMixedCollection.add(idField);
            }

            
            fieldConvertSortFn();
            fieldsMixedCollection.on({
                add:     fieldConvertSortFn,
                replace: fieldConvertSortFn
            });

            data.fields = fieldsMixedCollection;

            if (idgen) {
                data.idgen = Ext.data.IdGenerator.get(idgen);
            }

            
            
            addAssociations(data.belongsTo, 'belongsTo');
            delete data.belongsTo;
            addAssociations(data.hasMany, 'hasMany');
            delete data.hasMany;
            addAssociations(data.hasOne, 'hasOne');
            delete data.hasOne;

            if (superAssociations) {
                associationsConfigs = superAssociations.items.concat(associationsConfigs);
            }

            for (i = 0, ln = associationsConfigs.length; i < ln; ++i) {
                dependencies.push('association.' + associationsConfigs[i].type.toLowerCase());
            }

            
            if (clsProxy) {
                if (!clsProxy.isProxy) {
                    dependencies.push('proxy.' + (clsProxy.type || clsProxy));
                }
            }
            
            
            else if (!cls.prototype.proxy) {
                cls.prototype.proxy = cls.prototype.defaultProxyType;
                dependencies.push('proxy.' + cls.prototype.defaultProxyType);
            }

            Ext.require(dependencies, function() {
                Ext.ModelManager.registerType(name, cls);

                for (i = 0, ln = associationsConfigs.length; i < ln; ++i) {
                    associationConfig = associationsConfigs[i];
                    if (associationConfig.isAssociation) {
                        associationConfig = Ext.applyIf({
                            ownerModel: name,
                            associatedModel: associationConfig.model
                        }, associationConfig.initialConfig);
                    } else {
                        Ext.apply(associationConfig, {
                            ownerModel: name,
                            associatedModel: associationConfig.model
                        });
                    }

                    if (Ext.ModelManager.getModel(associationConfig.model) === undefined) {
                        Ext.ModelManager.registerDeferredAssociation(associationConfig);
                    } else {
                        associationsMixedCollection.add(Ext.data.association.Association.create(associationConfig));
                    }
                }

                data.associations = associationsMixedCollection;

                
                
                
                
                
                onBeforeClassCreated.call(me, cls, data, hooks);

                
                if (clsProxy && clsProxy.isProxy) {
                    cls.setProxy(clsProxy);
                }

                
                Ext.ModelManager.onModelDefined(cls);
            });
        };
    },

    inheritableStatics: {
        
        setProxy: function(proxy) {
            
            if (!proxy.isProxy) {
                if (typeof proxy == "string") {
                    proxy = {
                        type: proxy
                    };
                }
                proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
            }
            proxy.setModel(this);
            this.proxy = this.prototype.proxy = proxy;

            return proxy;
        },

        
        getProxy: function() {

            var proxy = this.proxy;

            
            if (!proxy) {
                proxy = this.prototype.proxy;

                
                if (proxy.isProxy) {
                    proxy = proxy.clone()
                }

                return this.setProxy(proxy);
            }

            return proxy;
        },

        
        setFields: function(fields, idProperty, clientIdProperty) {
            var me = this,
                newField,
                idField,
                idFieldDefined = false,
                proto = me.prototype,
                prototypeFields = proto.fields,
                superFields = proto.superclass.fields,
                len,
                i;

            if (idProperty) {
                proto.idProperty = idProperty;
                idField = idProperty.isField ? idProperty : new Ext.data.Field(idProperty);

            }
            if (clientIdProperty) {
                proto.clientIdProperty = clientIdProperty;
            }

            if (prototypeFields) {
                prototypeFields.clear();
            }
            else {
                prototypeFields = me.prototype.fields = new Ext.util.MixedCollection(false, function(field) {
                    return field.name;
                });
            }

            
            if (superFields) {
                fields = superFields.items.concat(fields);
            }

            for (i = 0, len = fields.length; i < len; i++) {
                newField = new Ext.data.Field(fields[i]);

                
                
                
                if (idField && ((newField.mapping && (newField.mapping === idField.mapping)) || (newField.name === idField.name))) {
                    idFieldDefined = true;
                    newField.defaultValue = undefined;
                }
                prototypeFields.add(newField);
            }

            
            
            
            if (idField && !idFieldDefined) {
                idField.defaultValue = undefined;
                prototypeFields.add(idField);
            }

            me.fields = prototypeFields;

            return prototypeFields;
        },

        
        getFields: function() {
            return this.prototype.fields.items;
        },

        
        load: function(id, config) {
            config = Ext.apply({}, config);
            config = Ext.applyIf(config, {
                action: 'read',
                id    : id
            });

            var operation  = new Ext.data.Operation(config),
                scope      = config.scope || this,
                callback;

            callback = function(operation) {
                var record = null,
                    success = operation.wasSuccessful();
                
                if (success) {
                    record = operation.getRecords()[0];
                    
                    if (!record.hasId()) {
                        record.setId(id);
                    }
                    Ext.callback(config.success, scope, [record, operation]);
                } else {
                    Ext.callback(config.failure, scope, [record, operation]);
                }
                Ext.callback(config.callback, scope, [record, operation, success]);
            };

            this.getProxy().read(operation, callback, this);
        }
    },

    statics: {
        
        PREFIX : 'ext-record',
        
        AUTO_ID: 1,
        
        EDIT   : 'edit',
        
        REJECT : 'reject',
        
        COMMIT : 'commit',

        
        id: function(rec) {
            var id = [this.PREFIX, '-', this.AUTO_ID++].join('');
            rec.phantom = true;
            rec.internalId = id;
            return id;
        }
    },

    
    idgen: {
        isGenerator: true,
        type: 'default',

        generate: function () {
            return null;
        },
        getRecId: function (rec) {
            return rec.modelName + '-' + rec.internalId;
        }
    },

    
    editing : false,

    
    dirty : false,

    
    persistenceProperty: 'data',

    evented: false,

    
    isModel: true,

    
    phantom : false,

    
    idProperty: 'id',

    
    clientIdProperty: null,

    
    defaultProxyType: 'ajax',

    
    emptyData: [],

    
    
    

    

    
    
    
    
    

    

    
    constructor: function(data, id, raw, convertedData) {
        
        
        
        
        
        
        

        var me = this,
            passedId = (id || id === 0),
            hasId,
            fields,
            length,
            field,
            name,
            value,
            newId,
            persistenceProperty,
            idProperty = me.idProperty,
            idField = me.idField,
            i;

        
        me.raw = raw || data; 

        
        me.modified = {};

        persistenceProperty = me[me.persistenceProperty] = convertedData || {};

        
        me.data = me[me.persistenceProperty];

        me.mixins.observable.constructor.call(me);

        if (!convertedData) {

            if (data) {
                
                if (!passedId && idProperty) {
                    id = data[idProperty];
                    hasId = (id || id === 0);
                }
            }
            
            else {
                data = me.emptyData;
            } 

            
            fields = me.fields.items;
            length = fields.length;
            i = 0;

            if (Ext.isArray(data)) {
                for (; i < length; i++) {
                    field = fields[i];
                    name  = field.name;

                    
                    
                    value = data[field.originalIndex];

                    if (value === undefined) {
                        value = field.defaultValue;
                    }
                    
                    
                    if (field.convert) {
                        value = field.convert(value, me);
                    }
                    
                    if (value !== undefined) {
                        persistenceProperty[name] = value;
                    }
                }

            } else {
               for (; i < length; i++) {
                    field = fields[i];
                    name  = field.name;
                    value = data[name];
                    if (value === undefined) {
                        value = field.defaultValue;
                    }
                    if (field.convert) {
                        value = field.convert(value, me);
                    }
                    
                    if (value !== undefined) {
                        persistenceProperty[name] = value;
                    }
               }
            }
        }

        
        me.stores = [];

        
        
        if (passedId) {
            hasId = true;
            persistenceProperty[idProperty] = idField && idField.convert ? idField.convert(id) : id;
        }

        
        else if (!hasId) {
            
            newId = me.idgen.generate();
            if (newId != null) {
                me.preventInternalUpdate = true;
                me.setId(newId);
                delete me.preventInternalUpdate;
            }
        }

        
        me.internalId = hasId ? id : Ext.data.Model.id(me);
        

        if (typeof me.init == 'function') {
            me.init();
        }

        
        me.id = me.idgen.getRecId(me);
    },

    
    get: function(field) {
        return this[this.persistenceProperty][field];
    },

    
    
    
    _singleProp: {},

    
    set: function (fieldName, newValue) {
        var me = this,
            data = me[me.persistenceProperty],
            fields = me.fields,
            modified = me.modified,
            single = (typeof fieldName == 'string'),
            currentValue, field, idChanged, key, modifiedFieldNames, name, oldId,
            newId, value, values;

        if (single) {
            values = me._singleProp;
            values[fieldName] = newValue;
        } else {
            values = fieldName;
        }

        for (name in values) {
            if (values.hasOwnProperty(name)) {
                value = values[name];

                if (fields && (field = fields.get(name)) && field.convert) {
                    value = field.convert(value, me);
                }

                currentValue = data[name];
                if (me.isEqual(currentValue, value)) {
                    continue; 
                }

                data[name] = value;
                (modifiedFieldNames || (modifiedFieldNames = [])).push(name);

                if (field && field.persist) {
                    if (modified.hasOwnProperty(name)) {
                        if (me.isEqual(modified[name], value)) {
                            
                            
                            delete modified[name];

                            
                            
                            
                            me.dirty = false;
                            for (key in modified) {
                                if (modified.hasOwnProperty(key)){
                                    me.dirty = true;
                                    break;
                                }
                            }
                        }
                    } else {
                        me.dirty = true;
                        modified[name] = currentValue;
                    }
                }

                if (name == me.idProperty) {
                    idChanged = true;
                    oldId = currentValue;
                    newId = value;
                }
            }
        }

        if (single) {
            
            
            delete values[fieldName];
        }

        if (idChanged) {
            me.changeId(oldId, newId);
        }

        if (!me.editing && modifiedFieldNames) {
            me.afterEdit(modifiedFieldNames);
        }

        return modifiedFieldNames || null;
    },

    
    copyFrom: function(sourceRecord) {
        var me = this,
            fields = me.fields.items,
            fieldCount = fields.length,
            modifiedFieldNames = [],
            field, i = 0,
            myData,
            sourceData,
            idProperty = me.idProperty,
            name,
            value;

        if (sourceRecord) {
            myData = me[me.persistenceProperty];
            sourceData = sourceRecord[sourceRecord.persistenceProperty];
            for (; i < fieldCount; i++) {
                field = fields[i];
                name = field.name;

                
                
                
                
                
                
                if (name != idProperty) {
                    value = sourceData[name];

                    
                    
                    if (value !== undefined && !me.isEqual(myData[name], value)) {
                        myData[name] = value;
                        modifiedFieldNames.push(name);
                    }
                }
            }

            
            if (me.phantom && !sourceRecord.phantom) {
                
                
                me.beginEdit();
                me.setId(sourceRecord.getId());
                me.endEdit(true);
                me.commit(true);
            }
        }
        return modifiedFieldNames;
    },

    
    isEqual: function(a, b) {
        
        if (a instanceof Date && b instanceof Date) {
            return a.getTime() === b.getTime();
        }
        return a === b;
    },

    
    beginEdit : function(){
        var me = this,
            key,
            data,
            o;

        if (!me.editing) {
            me.editing = true;
            me.dirtySave = me.dirty;

            o = me[me.persistenceProperty];
            data = me.dataSave = {};
            for (key in o) {
                if (o.hasOwnProperty(key)) {
                    data[key] = o[key];
                }
            }

            o = me.modified;
            data = me.modifiedSave = {}; 
            for (key in o) {
                if (o.hasOwnProperty(key)) {
                    data[key] = o[key];
                }
            }
        }
    },

    
    cancelEdit : function(){
        var me = this;
        if (me.editing) {
            me.editing = false;
            
            me.modified = me.modifiedSave;
            me[me.persistenceProperty] = me.dataSave;
            me.dirty = me.dirtySave;
            me.modifiedSave = me.dataSave = me.dirtySave = null;
        }
    },

    
    endEdit : function(silent, modifiedFieldNames){
        var me = this,
            dataSave,
            changed;

        silent = silent === true;
        if (me.editing) {
            me.editing = false;
            dataSave = me.dataSave;
            me.modifiedSave = me.dataSave = me.dirtySave = null;
            if (!silent) {
                if (!modifiedFieldNames) {
                    modifiedFieldNames = me.getModifiedFieldNames(dataSave);
                }
                changed = me.dirty || modifiedFieldNames.length > 0;
                if (changed) {
                    me.afterEdit(modifiedFieldNames);
                }
            }
        }
    },

    
    getModifiedFieldNames: function(saved){
        var me = this,
            data = me[me.persistenceProperty],
            modified = [],
            key;

        saved = saved || me.dataSave;
        for (key in data) {
            if (data.hasOwnProperty(key)) {
                if (!me.isEqual(data[key], saved[key])) {
                    modified.push(key);
                }
            }
        }
        return modified; 
    },

    
    getChanges : function(){
        var modified = this.modified,
            changes  = {},
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)){
                changes[field] = this.get(field);
            }
        }

        return changes;
    },

    
    isModified : function(fieldName) {
        return this.modified.hasOwnProperty(fieldName);
    },

    
    setDirty : function() {
        var me     = this,
            fields = me.fields.items,
            fLen   = fields.length,
            field, name, f;

        me.dirty = true;

        for (f = 0; f < fLen; f++) {
            field = fields[f];

            if (field.persist) {
                name  = field.name;
                me.modified[name] = me.get(name);
            }
        }
    },


    
    reject : function(silent) {
        var me = this,
            modified = me.modified,
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)) {
                if (typeof modified[field] != "function") {
                    me[me.persistenceProperty][field] = modified[field];
                }
            }
        }

        me.dirty = false;
        me.editing = false;
        me.modified = {};

        if (silent !== true) {
            me.afterReject();
        }
    },

    
    commit : function(silent, modifiedFieldNames) {
        var me = this;

        me.phantom = me.dirty = me.editing = false;
        me.modified = {};

        if (silent !== true) {
            me.afterCommit(modifiedFieldNames);
        }
    },

    
    copy : function(newId) {
        var me = this;
        return new me.self(me.raw, newId, null, Ext.apply({}, me[me.persistenceProperty]));
    },

    
    setProxy: function(proxy) {
        
        if (!proxy.isProxy) {
            if (typeof proxy === "string") {
                proxy = {
                    type: proxy
                };
            }
            proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
        }
        proxy.setModel(this.self);
        this.proxy = proxy;

        return proxy;
    },

    
    getProxy: function() {
        return this.hasOwnProperty('proxy') ? this.proxy : this.self.getProxy();
    },

    
    validate: function() {
        var errors      = new Ext.data.Errors(),
            validations = this.validations,
            validators  = Ext.data.validations,
            length, validation, field, valid, type, i;

        if (validations) {
            length = validations.length;

            for (i = 0; i < length; i++) {
                validation = validations[i];
                field = validation.field || validation.name;
                type  = validation.type;
                valid = validators[type](validation, this.get(field));

                if (!valid) {
                    errors.add({
                        field  : field,
                        message: validation.message || validators[type + 'Message']
                    });
                }
            }
        }

        return errors;
    },

    
    isValid: function(){
        return this.validate().isValid();
    },

    
    save: function(options) {
        options = Ext.apply({}, options);

        var me     = this,
            action = me.phantom ? 'create' : 'update',
            scope  = options.scope || me,
            stores = me.stores,
            i = 0,
            storeCount,
            store,
            operation,
            callback;

        Ext.apply(options, {
            records: [me],
            action : action
        });

        operation = new Ext.data.Operation(options);

        callback = function(operation) {
            var success = operation.wasSuccessful();
            
            if (success) {
                for(storeCount = stores.length; i < storeCount; i++) {
                    store = stores[i];
                    store.fireEvent('write', store, operation);
                    store.fireEvent('datachanged', store);
                    
                }
                Ext.callback(options.success, scope, [me, operation]);
            }
            else {
                Ext.callback(options.failure, scope, [me, operation]);
            }

            Ext.callback(options.callback, scope, [me, operation, success]);
        };

        me.getProxy()[action](operation, callback, me);

        return me;
    },

    
    destroy: function(options) {
        options = Ext.apply({
            records: [this],
            action : 'destroy'
        }, options);

        var me = this,
            isNotPhantom = me.phantom !== true,
            scope  = options.scope || me,
            stores,
            i = 0,
            storeCount,
            store,
            args,
            operation,
            callback;

        operation = new Ext.data.Operation(options);

        callback = function(operation) {
            args = [me, operation];

            
            stores = Ext.Array.clone(me.stores);
            if (operation.wasSuccessful()) {
                for (storeCount = stores.length; i < storeCount; i++) {
                    store = stores[i];

                    
                    
                    if (store.remove) {
                        store.remove(me, true);
                    }

                    
                    
                    store.fireEvent('bulkremove', store, [me], [store.indexOf(me)], false);
                    if (isNotPhantom) {
                        store.fireEvent('write', store, operation);
                    }
                }
                me.clearListeners();
                Ext.callback(options.success, scope, args);
            } else {
                Ext.callback(options.failure, scope, args);
            }
            Ext.callback(options.callback, scope, args);
        };

        
        
        if (isNotPhantom) {
            me.getProxy().destroy(operation, callback, me);
        }
        
        else {
            operation.complete = operation.success = true;
            operation.resultSet = me.getProxy().reader.nullResultSet;
            callback(operation);
        }
        return me;
    },

    
    getId: function() {
        return this.get(this.idField.name);
    },

    
    getObservableId: function() {
        return this.id;
    },

    
    setId: function(id) {
        this.set(this.idProperty, id);
    },

    changeId: function(oldId, newId) {
        var me = this,
            hasOldId, hasId, oldInternalId;

        if (!me.preventInternalUpdate) { 
            hasOldId = me.hasId(oldId);
            hasId = me.hasId(newId);
            oldInternalId = me.internalId;
            me.phantom  = !hasId;
            
            
            
            
            if (hasId !== hasOldId || (hasId && hasOldId)) {
                me.internalId = hasId ? newId : Ext.data.Model.id(me);
            }

            me.fireEvent('idchanged', me, oldId, newId, oldInternalId);
            me.callStore('onIdChanged', oldId, newId, oldInternalId);
         }
    },

    
    hasId: function(id) {
        if (arguments.length === 0) {
            id = this.getId();
        }
        return !!(id || id === 0);
    },

    
    join : function(store) {
        var me = this;

        
        if (!me.stores.length) {
            me.stores[0] = store;
        } else {
            Ext.Array.include(this.stores, store);
        }

        
        this.store = this.stores[0]; 
    },

    
    unjoin: function(store) {
        Ext.Array.remove(this.stores, store);
        this.store = this.stores[0] || null; 
    },

    
    afterEdit : function(modifiedFieldNames) {
        this.callStore('afterEdit', modifiedFieldNames);
    },

    
    afterReject : function() {
        this.callStore('afterReject');
    },

    
    afterCommit: function(modifiedFieldNames) {
        this.callStore('afterCommit', modifiedFieldNames);
    },

    
    callStore: function(fn) {
        var args = Ext.Array.clone(arguments),
            stores = this.stores,
            i = 0,
            len = stores.length,
            store;

        args[0] = this;
        for (; i < len; ++i) {
            store = stores[i];
            if (store && Ext.isFunction(store[fn])) {
                store[fn].apply(store, args);
            }
        }
    },

    
    getData: function(includeAssociated){
        var me     = this,
            fields = me.fields.items,
            fLen   = fields.length,
            data   = {},
            name, f;

        for (f = 0; f < fLen; f++) {
            name = fields[f].name;
            data[name] = me.get(name);
        }

        if (includeAssociated === true) {
            Ext.apply(data, me.getAssociatedData());
        }
        return data;
    },

    
    getAssociatedData: function(){
        return this.prepareAssociatedData({}, 1);
    },

    
    prepareAssociatedData: function(seenKeys, depth) {
        
        var me               = this,
            associations     = me.associations.items,
            associationCount = associations.length,
            associationData  = {},
            
            
            
            toRead           = [],
            toReadKey        = [],
            toReadIndex      = [],
            associatedStore, associatedRecords, associatedRecord, o, index, result, seenDepth,
            associationId, associatedRecordCount, association, i, j, type, name;

        for (i = 0; i < associationCount; i++) {
            association = associations[i];
            associationId = association.associationId;

            seenDepth = seenKeys[associationId];
            if (seenDepth && seenDepth !== depth) {
                continue;
            }
            seenKeys[associationId] = depth;

            type = association.type;
            name = association.name;
            if (type == 'hasMany') {
                
                associatedStore = me[association.storeName];

                
                associationData[name] = [];

                
                if (associatedStore && associatedStore.getCount() > 0) {
                    associatedRecords = associatedStore.data.items;
                    associatedRecordCount = associatedRecords.length;

                    
                    
                    for (j = 0; j < associatedRecordCount; j++) {
                        associatedRecord = associatedRecords[j];
                        associationData[name][j] = associatedRecord.getData();
                        toRead.push(associatedRecord);
                        toReadKey.push(name);
                        toReadIndex.push(j);
                    }
                }
            } else if (type == 'belongsTo' || type == 'hasOne') {
                associatedRecord = me[association.instanceName];
                
                if (associatedRecord !== undefined) {
                    associationData[name] = associatedRecord.getData();
                    toRead.push(associatedRecord);
                    toReadKey.push(name);
                    toReadIndex.push(-1);
                }
            }
        }

        for (i = 0, associatedRecordCount = toRead.length; i < associatedRecordCount; ++i) {
            associatedRecord = toRead[i];
            o = associationData[toReadKey[i]];
            index = toReadIndex[i];
            result = associatedRecord.prepareAssociatedData(seenKeys, depth + 1);
            if (index === -1) {
                Ext.apply(o, result);
            } else {
                Ext.apply(o[index], result);
            }
        }

        return associationData;
    }
});


Ext.define('Ext.data.proxy.Server', {
    extend:  Ext.data.proxy.Proxy ,
    alias : 'proxy.server',
    alternateClassName: 'Ext.data.ServerProxy',
                                 

    

    
    pageParam: 'page',

    
    startParam: 'start',

    
    limitParam: 'limit',

    
    groupParam: 'group',

    
    groupDirectionParam: 'groupDir',

    
    sortParam: 'sort',

    
    filterParam: 'filter',

    
    directionParam: 'dir',

    
    idParam: 'id',

    
    simpleSortMode: false,

    
    simpleGroupMode: false,

    
    noCache : true,

    
    cacheString: "_dc",

    
    timeout : 30000,

    

    constructor: function(config) {
        var me = this;

        config = config || {};
        
        me.callParent([config]);

        
        me.extraParams = config.extraParams || {};

        me.api = Ext.apply({}, config.api || me.api);
        

        
        me.nocache = me.noCache;
    },

    
    create: function() {
        return this.doRequest.apply(this, arguments);
    },

    read: function() {
        return this.doRequest.apply(this, arguments);
    },

    update: function() {
        return this.doRequest.apply(this, arguments);
    },

    destroy: function() {
        return this.doRequest.apply(this, arguments);
    },

    
    setExtraParam: function(name, value) {
        this.extraParams[name] = value;
    },

    
    buildRequest: function(operation) {
        var me = this,
            
            params = operation.params = Ext.apply({}, operation.params, me.extraParams),
            request;

        
        Ext.applyIf(params, me.getParams(operation));

        
        
        
        if (operation.id !== undefined && params[me.idParam] === undefined) {
            params[me.idParam] = operation.id;
        }

        request = new Ext.data.Request({
            params   : params,
            action   : operation.action,
            records  : operation.records,
            operation: operation,
            url      : operation.url,

            
            
            proxy: me
        });

        request.url = me.buildUrl(request);

        
        operation.request = request;

        return request;
    },

    
    processResponse: function(success, operation, request, response, callback, scope) {
        var me = this,
            reader,
            result;

        if (success === true) {
            reader = me.getReader();

            
            
            
            reader.applyDefaults = operation.action === 'read';

            result = reader.read(me.extractResponseData(response));

            if (result.success !== false) {
                
                Ext.apply(operation, {
                    response: response,
                    resultSet: result
                });

                operation.commitRecords(result.records);
                operation.setCompleted();
                operation.setSuccessful();
            } else {
                operation.setException(result.message);
                me.fireEvent('exception', this, response, operation);
            }
        } else {
            me.setException(operation, response);
            me.fireEvent('exception', this, response, operation);
        }

        
        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }

        me.afterRequest(request, success);
    },

    
    setException: function(operation, response) {
        operation.setException({
            status: response.status,
            statusText: response.statusText
        });
    },

    
    extractResponseData: Ext.identityFn,

    
    applyEncoding: function(value) {
        return Ext.encode(value);
    },

    
    encodeSorters: function(sorters) {
        var min = [],
            length = sorters.length,
            i = 0;

        for (; i < length; i++) {
            min[i] = {
                property : sorters[i].property,
                direction: sorters[i].direction
            };
        }
        return this.applyEncoding(min);

    },

    
    encodeFilters: function(filters) {
        var min = [],
            length = filters.length,
            i = 0;

        for (; i < length; i++) {
            min[i] = {
                property: filters[i].property,
                value   : filters[i].value
            };
        }
        return this.applyEncoding(min);
    },

    
    getParams: function(operation) {
        var me = this,
            params = {},
            isDef = Ext.isDefined,
            groupers = operation.groupers,
            sorters = operation.sorters,
            filters = operation.filters,
            page = operation.page,
            start = operation.start,
            limit = operation.limit,
            simpleSortMode = me.simpleSortMode,
            simpleGroupMode = me.simpleGroupMode,
            pageParam = me.pageParam,
            startParam = me.startParam,
            limitParam = me.limitParam,
            groupParam = me.groupParam,
            groupDirectionParam = me.groupDirectionParam,
            sortParam = me.sortParam,
            filterParam = me.filterParam,
            directionParam = me.directionParam,
            hasGroups, index;

        if (pageParam && isDef(page)) {
            params[pageParam] = page;
        }

        if (startParam && isDef(start)) {
            params[startParam] = start;
        }

        if (limitParam && isDef(limit)) {
            params[limitParam] = limit;
        }

        hasGroups = groupParam && groupers && groupers.length > 0;
        if (hasGroups) {
            
            if (simpleGroupMode) {
                params[groupParam] = groupers[0].property;
                params[groupDirectionParam] = groupers[0].direction || 'ASC';
            } else {
                params[groupParam] = me.encodeSorters(groupers);
            }
        }

        if (sortParam && sorters && sorters.length > 0) {
            if (simpleSortMode) {
                index = 0;
                
                if (sorters.length > 1 && hasGroups) {
                    index = 1;
                }
                params[sortParam] = sorters[index].property;
                params[directionParam] = sorters[index].direction;
            } else {
                params[sortParam] = me.encodeSorters(sorters);
            }

        }

        if (filterParam && filters && filters.length > 0) {
            params[filterParam] = me.encodeFilters(filters);
        }

        return params;
    },

    
    buildUrl: function(request) {
        var me = this,
            url = me.getUrl(request);


        if (me.noCache) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.cacheString, Ext.Date.now()));
        }

        return url;
    },

    
    getUrl: function(request) {
        return request.url || this.api[request.action] || this.url;
    },

    
    doRequest: function(operation, callback, scope) {
    },

    
    afterRequest: Ext.emptyFn,

    onDestroy: function() {
        Ext.destroy(this.reader, this.writer);
    }
});


Ext.define('Ext.data.proxy.Ajax', {
                                                       
    extend:  Ext.data.proxy.Server ,
    alias: 'proxy.ajax',
    alternateClassName: ['Ext.data.HttpProxy', 'Ext.data.AjaxProxy'],
    
    
    actionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'POST',
        destroy: 'POST'
    },

    
    binary: false,
    
    
    
    doRequest: function(operation, callback, scope) {
        var writer  = this.getWriter(),
            request = this.buildRequest(operation);
            
        if (operation.allowWrite()) {
            request = writer.write(request);
        }
        
        Ext.apply(request, {
            binary        : this.binary,
            headers       : this.headers,
            timeout       : this.timeout,
            scope         : this,
            callback      : this.createRequestCallback(request, operation, callback, scope),
            method        : this.getMethod(request),
            disableCaching: false 
        });
        
        Ext.Ajax.request(request);
        
        return request;
    },
    
    
    getMethod: function(request) {
        return this.actionMethods[request.action];
    },
    
    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;
        
        return function(options, success, response) {
            me.processResponse(success, operation, request, response, callback, scope);
        };
    }
}, function() {
    
    Ext.data.HttpProxy = this;
});


Ext.define('Ext.data.proxy.Client', {
    extend:  Ext.data.proxy.Proxy ,
    alternateClassName: 'Ext.data.ClientProxy',
    
    
    isSynchronous: true,

    
    clear: function() {
    }
});


Ext.define('Ext.data.proxy.Memory', {
    extend:  Ext.data.proxy.Client ,
    alias: 'proxy.memory',
    alternateClassName: 'Ext.data.MemoryProxy',

    

    

    constructor: function(config) {
        this.callParent([config]);

        
        this.setReader(this.reader);
    },
    
    
    updateOperation: function(operation, callback, scope) {
        var i = 0,
            recs = operation.getRecords(),
            len = recs.length;
            
        for (i; i < len; i++) {
            recs[i].commit();
        }
        operation.setCompleted();
        operation.setSuccessful();
        
        Ext.callback(callback, scope || this, [operation]);
    },
    
    
    create: function() {
        this.updateOperation.apply(this, arguments);
    },
    
    
    update: function() {
        this.updateOperation.apply(this, arguments);
    },
    
    
    destroy: function() {
        this.updateOperation.apply(this, arguments);
    },

    
    read: function(operation, callback, scope) {
        var me = this,
            resultSet = operation.resultSet = me.getReader().read(me.data),
            records = resultSet.records,
            sorters = operation.sorters,
            groupers = operation.groupers,
            filters = operation.filters;

        operation.setCompleted();

        
        if (resultSet.success) {

            
            if (filters && filters.length) {
                records = resultSet.records = Ext.Array.filter(records, Ext.util.Filter.createFilterFn(filters));
            }

            
            if (groupers && groupers.length) {
                
                sorters = sorters ? sorters.concat(groupers) : sorters;
            }

            
            if (sorters && sorters.length) {
                resultSet.records = Ext.Array.sort(records, Ext.util.Sortable.createComparator(sorters));
            }

            
            
            if (me.enablePaging && operation.start !== undefined && operation.limit !== undefined) {

                
                if (operation.start >= resultSet.total) {
                    resultSet.success = false;
                    resultSet.count = 0;
                    resultSet.records = [];
                }
                
                else {
                    resultSet.records = Ext.Array.slice(resultSet.records, operation.start, operation.start + operation.limit);
                    resultSet.count = resultSet.records.length;
                }
            }
        }

        if (resultSet.success) {
            operation.setSuccessful();
        } else {
            me.fireEvent('exception', me, null, operation);
        }
        Ext.callback(callback, scope || me, [operation]);
    },

    clear: Ext.emptyFn
});


Ext.define('Ext.util.LruCache', {
    extend:  Ext.util.HashMap ,

    

    constructor: function(config) {
        Ext.apply(this, config);
        this.callParent([config]);
    },

    
    add: function(key, newValue) {
        var me = this,
            existingKey = me.findKey(newValue),
            entry;

        
        if (existingKey) {
            me.unlinkEntry(entry = me.map[existingKey]);
            entry.prev = me.last;
            entry.next = null;
        }
        
        else {
            entry = {
                prev: me.last,
                next: null,
                key: key,
                value: newValue
            };
        }

        
        if (me.last) {
            me.last.next = entry;
        }
        
        else {
            me.first = entry;
        }
        me.last = entry;
        me.callParent([key, entry]);
        me.prune();
        return newValue;
    },

    
    insertBefore: function(key, newValue, sibling) {
        var me = this,
            existingKey,
            entry;

        
        
        if (sibling = this.map[this.findKey(sibling)]) {
            existingKey = me.findKey(newValue);

            
            if (existingKey) {
                me.unlinkEntry(entry = me.map[existingKey]);
            }
            
            else {
                entry = {
                    prev: sibling.prev,
                    next: sibling,
                    key: key,
                    value: newValue
                };
            }

            if (sibling.prev) {
                entry.prev.next = entry;
            } else {
                me.first = entry;
            }
            entry.next = sibling;
            sibling.prev = entry;
            me.prune();
            return newValue;
        }
        
        else {
            return me.add(key, newValue);
        }
    },

    
    get: function(key) {
        var entry = this.map[key];
        if (entry) {

            
            if (entry.next) {
                this.moveToEnd(entry);
            }
            return entry.value;
        }
    },

    
    removeAtKey: function(key) {
        this.unlinkEntry(this.map[key]);
        return this.callParent(arguments);
    },

    
    clear: function( initial) {
        this.first = this.last = null;
        return this.callParent(arguments);
    },

    
    unlinkEntry: function(entry) {
        
        if (entry) {
            if (entry.next) {
                entry.next.prev = entry.prev;
            } else {
                this.last = entry.prev;
            }
            if (entry.prev) {
                entry.prev.next = entry.next;
            } else {
                this.first = entry.next;
            }
            entry.prev = entry.next = null;
        }
    },

    
    moveToEnd: function(entry) {
        this.unlinkEntry(entry);

        
        
        if (entry.prev = this.last) {
            this.last.next = entry;
        }
        
        else {
            this.first = entry;
        }
        this.last = entry;
    },

    
    getArray: function(isKey) {
        var arr = [],
            entry = this.first;

        while (entry) {
            arr.push(isKey ? entry.key: entry.value);
            entry = entry.next;
        }
        return arr;
    },

    
    each: function(fn, scope, reverse) {
        var me = this,
            entry = reverse ? me.last : me.first,
            length = me.length;

        scope = scope || me;
        while (entry) {
            if (fn.call(scope, entry.key, entry.value, length) === false) {
                break;
            }
            entry = reverse ? entry.prev : entry.next;
        }
        return me;
    },

    
    findKey: function(value) {
        var key,
            map = this.map;

        for (key in map) {
            
            
            if (map.hasOwnProperty(key) && map[key].value === value) {
                return key;
            }
        }
        return undefined;
    },

    
    clone: function() {
        var newCache = new this.self(this.initialConfig),
            map = this.map,
            key;

        newCache.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                newCache.add(key, map[key].value);
            }
        }
        newCache.resumeEvents();
        return newCache;
    },

    
    prune: function() {
        var me = this,
            purgeCount = me.maxSize ? (me.length - me.maxSize) : 0;

        if (purgeCount > 0) {
            for (; me.first && purgeCount; purgeCount--) {
                me.removeAtKey(me.first.key);
            }
        }
    }

  
  
  
  
});


Ext.define('Ext.data.PageMap', {
    extend:  Ext.util.LruCache ,

    
    clear: function(initial) {
        var me = this;
        me.pageMapGeneration = (me.pageMapGeneration || 0) + 1;
        me.callParent(arguments);
    },

    forEach: function(fn, scope) {
        var me = this,
            pageNumbers = Ext.Object.getKeys(me.map),
            pageCount = pageNumbers.length,
            i, j,
            pageNumber,
            page,
            pageSize;

        for (i = 0; i < pageCount; i++) {
            pageNumbers[i] = Number(pageNumbers[i]);
        }
        Ext.Array.sort(pageNumbers);
        scope = scope || me;
        for (i = 0; i < pageCount; i++) {
            pageNumber = pageNumbers[i];
            page = me.getPage(pageNumber);
            pageSize = page.length;
            for (j = 0; j < pageSize; j++) {
                if (fn.call(scope, page[j], (pageNumber - 1) * me.pageSize + j) === false) {
                    return;
                }
            }
        }
    },

    
    findBy: function(fn, scope) {
        var me = this,
            result = null;

        scope = scope || me;
        me.forEach(function(rec, index) {
            if (fn.call(scope, rec, index)) {
                result = rec;
                return false;
            }
        });
        return result;
    },

    
    findIndexBy: function(fn, scope) {
        var me = this,
            result = -1;

        scope = scope || me;
        me.forEach(function(rec, index) {
            if (fn.call(scope, rec)) {
                result = index;
                return false;
            }
        });
        return result;
    },

    getPageFromRecordIndex: function() {
        return Ext.data.Store.prototype.getPageFromRecordIndex.apply(this, arguments);
    },

    addAll: function(records) {
        this.addPage(1, records);
    },

    addPage: function(pageNumber, records) {
        var me = this,
            lastPage = pageNumber + Math.floor((records.length - 1) / me.pageSize),
            startIdx,
            page;

        
        
        for (startIdx = 0; pageNumber <= lastPage; pageNumber++, startIdx += me.pageSize) {
            page = Ext.Array.slice(records, startIdx, startIdx + me.pageSize);
            me.add(pageNumber, page);
            me.fireEvent('pageAdded', pageNumber, page);
        }
    },

    getCount: function() {
        var result = this.callParent();
        if (result) {
            result = (result - 1) * this.pageSize + this.last.value.length;
        }
        return result;
    },

    indexOf: function(record) {
        return record ? record.index : -1;
    },

    insert: function() {
    },

    remove: function() {
    },

    removeAt: function() {
    },

    getPage: function(pageNumber) {
        return this.get(pageNumber);
    },

    hasRange: function(start, end) {
        var pageNumber = this.getPageFromRecordIndex(start),
            endPageNumber = this.getPageFromRecordIndex(end);

        for (; pageNumber <= endPageNumber; pageNumber++) {
            if (!this.hasPage(pageNumber)) {
                return false;
            }
        }
        return true;
    },

    hasPage: function(pageNumber) {
        
        return !!this.get(pageNumber);
    },

    getAt: function(index) {
        return this.getRange(index, index)[0];
    },

    getRange: function(start, end) {
        if (!this.hasRange(start, end)) {
            Ext.Error.raise('PageMap asked for range which it does not have');
        }
        var me = this,
            startPageNumber = me.getPageFromRecordIndex(start),
            endPageNumber = me.getPageFromRecordIndex(end),
            dataStart = (startPageNumber - 1) * me.pageSize,
            dataEnd = (endPageNumber * me.pageSize) - 1,
            pageNumber = startPageNumber,
            result = [],
            sliceBegin, sliceEnd, doSlice,
            i = 0, len;

        for (; pageNumber <= endPageNumber; pageNumber++) {

            
            if (pageNumber == startPageNumber) {
                sliceBegin = start - dataStart;
                doSlice = true;
            } else {
                sliceBegin = 0;
                doSlice = false;
            }
            if (pageNumber == endPageNumber) {
                sliceEnd = me.pageSize - (dataEnd - end);
                doSlice = true;
            }

            
            if (doSlice) {
                Ext.Array.push(result, Ext.Array.slice(me.getPage(pageNumber), sliceBegin, sliceEnd));
            } else {
                Ext.Array.push(result, me.getPage(pageNumber));
            }
        }

        
        for (len = result.length; i < len; i++) {
            result[i].index = start++;
        }
        return result;
    }
});


Ext.define('Ext.data.Group', {

    extend:  Ext.util.Observable ,

    key: undefined,

    dirty: true,

    constructor: function(){
        this.callParent(arguments);
        this.records = [];    
    },

    contains: function(record){
        return Ext.Array.indexOf(this.records, record) !== -1;
    },

    add: function(records) {
        Ext.Array.push(this.records, records);
        this.dirty = true;  
    },

    remove: function(records) {
        if (!Ext.isArray(records)) {
            records = [records];
        }

        var len = records.length,
            i;

        for (i = 0; i < len; ++i) {
            Ext.Array.remove(this.records, records[i]);
        }
        this.dirty = true;
    },

    isDirty: function(){
        return this.dirty;    
    },

    hasAggregate: function(){
        return !!this.aggregate;
    },

    setDirty: function(){
        this.dirty = true;
    },

    commit: function(){
        this.dirty = false;
    },

    isCollapsed: function(){
        return this.collapsed;    
    },

    getAggregateRecord: function(forceNew){
        var me = this,
            Model;

        if (forceNew === true || me.dirty || !me.aggregate) {
            Model = me.store.model;
            me.aggregate = new Model();
            me.aggregate.isSummary = true;
        }
        return me.aggregate;
    }

});


Ext.define('Ext.data.Store', {
    extend:  Ext.data.AbstractStore ,

    alias: 'store.store',

    
    
    
               
                                
                         
                              
                                
                               
                               
                           
                        
      

           
                           
                          
      

    
    remoteSort: false,

    
    remoteFilter: false,

    
    remoteGroup : false,

    

    

    

    
    groupField: undefined,

    
    groupDir: "ASC",

    
    trailingBufferZone: 25,

    
    leadingBufferZone: 200,

    
    pageSize: undefined,

    
    currentPage: 1,

    
    clearOnPageLoad: true,

    
    loading: false,

    
    sortOnFilter: true,

    
    buffered: false,

    
    purgePageCount: 5,

    
    clearRemovedOnLoad: true,

    defaultPageSize: 25,

    
    defaultViewSize: 100,

    
    addRecordsOptions: {
        addRecords: true
    },

    statics: {
        recordIdFn: function(record) {
            return record.internalId;
        },
        recordIndexFn: function(record) {
            return record.index;
        },
        grouperIdFn: function(grouper) {
            return grouper.id || grouper.property;
        },
        groupIdFn: function(group) {
            return group.key;
        }
    },

    
    constructor: function(config) {
        
        config = Ext.apply({}, config);

        var me = this,
            groupers = config.groupers || me.groupers,
            groupField = config.groupField || me.groupField,
            proxy,
            data;

        
        
        
        
        data = config.data || me.data;

        if (data) {
            me.inlineData = data;
            delete config.data;
        }

        if (!groupers && groupField) {
            groupers = [{
                property : groupField,
                direction: config.groupDir || me.groupDir
            }];
        
            
            if (config.getGroupString || (me.getGroupString !== Ext.data.Store.prototype.getGroupString)) {
                groupers[0].getGroupString = function(record) {
                    return me.getGroupString(record);
                }
            }
        }
        delete config.groupers;

        
        me.groupers = new Ext.util.MixedCollection(false, Ext.data.Store.grouperIdFn);
        me.groupers.addAll(me.decodeGroupers(groupers));

        me.groups = new Ext.util.MixedCollection(false, Ext.data.Store.groupIdFn);

        
        me.callParent([config]);
        

        if (me.buffered) {
            me.data = new Ext.data.PageMap({
                store: me,
                keyFn: Ext.data.Store.recordIdFn,
                pageSize: me.pageSize,
                maxSize: me.purgePageCount,
                listeners: {
                    
                    
                    clear: me.onPageMapClear,
                    scope: me
                }
            });
            me.pageRequests = {};

            
            me.remoteSort = me.remoteGroup = me.remoteFilter = true;

            me.sortOnLoad = false;
            me.filterOnLoad = false;
        } else {
           
            me.data = new Ext.util.MixedCollection({
                getKey: Ext.data.Store.recordIdFn,
                maintainIndices: true
            });
            me.data.pageSize = me.pageSize;
        }

        
        if (me.remoteGroup) {
            me.remoteSort = true;
        }

        
        me.sorters.insert(0, me.groupers.getRange());

        proxy = me.proxy;
        data = me.inlineData;

        
        
        if (!me.buffered && !me.pageSize) {
            me.pageSize = me.defaultPageSize;
        }

        
        if (data) {
            if (proxy instanceof Ext.data.proxy.Memory) {
                proxy.data = data;
                me.read();
            } else {
                me.add.apply(me, [data]);
            }
            
            
            
            if (me.sorters.items.length && !me.remoteSort) {
                me.group(null, null, true);
            }

            delete me.inlineData;
        }
        else if (me.autoLoad) {
            
            Ext.defer(me.load, 1, me, [ typeof me.autoLoad === 'object' ? me.autoLoad : undefined ]);
        }
    },

    onBeforeSort: function() {
        var groupers = this.groupers;
        if (groupers.getCount() > 0) {
            this.sort(groupers.items, 'prepend', false);
        }
    },

    
    decodeGroupers: function(groupers) {
        if (!Ext.isArray(groupers)) {
            if (groupers === undefined) {
                groupers = [];
            } else {
                groupers = [groupers];
            }
        }

        var length = groupers.length,
            Grouper = Ext.util.Grouper,
            config, i, result = [];

        for (i = 0; i < length; i++) {
            config = groupers[i];

            if (!(config instanceof Grouper)) {
                if (Ext.isString(config)) {
                    config = {
                        property: config
                    };
                }

                config = Ext.apply({
                    root     : 'data',
                    direction: "ASC"
                }, config);

                
                if (config.fn) {
                    config.sorterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }

                
                result.push(new Grouper(config));
            } else {
                result.push(config);
            }
        }
        return result;
    },

    
    group: function(groupers, direction,  suppressEvent) {
        var me = this,
            grouper,
            newGroupers;

        
        if (groupers) {

            
            me.sorters.removeAll(me.groupers.items);

            if (Ext.isArray(groupers)) {
                newGroupers = groupers;
            } else if (Ext.isObject(groupers)) {
                newGroupers = [groupers];
            } else if (Ext.isString(groupers)) {
                grouper = me.groupers.get(groupers);

                if (!grouper) {
                    grouper = {
                        property : groupers,
                        direction: direction || 'ASC'
                    };
                    newGroupers = [grouper];
                } else if (direction === undefined) {
                    grouper.toggle();
                } else {
                    grouper.setDirection(direction);
                }
            }

            
            if (newGroupers && newGroupers.length) {
                me.groupers.clear();
                me.groupers.addAll(me.decodeGroupers(newGroupers));
            }

            
            me.sorters.insert(0, me.groupers.items);
        }

        if (me.remoteGroup) {
            if (me.buffered) {
                me.data.clear();
                me.loadPage(1, { groupChange: true });
            } else {
                me.load({
                    scope: me,
                    callback: suppressEvent ? null : me.fireGroupChange
                });
            }
        } else {
            me.doSort(me.generateComparator());
            me.constructGroups();
            if (!suppressEvent) {
                me.fireGroupChange();
            }
        }
    },

    getGroupField: function(){
        var first = this.groupers.first(),
            group;

        if (first) {
            group = first.property;
        }   
        return group; 
    },

    constructGroups: function(){
        var me = this,
            data = this.data.items,
            len = data.length,
            groups = me.groups,
            groupValue, i, group, rec;

        groups.clear();

        if (me.isGrouped()) {
            for (i = 0; i < len; ++i) {
                rec = data[i];
                groupValue = me.getGroupString(rec);
                group = groups.get(groupValue);
                if (!group) {
                    group = new Ext.data.Group({
                        key: groupValue,
                        store: me
                    });
                    groups.add(groupValue, group);
                }
                group.add(rec);
            }
        }
    },

    
    clearGrouping: function() {
        var me       = this,
            groupers = me.groupers.items,
            gLen     = groupers.length,
            g;

        
        for (g = 0; g < gLen; g++) {
            me.sorters.remove(groupers[g]);
        }
        me.groupers.clear();
        if (me.remoteGroup) {
            if (me.buffered) {
                me.data.clear();
                me.loadPage(1, { groupChange: true });
            } else {
                me.load({
                    scope: me,
                    callback: me.fireGroupChange
                });
            }
        } else {
            me.groups.clear();
            if (me.sorters.length) {
                me.sort();
            } else {
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
            }
            me.fireGroupChange();
        }
    },

    
    isGrouped: function() {
        return this.groupers.getCount() > 0;
    },

    
    fireGroupChange: function() {
        this.fireEvent('groupchange', this, this.groupers);
    },

    
    getGroups: function(requestGroupString) {
        var records = this.data.items,
            length = records.length,
            groups = [],
            pointers = {},
            record,
            groupStr,
            group,
            i;

        for (i = 0; i < length; i++) {
            record = records[i];
            groupStr = this.getGroupString(record);
            group = pointers[groupStr];

            if (group === undefined) {
                group = {
                    name: groupStr,
                    children: []
                };

                groups.push(group);
                pointers[groupStr] = group;
            }

            group.children.push(record);
        }

        return requestGroupString ? pointers[requestGroupString] : groups;
    },

    
    getGroupsForGrouper: function(records, grouper) {
        var length = records.length,
            groups = [],
            oldValue,
            newValue,
            record,
            group,
            i;

        for (i = 0; i < length; i++) {
            record = records[i];
            newValue = grouper.getGroupString(record);

            if (newValue !== oldValue) {
                group = {
                    name: newValue,
                    grouper: grouper,
                    records: []
                };
                groups.push(group);
            }

            group.records.push(record);

            oldValue = newValue;
        }

        return groups;
    },

    
    getGroupsForGrouperIndex: function(records, grouperIndex) {
        var me = this,
            groupers = me.groupers,
            grouper = groupers.getAt(grouperIndex),
            groups = me.getGroupsForGrouper(records, grouper),
            length = groups.length,
            i;

        if (grouperIndex + 1 < groupers.length) {
            for (i = 0; i < length; i++) {
                groups[i].children = me.getGroupsForGrouperIndex(groups[i].records, grouperIndex + 1);
            }
        }

        for (i = 0; i < length; i++) {
            groups[i].depth = grouperIndex;
        }

        return groups;
    },

    
    getGroupData: function(sort) {
        var me = this;
        if (sort !== false) {
            me.sort();
        }

        return me.getGroupsForGrouperIndex(me.data.items, 0);
    },

    
    getGroupString: function(instance) {
        var group = this.groupers.first();
        if (group) {
            return group.getGroupString(instance);
        }
        return '';
    },

    
    insert: function(index, records) {
        var me = this,
            sync = false,
            i, len, record,
            defaults = me.modelDefaults,
            out;

        
        if (!Ext.isIterable(records)) {
            out = records = [records];
        } else {
            out = [];
        }
        len = records.length;

        if (len) {
            for (i = 0; i < len; i++) {
                record = records[i];
                if (!record.isModel) {
                    record = me.createModel(record);
                }
                out[i] = record;
                if (defaults) {
                    record.set(defaults);
                }

                record.join(me);
                sync = sync || record.phantom === true;
            }
            
            me.data.insert(index, out);

            if (me.snapshot) {
                me.snapshot.addAll(out);
            }

            if (me.requireSort) {
                
                me.suspendEvents();
                me.sort();
                me.resumeEvents();
            }

            if (me.isGrouped()) {
                me.updateGroupsOnAdd(out);
            }

            me.fireEvent('add', me, out, index);
            me.fireEvent('datachanged', me);
            if (me.autoSync && sync && !me.autoSyncSuspended) {
                me.sync();
            }
        }
        return out;
    },

    updateGroupsOnAdd: function(records) {
        var me = this,
            groups = me.groups,
            len = records.length,
            i, groupName, group, rec;

        for (i = 0; i < len; ++i) {
            rec = records[i];
            groupName = me.getGroupString(rec);
            group = groups.getByKey(groupName);
            if (!group) {
                group = groups.add(new Ext.data.Group({
                    key: groupName,
                    store: me
                }));
            }
            group.add(rec);
        }
    },

    updateGroupsOnRemove: function(records) {
        var me = this,
            groups = me.groups,
            len = records.length,
            i, groupName, group, rec;

        for (i = 0; i < len; ++i) {
            rec = records[i];
            groupName = me.getGroupString(rec);
            group = groups.getByKey(groupName);

            if (group) {
                group.remove(rec);
                if (group.records.length === 0) {
                    groups.remove(group);
                }    
            }
        }
    },

    updateGroupsOnUpdate: function(record, modifiedFieldNames){
        var me = this,
            groupField = me.getGroupField(),
            groupName = me.getGroupString(record),
            groups = me.groups,
            len, i, items, group;

        if (modifiedFieldNames && Ext.Array.indexOf(modifiedFieldNames, groupField) !== -1) {

            
            if (me.buffered) {
                Ext.Error.raise({
                    msg: 'Cannot move records between groups in a buffered store record'
                });
            }

            
            items = groups.items;
            for (i = 0, len = items.length; i < len; ++i) {
                group = items[i];
                if (group.contains(record)) {
                    group.remove(record);
                    break;
                }
            }
            group = groups.getByKey(groupName);
            if (!group) {
                group = groups.add(new Ext.data.Group({
                    key: groupName,
                    store: me
                }));
            }
            group.add(record);

            
            
            me.data.remove(record);
            me.data.insert(me.data.findInsertionIndex(record, me.generateComparator()), record);

            
            for (i = 0, len = this.getCount(); i < len; i++) {
                me.data.items[i].index = i;
            }

        } else {
            
            groups.getByKey(groupName).setDirty();    
        }
    },

    
    add: function(arg) {
        var me = this,
            records,
            length, isSorted;


        
        if (Ext.isArray(arg)) {
            records = arg;
        } else {
            records = arguments;
        }

        length = records.length;
        isSorted = !me.remoteSort && me.sorters && me.sorters.items.length;

        
        
        if (isSorted && length === 1) {
            return [ me.addSorted(me.createModel(records[0])) ];
        }

        
        
        if (isSorted) {
            me.requireSort = true;
        }

        records = me.insert(me.data.length, records);
        delete me.requireSort;

        return records;
    },

    
    addSorted: function(record) {
        var me = this,
            index = me.data.findInsertionIndex(record, me.generateComparator());

        me.insert(index, record);
        return record;
    },

    
    createModel: function(record) {
        if (!record.isModel) {
            record = Ext.ModelManager.create(record, this.model);
        }

        return record;
    },

    onUpdate: function(record, type, modifiedFieldNames){
        if (this.isGrouped()) {
            this.updateGroupsOnUpdate(record, modifiedFieldNames);
        }
    },

    
    each: function(fn, scope) {
        var data = this.data.items,
            dLen = data.length,
            record, d;

        for (d = 0; d < dLen; d++) {
            record = data[d];
            if (fn.call(scope || record, record, d, dLen) === false) {
                break;
            }
        }
    },

    
    remove: function(records,  isMove, silent) {
        
        isMove = isMove === true;

        var me = this,
            sync = false,
            snapshot = me.snapshot,
            data = me.data,
            i = 0,
            length,
            info = [],
            allRecords = [],
            indexes = [],
            item,
            isNotPhantom,
            index,
            record,
            removeRange,
            removeCount,
            fireRemoveEvent = !silent && me.hasListeners.remove;

        
        if (records.isModel) {
            records = [records];
            length = 1;
        }

        
        else if (Ext.isIterable(records)) {
            length = records.length;
        }

        
        
        else if (typeof records === 'object') {
            removeRange = true;
            i = records.start;
            length = records.end + 1;
            removeCount = length - i;
        }

        
        
        if (!removeRange) {
            for (i = 0; i < length; ++i) {

                record = records[i];

                
                if (typeof record == 'number') {
                    index = record;
                    record = data.getAt(index);
                }
                
                else {
                    index = me.indexOf(record);
                }

                
                if (record && index > -1) {
                    info.push({
                        record: record,
                        index: index
                    });
                }

                
                if (snapshot) {
                    snapshot.remove(record);
                }
            }

            
            info = Ext.Array.sort(info, function(o1, o2) {
                var index1 = o1.index,
                    index2 = o2.index;

                return index1 === o2.index2 ? 0 : (index1 < index2 ? -1 : 1);
            });

            
            i = 0;
            length = info.length;
        }

        
        
        
        for (; i < length; i++) {
            if (removeRange) {
                record = data.getAt(i);
                index = i;
            } else {
                item = info[i];
                record = item.record;
                index = item.index;
            }

            allRecords.push(record);
            indexes.push(index);

            isNotPhantom = record.phantom !== true;
            
            if (!isMove && isNotPhantom) {

                
                
                record.removedFrom = index;
                me.removed.push(record);
            }

            record.unjoin(me);

            
            
            index -= i;
            sync = sync || isNotPhantom;

            
            
            if (!removeRange) {
                data.removeAt(index);

                
                if (fireRemoveEvent) {
                    me.fireEvent('remove', me, record, index, !!isMove);
                }
            }
        }

        
        
        if (removeRange) {
            data.removeRange(records.start, removeCount);
        }

        if (!silent) {
            me.fireEvent('bulkremove', me, allRecords, indexes, !!isMove);
            me.fireEvent('datachanged', me);
        }
        if (!isMove && me.autoSync && sync && !me.autoSyncSuspended) {
            me.sync();
        }
    },

    
    removeAt: function(index, count) {
        var me = this,
            storeCount = me.getCount();

        if (index <= storeCount) {
            if (arguments.length === 1) {
                me.remove([ index ]);
            } else if (count) {
                me.remove({
                    start: index,
                    end: Math.min(index + count, storeCount) - 1
                });
            }
        }
    },

    
    removeAll: function(silent) {
        var me = this,
            snapshot = me.snapshot,
            data = me.data;
            
        if (snapshot) {
            snapshot.removeAll(data.getRange());
        }

        if (me.buffered) {
            if (data) {
                if (silent) {
                    me.suspendEvent('clear');
                }
                data.clear();
                if (silent) {
                    me.resumeEvent('clear');
                }
            }            
        }

        else {
            
            
            me.remove({
                start: 0,
                end: me.getCount() - 1
            }, false, silent);
            if (silent !== true) {
                me.fireEvent('clear', me);
            }
        }
    },

    
    load: function(options) {
        var me = this;

        options = options || {};

        if (typeof options == 'function') {
            options = {
                callback: options
            };
        }

        options.groupers = options.groupers ||  me.groupers.items;
        options.page = options.page || me.currentPage;
        options.start = (options.start !== undefined) ? options.start : (options.page - 1) * me.pageSize;
        options.limit = options.limit || me.pageSize;
        options.addRecords = options.addRecords || false;

        if (me.buffered) {
            options.limit = me.viewSize || me.defaultViewSize;
            return me.loadToPrefetch(options);
        }
        return me.callParent([options]);
    },

    reload: function(options) {
        var me = this,
            startIdx,
            endIdx,
            startPage,
            endPage,
            i,
            waitForReload,
            bufferZone,
            records,
            count = me.getCount();

        if (!options) {
            options = {};
        }

        
        
        if (me.buffered) {

            
            delete me.totalCount;

            waitForReload = function() {
                if (me.rangeCached(startIdx, endIdx)) {
                    me.loading = false;
                    me.data.un('pageAdded', waitForReload);
                    records = me.data.getRange(startIdx, endIdx);
                    me.fireEvent('load', me, records, true);
                }
            };
            bufferZone = Math.ceil((me.leadingBufferZone + me.trailingBufferZone) / 2);

            
            startIdx = options.start || (count ? me.getAt(0).index : 0);
            endIdx = startIdx + (options.count || (count ? count : me.pageSize)) - 1;

            
            startPage = me.getPageFromRecordIndex(Math.max(startIdx - bufferZone, 0));
            endPage = me.getPageFromRecordIndex(endIdx + bufferZone);

            
            me.data.clear(true);

            if (me.fireEvent('beforeload', me, options) !== false) {
                me.loading = true;

                
                
                me.data.on('pageAdded', waitForReload);

                
                for (i = startPage; i <= endPage; i++) {
                    me.prefetchPage(i, options);
                }
            }
        } else {
            return me.callParent(arguments);
        }
    },

    
    onProxyLoad: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful();

        if (me.isDestroyed) {
            return;
        }
        
        if (resultSet) {
            me.totalCount = resultSet.total;
        }

        
        
        
        me.loading = false;
        if (successful) {
            me.loadRecords(records, operation);
        }

        if (me.hasListeners.load) {
            me.fireEvent('load', me, records, successful);
        }

        
        
        if (me.hasListeners.read) {
            me.fireEvent('read', me, records, successful);
        }

        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
    },

    
    getNewRecords: function() {
        return this.data.filterBy(this.filterNew).items;
    },

    
    getUpdatedRecords: function() {
        return this.data.filterBy(this.filterUpdated).items;
    },

    
    filter: function(filters, value) {
        if (Ext.isString(filters)) {
            filters = {
                property: filters,
                value: value
            };
        }

        var me = this,
            decoded = me.decodeFilters(filters),
            i,
            doLocalSort = me.sorters.length && me.sortOnFilter && !me.remoteSort,
            length = decoded.length;

        
        for (i = 0; i < length; i++) {
            me.filters.replace(decoded[i]);
        }

        filters = me.filters.items;

        
        
        
        if (filters.length) {
            if (me.remoteFilter) {
                
                delete me.totalCount;

                
                
                
                if (me.buffered) {
                    me.data.clear();
                    me.loadPage(1);
                } else {
                    
                    me.currentPage = 1;
                    
                    me.load();
                }
            } else {
                
                me.snapshot = me.snapshot || me.data.clone();

                
                me.data = me.snapshot.filter(filters);

                
                me.constructGroups();

                if (doLocalSort) {
                    me.sort();
                } else {
                    
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                }
            }
            me.fireEvent('filterchange', me, filters);
        }
    },

    
    clearFilter: function(suppressEvent) {
        var me = this;

        me.filters.clear();

        if (me.remoteFilter) {

            
            if (suppressEvent) {
                return;
            }

            
            delete me.totalCount;

            
            
            
            if (me.buffered) {
                me.data.clear();
                me.loadPage(1);
            } else {
                
                me.currentPage = 1;
                me.load();
            }
        } else if (me.isFiltered()) {
            me.data = me.snapshot;
            delete me.snapshot;

            
            me.constructGroups();

            if (suppressEvent !== true) {
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
            }
        }
        me.fireEvent('filterchange', me, me.filters.items);
    },

    
    removeFilter: function(toRemove, applyFilters) {
        var me = this;

        if (!me.remoteFilter && me.isFiltered()) {
            if (toRemove instanceof Ext.util.Filter) {
                me.filters.remove(toRemove);
            } else {
                me.filters.removeAtKey(toRemove);
            }

            if (applyFilters !== false) {

                
                if (me.filters.length) {
                    me.filter();
                }

                
                else {
                    me.clearFilter();
                }
            } else {
                me.fireEvent('filterchange', me, me.filters.items);
            }
        }
    },

    
    addFilter: function(filters, applyFilters) {
        var me = this,
            decoded,
            i,
            length;

        
        decoded = me.decodeFilters(filters);
        length = decoded.length;
        for (i = 0; i < length; i++) {
            me.filters.replace(decoded[i]);
        }

        if (applyFilters !== false && me.filters.length) {
            me.filter();
        } else {
            me.fireEvent('filterchange', me, me.filters.items);
        }
    },

    
    isFiltered: function() {
        var snapshot = this.snapshot;
        return !!(snapshot && snapshot !== this.data);
    },

    
    filterBy: function(fn, scope) {
        var me = this;

        me.snapshot = me.snapshot || me.data.clone();
        me.data = me.queryBy(fn, scope || me);
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },

    
    queryBy: function(fn, scope) {
        var me = this;
        return (me.snapshot || me.data).filterBy(fn, scope || me);
    },

    
    query: function(property, value, anyMatch, caseSensitive, exactMatch) {
        var me = this,
            queryFn = me.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch),
            results = me.queryBy(queryFn);

        
        if(!results) {
            results = new Ext.util.MixedCollection();
        }

        return results;
    },

    
    loadData: function(data, append) {
        var length = data.length,
            newData = [],
            i;

        
        for (i = 0; i < length; i++) {
            newData.push(this.createModel(data[i]));
        }

        this.loadRecords(newData, append ? this.addRecordsOptions : undefined);
    },

    
    loadRawData : function(data, append) {
         var me      = this,
             result  = me.proxy.reader.read(data),
             records = result.records;

         if (result.success) {
             me.totalCount = result.total;
             me.loadRecords(records, append ? me.addRecordsOptions : undefined);
         }
     },

    
    loadRecords: function(records, options) {
        var me     = this,
            i      = 0,
            length = records.length,
            start,
            addRecords,
            snapshot = me.snapshot;

        if (options) {
            start = options.start;
            addRecords = options.addRecords;
        }

        if (!addRecords) {
            delete me.snapshot;
            me.clearData(true);
        } else if (snapshot) {
            snapshot.addAll(records);
        }

        me.data.addAll(records);

        if (start !== undefined) {
            for (; i < length; i++) {
                records[i].index = start + i;
                records[i].join(me);
            }
        } else {
            for (; i < length; i++) {
                records[i].join(me);
            }
        }

        
        me.suspendEvents();

        if (me.filterOnLoad && !me.remoteFilter) {
            me.filter();
        }

        if (me.sortOnLoad && !me.remoteSort) {
            me.sort(undefined, undefined, undefined, true);
        }

        me.resumeEvents();
        if (me.isGrouped()) {
            me.constructGroups();
        }
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },

    
    
    loadPage: function(page, options) {
        var me = this;

        me.currentPage = page;

        
        options = Ext.apply({
            page: page,
            start: (page - 1) * me.pageSize,
            limit: me.pageSize,
            addRecords: !me.clearOnPageLoad
        }, options);

        if (me.buffered) {
            options.limit = me.viewSize || me.defaultViewSize;
            return me.loadToPrefetch(options);
        }
        me.read(options);
    },

    
    nextPage: function(options) {
        this.loadPage(this.currentPage + 1, options);
    },

    
    previousPage: function(options) {
        this.loadPage(this.currentPage - 1, options);
    },

    
    clearData: function(isLoad) {
        var me = this,
            records,
            i;

        
        
        
        
        
        if (!me.buffered && me.data) {
            records = me.data.items;
            i = records.length;
            while (i--) {
                records[i].unjoin(me);
            }
        }

        
        if (me.data) {
            me.data.clear();
        }
        
        if (isLoad !== true || me.clearRemovedOnLoad) {
            me.removed.length = 0;
        }
    },

    loadToPrefetch: function(options) {
        var me = this,
            i,
            records,
            dataSetSize,
            prefetchOptions = options,

            
            startIdx = options.start,
            endIdx = options.start + options.limit - 1,

            
            loadEndIdx = Math.min(endIdx, options.start + (me.viewSize || options.limit) - 1),

            
            
            startPage = me.getPageFromRecordIndex(Math.max(startIdx - me.trailingBufferZone, 0)),
            endPage = me.getPageFromRecordIndex(endIdx + me.leadingBufferZone),

            
            waitForRequestedRange = function() {
                if (me.rangeCached(startIdx, loadEndIdx)) {
                    me.loading = false;
                    records = me.data.getRange(startIdx, loadEndIdx);
                    me.data.un('pageAdded', waitForRequestedRange);

                    
                    if (me.hasListeners.guaranteedrange) {
                        me.guaranteeRange(startIdx, loadEndIdx, options.callback, options.scope);
                    }
                    if (options.callback) {
                        options.callback.call(options.scope||me, records, startIdx, endIdx, options);
                    }
                    me.fireEvent('datachanged', me);
                    me.fireEvent('refresh', me);
                    me.fireEvent('load', me, records, true);
                    if (options.groupChange) {
                        me.fireGroupChange();
                    }
                }
            };

        if (me.fireEvent('beforeload', me, options) !== false) {

            
            delete me.totalCount;

            me.loading = true;

            
            
            if (options.callback) {
                prefetchOptions = Ext.apply({}, options);
                delete prefetchOptions.callback;
            }

            
            
            
            
            me.on('prefetch', function(store, records, successful, operation) {

                if (successful) {
                    
                    
                    if ((dataSetSize = me.getTotalCount())) {

                        
                        me.data.on('pageAdded', waitForRequestedRange);

                        
                        loadEndIdx = Math.min(loadEndIdx, dataSetSize - 1);

                        
                        endPage = me.getPageFromRecordIndex(Math.min(loadEndIdx + me.leadingBufferZone, dataSetSize - 1));

                        for (i = startPage + 1; i <= endPage; ++i) {
                            me.prefetchPage(i, prefetchOptions);
                        }
                    } else {
                        me.fireEvent('datachanged', me);
                        me.fireEvent('refresh', me);
                        me.fireEvent('load', me, records, true);
                    }
                }
                
                else {
                    me.fireEvent('load', me, records, false);
                }
            }, null, {single: true});

            me.prefetchPage(startPage, prefetchOptions);
        }
    },

    
    
    prefetch: function(options) {
        var me = this,
            pageSize = me.pageSize,
            proxy,
            operation;

        
        if (pageSize) {
            if (me.lastPageSize && pageSize != me.lastPageSize) {
                Ext.Error.raise("pageSize cannot be dynamically altered");
            }
            if (!me.data.pageSize) {
                me.data.pageSize = pageSize;
            }
        }

        
        else {
            me.pageSize = me.data.pageSize = pageSize = options.limit;
        }

        
        me.lastPageSize = pageSize;

        
        if (!options.page) {
            options.page = me.getPageFromRecordIndex(options.start);
            options.start = (options.page - 1) * pageSize;
            options.limit = Math.ceil(options.limit / pageSize) * pageSize;
        }

        
        if (!me.pageRequests[options.page]) {

            
            options = Ext.apply({
                action : 'read',
                filters: me.filters.items,
                sorters: me.sorters.items,
                groupers: me.groupers.items,

                
                
                pageMapGeneration: me.data.pageMapGeneration
            }, options);

            operation = new Ext.data.Operation(options);

            if (me.fireEvent('beforeprefetch', me, operation) !== false) {
                proxy = me.proxy;
                me.pageRequests[options.page] = proxy.read(operation, me.onProxyPrefetch, me);
                if (proxy.isSynchronous) {
                    delete me.pageRequests[options.page];
                }
            }
        }

        return me;
    },

    
    onPageMapClear: function() {
        var me = this,
            loadingFlag = me.wasLoading,
            reqs = me.pageRequests,
            req,
            page;

        
        if (me.data.events.pageadded) {
            me.data.events.pageadded.clearListeners();
        }

        
        
        
        me.loading = true;
        me.totalCount = 0;

        
        for (page in reqs) {
            if (reqs.hasOwnProperty(page)) {
                req = reqs[page];
                delete reqs[page];
                delete req.callback;
            }
        }

        
        me.fireEvent('clear', me);

        
        
        me.loading = loadingFlag;
    },

    
    prefetchPage: function(page, options) {
        var me = this,
            pageSize = me.pageSize || me.defaultPageSize,
            start = (page - 1) * me.pageSize,
            total = me.totalCount;

        
        if (total !== undefined && me.getCount() === total) {
            return;
        }

        
        me.prefetch(Ext.applyIf({
            page     : page,
            start    : start,
            limit    : pageSize
        }, options));
    },

    
    onProxyPrefetch: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful(),
            page = operation.page;

        
        
        if (operation.pageMapGeneration === me.data.pageMapGeneration) {

            if (resultSet) {
                me.totalCount = resultSet.total;
                me.fireEvent('totalcountchange', me.totalCount);
            }

            
            if (page !== undefined) {
                delete me.pageRequests[page];
            }

            
            me.loading = false;
            me.fireEvent('prefetch', me, records, successful, operation);

            
            
            if (successful) {
                me.cachePage(records, operation.page);
            }

            
            Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
        }
    },

    
    cachePage: function(records, page) {
        var me = this,
            len = records.length, i;

        if (!Ext.isDefined(me.totalCount)) {
            me.totalCount = records.length;
            me.fireEvent('totalcountchange', me.totalCount);
        }

        
        for (i = 0; i < len; i++) {
            records[i].join(me);
        }
        me.data.addPage(page, records);
    },

    
    rangeCached: function(start, end) {
        return this.data && this.data.hasRange(start, end);
    },

    
    pageCached: function(page) {
        return this.data && this.data.hasPage(page);
    },
    
    
    pagePending: function(page) {
        return !!this.pageRequests[page];
    },

    
    rangeSatisfied: function(start, end) {
        return this.rangeCached(start, end);
    },

    
    getPageFromRecordIndex: function(index) {
        return Math.floor(index / this.pageSize) + 1;
    },

    
    onGuaranteedRange: function(options) {
        var me = this,
            totalCount = me.getTotalCount(),
            start = options.prefetchStart,
            end = (options.prefetchEnd > totalCount - 1) ? totalCount - 1 : options.prefetchEnd,
            range;

        end = Math.max(0, end);


        range = me.data.getRange(start, end);
        if (options.fireEvent !== false) {
            me.fireEvent('guaranteedrange', range, start, end, options);
        }
        if (options.callback) {
            options.callback.call(options.scope || me, range, start, end, options);
        }
    },

    
    guaranteeRange: function(start, end, callback, scope, options) {
         options = Ext.apply({
             callback: callback,
             scope: scope
         }, options);
         this.getRange(start, end, options)
     },

    
    prefetchRange: function(start, end) {
        var me = this,
            startPage, endPage, page;
        if (!me.rangeCached(start, end)) {
            startPage = me.getPageFromRecordIndex(start);
            endPage = me.getPageFromRecordIndex(end);

            
            
            
            me.data.maxSize = me.purgePageCount ? (endPage - startPage + 1) + me.purgePageCount : 0;

            
            for (page = startPage; page <= endPage; page++) {
                if (!me.pageCached(page)) {
                    me.prefetchPage(page);
                }
            }
        }
    },

    primeCache: function(start, end, direction) {
        var me = this;

        
        if (direction === -1) {
            start = Math.max(start - me.leadingBufferZone, 0);
            end   = Math.min(end   + me.trailingBufferZone, me.totalCount - 1);
        }
        
        else if (direction === 1) {
            start = Math.max(Math.min(start - me.trailingBufferZone, me.totalCount - me.pageSize), 0);
            end   = Math.min(end + me.leadingBufferZone, me.totalCount - 1);
        }
        
        else {
            start = Math.min(Math.max(Math.floor(start - ((me.leadingBufferZone + me.trailingBufferZone) / 2)), 0), me.totalCount - me.pageSize);
            end =   Math.min(Math.max(Math.ceil (end   + ((me.leadingBufferZone + me.trailingBufferZone) / 2)), 0), me.totalCount - 1);
        }
        me.prefetchRange(start, end);
    },

    
    
    sort: function() {
        var me = this;

        if (me.buffered && me.remoteSort) {
            me.data.clear();
        }
        return me.callParent(arguments);
    },

    
    
    
    doSort: function(sorterFn) {
        var me = this,
            range,
            ln,
            i;

        if (me.remoteSort) {

            
            
            
            if (me.buffered) {
                me.data.clear();
                me.loadPage(1);
            } else {
                
                me.load();
            }
        } else {
            me.data.sortBy(sorterFn);
            if (!me.buffered) {
                range = me.getRange();
                ln = range.length;
                for (i = 0; i < ln; i++) {
                    range[i].index = i;
                }
            }
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
    },

    
    find: function(property, value, start, anyMatch, caseSensitive, exactMatch) {
        var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
        return fn ? this.data.findIndexBy(fn, null, start) : -1;
    },

    
    findRecord: function() {
        var me = this,
            index = me.find.apply(me, arguments);
        return index !== -1 ? me.getAt(index) : null;
    },

    
    createFilterFn: function(property, value, anyMatch, caseSensitive, exactMatch) {
        if (Ext.isEmpty(value)) {
            return false;
        }
        value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
        return function(r) {
            return value.test(r.data[property]);
        };
    },

    
    findExact: function(property, value, start) {
        return this.data.findIndexBy(function(rec) {
            return rec.isEqual(rec.get(property), value);
        },
        this, start);
    },

    
    findBy: function(fn, scope, start) {
        return this.data.findIndexBy(fn, scope, start);
    },

    
    collect: function(dataIndex, allowNull, bypassFilter) {
        var me = this,
            data = (bypassFilter === true && me.snapshot) ? me.snapshot : me.data;

        return data.collect(dataIndex, 'data', allowNull);
    },

    
    getCount: function() {
        return this.data.getCount();
    },

    
    getTotalCount: function() {
        return this.totalCount || 0;
    },

    
    getAt: function(index) {
        return this.data.getAt(index);
    },

    
    getRange: function(start, end, options) {

        var me = this,
            requiredStart,
            requiredEnd,
            maxIndex = me.totalCount - 1,
            lastRequestStart = me.lastRequestStart,
            pageAddHandler,
            result;

        options = Ext.apply({
            prefetchStart: start,
            prefetchEnd: end
        }, options);

        if (me.buffered) {
            
            end = (end >= me.totalCount) ? maxIndex : end;

            
            
            
            
            requiredStart = start === 0 ? 0 : start - 1;
            requiredEnd = end === maxIndex ? end : end + 1;

            
            me.lastRequestStart = start;

            
            if (me.rangeCached(requiredStart, requiredEnd)) {
                me.onGuaranteedRange(options);
                result = me.data.getRange(start, end);
            }
            
            else {
                
                me.fireEvent('cachemiss', me, start, end);

                
                pageAddHandler = function(page, records) {
                    if (me.rangeCached(requiredStart, requiredEnd)) {
                        
                        me.fireEvent('cachefilled', me, start, end);
                        me.data.un('pageAdded', pageAddHandler);
                        me.onGuaranteedRange(options);
                    }
                };
                me.data.on('pageAdded', pageAddHandler);

                
                
                
                me.prefetchRange(start, end);

            }
            
            me.primeCache(start, end, start < lastRequestStart ? -1 : 1);
        } else {
            result = me.data.getRange(start, end);

            
            if (options.callback) {
                options.callback.call(options.scope || me, result, start, end, options)
            }
        }

        return result;
    },

    
    getById: function(id) {
        var result = (this.snapshot || this.data).findBy(function(record) {
            return record.getId() === id;
        });
        return result;
    },

    
    indexOf: function(record) {
        return this.data.indexOf(record);
    },

    
    indexOfTotal: function(record) {
        var index = record.index;
        if (index || index === 0) {
            return index;
        }
        return this.indexOf(record);
    },

    
    indexOfId: function(id) {
        return this.indexOf(this.getById(id));
    },

    

    
    first: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                return records.length ? records[0] : undefined;
            }, me, true);
        } else {
            return me.data.first();
        }
    },

    
    last: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                var len = records.length;
                return len ? records[len - 1] : undefined;
            }, me, true);
        } else {
            return me.data.last();
        }
    },

    
    sum: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getSum, me, true, [field]);
        } else {
            return me.getSum(me.data.items, field);
        }
    },

    
    getSum: function(records, field) {
        var total = 0,
            i = 0,
            len = records.length;

        for (; i < len; ++i) {
            total += records[i].get(field);
        }

        return total;
    },

    
    count: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                return records.length;
            }, me, true);
        } else {
            return me.getCount();
        }
    },

    
    min: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getMin, me, true, [field]);
        } else {
            return me.getMin(me.data.items, field);
        }
    },

    
    getMin: function(records, field) {
        var i = 1,
            len = records.length,
            value, min;

        if (len > 0) {
            min = records[0].get(field);
        }

        for (; i < len; ++i) {
            value = records[i].get(field);
            if (value < min) {
                min = value;
            }
        }
        return min;
    },

    
    max: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getMax, me, true, [field]);
        } else {
            return me.getMax(me.data.items, field);
        }
    },

    
    getMax: function(records, field) {
        var i = 1,
            len = records.length,
            value,
            max;

        if (len > 0) {
            max = records[0].get(field);
        }

        for (; i < len; ++i) {
            value = records[i].get(field);
            if (value > max) {
                max = value;
            }
        }
        return max;
    },

    
    average: function(field, grouped) {
        var me = this;
        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getAverage, me, true, [field]);
        } else {
            return me.getAverage(me.data.items, field);
        }
    },

    
    getAverage: function(records, field) {
        var i = 0,
            len = records.length,
            sum = 0;

        if (records.length > 0) {
            for (; i < len; ++i) {
                sum += records[i].get(field);
            }
            return sum / len;
        }
        return 0;
    },

    
    aggregate: function(fn, scope, grouped, args) {
        args = args || [];
        if (grouped && this.isGrouped()) {
            var groups = this.getGroups(),
                len = groups.length,
                out = {},
                group, i;

            for (i = 0; i < len; ++i) {
                group = groups[i];
                out[group.name] = this.getAggregate(fn, scope || this, group.children, args);
            }
            return out;
        } else {
            return this.getAggregate(fn, scope, this.data.items, args);
        }
    },

    getAggregate: function(fn, scope, records, args){
        args = args || [];
        return fn.apply(scope || this, [records].concat(args));
    },

    onIdChanged: function(rec, oldId, newId, oldInternalId){
        var snapshot = this.snapshot;
        if (snapshot) {
            snapshot.updateKey(oldInternalId, newId);
        }
        this.data.updateKey(oldInternalId, newId);
        this.callParent(arguments);
    },

    
    commitChanges : function(){
        var me = this,
            recs = me.getModifiedRecords(),
            len = recs.length,
            i = 0;

        for (; i < len; i++){
            recs[i].commit();
        }

        
        
        me.removed.length = 0;
    },

    filterNewOnly: function(item){
        return item.phantom === true;
    },

    
    
    
    getRejectRecords: function() {
        
        return Ext.Array.push(this.data.filterBy(this.filterNewOnly).items, this.getUpdatedRecords());
    },

    
    rejectChanges : function() {
        var me = this,
            recs = me.getRejectRecords(),
            len = recs.length,
            i = 0,
            rec;

        for (; i < len; i++) {
            rec = recs[i];
            rec.reject();
            if (rec.phantom) {
                me.remove(rec);
            }
        }

        
        recs = me.removed;
        len = recs.length;
        for (i = 0; i < len; i++) {
            rec = recs[i];
            me.insert(rec.removedFrom || 0, rec);
            rec.reject();
        }

        
        
        me.removed.length = 0;
    }
}, function() {
    
    
    
    Ext.regStore('ext-empty-store', {fields: [], proxy: 'memory'});
});


Ext.define('Ext.data.reader.Array', {
    extend:  Ext.data.reader.Json ,
    alternateClassName: 'Ext.data.ArrayReader',
    alias : 'reader.array',

    
    totalProperty: undefined,
    successProperty: undefined,

    
    createFieldAccessExpression: function(field, fieldVarName, dataName) {
            
            
        var index  = (field.mapping == null) ? field.originalIndex : field.mapping,
            result;

        if (typeof index === 'function') {
            result = fieldVarName + '.mapping(' + dataName + ', this)';
        } else {
            if (isNaN(index)) {
                index = '"' + index + '"';
            }
            result = dataName + "[" + index + "]";
        }
        return result;
    }
});


Ext.define('Ext.data.ArrayStore', {
    extend:  Ext.data.Store ,
    alias: 'store.array',
               
                                
                               
      

    constructor: function(config) {
        config = Ext.apply({
            proxy: {
                type: 'memory',
                reader: 'array'
            }
        }, config);
        this.callParent([config]);
    },

    loadData: function(data, append) {
        if (this.expandData === true) {
            var r = [],
                i = 0,
                ln = data.length;

            for (; i < ln; i++) {
                r[r.length] = [data[i]];
            }

            data = r;
        }

        this.callParent([data, append]);
    }
}, function() {
    
    Ext.data.SimpleStore = Ext.data.ArrayStore;
    
});


Ext.define('Ext.data.Batch', {
    mixins: {
        observable:  Ext.util.Observable 
    },

    
    autoStart: false,
    
    
    pauseOnException: false,

    
    current: -1,

    
    total: 0,

    
    isRunning: false,

    
    isComplete: false,

    
    hasException: false,

    
    constructor: function(config) {
        var me = this;

        

        

        

        me.mixins.observable.constructor.call(me, config);

        
        me.operations = [];
        
        
        me.exceptions = [];
    },

    
    add: function(operation) {
        this.total++;

        operation.setBatch(this);

        this.operations.push(operation);
        
        return this;
    },

    
    start: function( index) {
        var me = this;
        
        if (me.isRunning) {
            return me;
        }
        
        me.exceptions.length = 0;
        me.hasException = false;
        me.isRunning = true;

        return me.runOperation(Ext.isDefined(index) ? index : me.current + 1);
    },
    
    
    retry: function() {
        return this.start(this.current);
    },

    
    runNextOperation: function() {
        return this.runOperation(this.current + 1);
    },

    
    pause: function() {
        this.isRunning = false;
        return this;
    },

    
    runOperation: function(index) {
        var me = this,
            operations = me.operations,
            operation = operations[index],
            onProxyReturn;

        if (operation === undefined) {
            me.isRunning = false;
            me.isComplete = true;
            me.fireEvent('complete', me, operations[operations.length - 1]);
        } else {
            me.current = index;

            onProxyReturn = function(operation) {
                var hasException = operation.hasException();

                if (hasException) {
                    me.hasException = true;
                    me.exceptions.push(operation);
                    me.fireEvent('exception', me, operation);
                }

                if (hasException && me.pauseOnException) {
                    me.pause();
                } else {
                    operation.setCompleted();
                    me.fireEvent('operationcomplete', me, operation);
                    me.runNextOperation();
                }
            };

            operation.setStarted();

            me.proxy[operation.action](operation, onProxyReturn, me);
        }
        
        return me;
    }
});


Ext.define('Ext.data.BufferStore', {
    extend:  Ext.data.Store ,
    alias: 'store.buffer',
    sortOnLoad: false,
    filterOnLoad: false,
    
    constructor: function() {
        Ext.Error.raise('The BufferStore class has been deprecated. Instead, specify the buffered config option on Ext.data.Store');
    }
});



Ext.define('Ext.direct.Manager', {
    singleton: true,

               
                                   
                               
      

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    exceptions: {
        TRANSPORT: 'xhr',
        PARSE: 'parse',
        DATA: 'data',
        LOGIN: 'login',
        SERVER: 'exception'
    },
    
    constructor: function() {
        var me = this;

        me.addEvents(
            
            'event',
            
            
            'exception'
        );
        
        me.transactions = new Ext.util.MixedCollection();
        me.providers    = new Ext.util.MixedCollection();

        me.mixins.observable.constructor.call(me);
    },

    
    addProvider: function(provider) {
        var me = this,
            args = arguments,
            relayers = me.relayers || (me.relayers = {}),
            i, len;

        if (args.length > 1) {
            for (i = 0, len = args.length; i < len; ++i) {
                me.addProvider(args[i]);
            }
            
            return;
        }

        
        if (!provider.isProvider) {
            provider = Ext.create('direct.' + provider.type + 'provider', provider);
        }
        
        me.providers.add(provider);
        provider.on('data', me.onProviderData, me);
        
        if (provider.relayedEvents) {
            relayers[provider.id] = me.relayEvents(provider, provider.relayedEvents);
        }

        if (!provider.isConnected()) {
            provider.connect();
        }

        return provider;
    },

    
    getProvider: function(id) {
        return id.isProvider ? id : this.providers.get(id);
    },

    
    removeProvider: function(provider) {
        var me = this,
            providers = me.providers,
            relayers = me.relayers,
            id;

        provider = provider.isProvider ? provider : providers.get(provider);

        if (provider) {
            provider.un('data', me.onProviderData, me);

            id = provider.id;
            
            if (relayers[id]) {
                relayers[id].destroy();
                delete relayers[id];
            }
            
            providers.remove(provider);
            
            return provider;
        }
        
        return null;
    },

    
    addTransaction: function(transaction) {
        this.transactions.add(transaction);
        
        return transaction;
    },

    
    removeTransaction: function(transaction) {
        var me = this;
        
        transaction = me.getTransaction(transaction);
        me.transactions.remove(transaction);
        
        return transaction;
    },

    
    getTransaction: function(transaction) {
        return typeof transaction === 'object' ? transaction : this.transactions.get(transaction);
    },

    onProviderData: function(provider, event) {
        var me = this,
            i, len;

        if (Ext.isArray(event)) {
            for (i = 0, len = event.length; i < len; ++i) {
                me.onProviderData(provider, event[i]);
            }
            
            return;
        }
        
        if (event.name && event.name != 'event' && event.name != 'exception') {
            me.fireEvent(event.name, event);
        }
        else if (event.status === false) {
            me.fireEvent('exception', event);
        }
        
        me.fireEvent('event', event, provider);
    },
    
    
    parseMethod: function(fn) {
        if (Ext.isString(fn)) {
            var parts = fn.split('.'),
                i = 0,
                len = parts.length,
                current = Ext.global;
                
            while (current && i < len) {
                current = current[parts[i]];
                ++i;
            }
            
            fn = Ext.isFunction(current) ? current : null;
        }
        
        return fn || null;
    }
    
}, function() {
    
    Ext.Direct = Ext.direct.Manager;
});


Ext.define('Ext.data.proxy.Direct', {
    

    extend:  Ext.data.proxy.Server ,
    alternateClassName: 'Ext.data.DirectProxy',

    alias: 'proxy.direct',

                                     

    

    
    paramOrder: undefined,

    
    paramsAsHash: true,

    
    directFn : undefined,

    

    

    
    paramOrderRe: /[\s,|]/,

    constructor: function(config){
        var me = this,
            paramOrder;
            
        me.callParent(arguments);
        
        paramOrder = me.paramOrder;
        if (Ext.isString(paramOrder)) {
            me.paramOrder = paramOrder.split(me.paramOrderRe);
        }
    },
    
    resolveMethods: function() {
        var me = this,
            fn = me.directFn,
            api = me.api,
            Manager = Ext.direct.Manager,
            method;
        
        if (fn) {
            method = me.directFn = Manager.parseMethod(fn);
            
            if (!Ext.isFunction(method)) {
                Ext.Error.raise('Cannot resolve directFn ' + fn);
            }
        }
        else if (api) {
            for (fn in api) {
                if (api.hasOwnProperty(fn)) {
                    method = api[fn];
                    api[fn] = Manager.parseMethod(method);
                    
                    if (!Ext.isFunction(api[fn])) {
                        Ext.Error.raise('Cannot resolve Direct api ' + fn + ' method ' + method);
                    }
                }
            }
        }
        
        me.methodsResolved = true;
    },

    doRequest: function(operation, callback, scope) {
        var me = this,
            writer = me.getWriter(),
            request = me.buildRequest(operation),
            params = request.params,
            args = [],
            fn, method;
        
        if (!me.methodsResolved) {
            me.resolveMethods();
        }

        fn = me.api[request.action] || me.directFn;
        

        if (operation.allowWrite()) {
            request = writer.write(request);
        }

        if (operation.action == 'read') {
            
            method = fn.directCfg.method;
            args = method.getArgs(params, me.paramOrder, me.paramsAsHash);
        } else {
            args.push(request.jsonData);
        }

        Ext.apply(request, {
            args: args,
            directFn: fn
        });
        args.push(me.createRequestCallback(request, operation, callback, scope), me);
        fn.apply(window, args);
    },

    
    applyEncoding: Ext.identityFn,

    createRequestCallback: function(request, operation, callback, scope){
        var me = this;

        return function(data, event){
            me.processResponse(event.status, operation, request, event, callback, scope);
        };
    },

    
    extractResponseData: function(response){
        return Ext.isDefined(response.result) ? response.result : response.data;
    },

    
    setException: function(operation, response) {
        operation.setException(response.message);
    },

    
    buildUrl: function(){
        return '';
    }
});


Ext.define('Ext.data.DirectStore', {
    
    
    extend:  Ext.data.Store ,
    
    alias: 'store.direct',
    
                                        
   
    

    constructor : function(config){
        config = Ext.apply({}, config);
        if (!config.proxy) {
            var proxy = {
                type: 'direct',
                reader: {
                    type: 'json'
                }
            };
            Ext.copyTo(proxy, config, 'paramOrder,paramsAsHash,directFn,api,simpleSortMode');
            Ext.copyTo(proxy.reader, config, 'totalProperty,root,idProperty');
            config.proxy = proxy;
        }
        this.callParent([config]);
    }    
});


Ext.define('Ext.data.JsonP', {

    

    singleton: true,

    

    
    requestCount: 0,

    
    requests: {},

    
    timeout: 30000,

    
    disableCaching: true,

    
    disableCachingParam: '_dc',

    
    callbackKey: 'callback',

    
    request: function(options) {
        options = Ext.apply({}, options);


        var me = this,
            disableCaching = Ext.isDefined(options.disableCaching) ? options.disableCaching : me.disableCaching,
            cacheParam = options.disableCachingParam || me.disableCachingParam,
            id = ++me.requestCount,
            callbackName = options.callbackName || 'callback' + id,
            callbackKey = options.callbackKey || me.callbackKey,
            timeout = Ext.isDefined(options.timeout) ? options.timeout : me.timeout,
            params = Ext.apply({}, options.params),
            url = options.url,
            name = Ext.name,
            request,
            script;


        
        if (disableCaching && !params[cacheParam]) {
            params[cacheParam] = Ext.Date.now();
        }
        options.params = params;

        params[callbackKey] = name + '.data.JsonP.' + callbackName;
        script = me.createScript(url, params, options);

        me.requests[id] = request = {
            url: url,
            params: params,
            script: script,
            id: id,
            scope: options.scope,
            success: options.success,
            failure: options.failure,
            callback: options.callback,
            callbackKey: callbackKey,
            callbackName: callbackName
        };

        if (timeout > 0) {
            request.timeout = setTimeout(Ext.bind(me.handleTimeout, me, [request]), timeout);
        }

        me.setupErrorHandling(request);
        me[callbackName] = Ext.bind(me.handleResponse, me, [request], true);
        me.loadScript(request);
        return request;
    },

    
    abort: function(request){
        var me = this,
            requests = me.requests,
            key;

        if (request) {
            if (!request.id) {
                request = requests[request];
            }
            me.handleAbort(request);
        } else {
            for (key in requests) {
                if (requests.hasOwnProperty(key)) {
                    me.abort(requests[key]);
                }
            }
        }
    },

    
    setupErrorHandling: function(request){
        request.script.onerror = Ext.bind(this.handleError, this, [request]);
    },

    
    handleAbort: function(request){
        request.errorType = 'abort';
        this.handleResponse(null, request);
    },

    
    handleError: function(request){
        request.errorType = 'error';
        this.handleResponse(null, request);
    },

    
    cleanupErrorHandling: function(request){
        request.script.onerror = null;
    },

    
    handleTimeout: function(request){
        request.errorType = 'timeout';
        this.handleResponse(null, request);
    },

    
    handleResponse: function(result, request){

        var success = true;

        if (request.timeout) {
            clearTimeout(request.timeout);
        }
        delete this[request.callbackName];
        delete this.requests[request.id];
        this.cleanupErrorHandling(request);
        Ext.fly(request.script).remove();

        if (request.errorType) {
            success = false;
            Ext.callback(request.failure, request.scope, [request.errorType]);
        } else {
            Ext.callback(request.success, request.scope, [result]);
        }
        Ext.callback(request.callback, request.scope, [success, result, request.errorType]);
        Ext.EventManager.idleEvent.fire();
    },

    
    createScript: function(url, params, options) {
        var script = document.createElement('script');
        script.setAttribute("src", Ext.urlAppend(url, Ext.Object.toQueryString(params)));
        script.setAttribute("async", true);
        script.setAttribute("type", "text/javascript");
        return script;
    },

    
    loadScript: function (request) {
        Ext.getHead().appendChild(request.script);
    }
});


Ext.define('Ext.data.proxy.JsonP', {
    extend:  Ext.data.proxy.Server ,
    alternateClassName: 'Ext.data.ScriptTagProxy',
    alias: ['proxy.jsonp', 'proxy.scripttag'],
                                 

    defaultWriterType: 'base',

    
    callbackKey : 'callback',

    
    recordParam: 'records',

    
    autoAppendParams: true,

    constructor: function() {
        this.addEvents(
            
            'exception'
        );
        this.callParent(arguments);
    },

    
    doRequest: function(operation, callback, scope) {
        
        var me      = this,
            request = me.buildRequest(operation),
            params  = request.params;

        
        Ext.apply(request, {
            callbackKey: me.callbackKey,
            timeout: me.timeout,
            scope: me,
            disableCaching: false, 
            callback: me.createRequestCallback(request, operation, callback, scope)
        });

        
        
        if (me.autoAppendParams) {
            request.params = {};
        }

        request.jsonp = Ext.data.JsonP.request(request);
        
        request.params = params;
        operation.setStarted();
        me.lastRequest = request;

        return request;
    },

    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;

        return function(success, response, errorType) {
            delete me.lastRequest;
            me.processResponse(success, operation, request, response, callback, scope);
        };
    },

    
    setException: function(operation, response) {
        operation.setException(operation.request.jsonp.errorType);
    },


    
    buildUrl: function(request) {
        var me      = this,
            url     = me.callParent(arguments),
            records = request.records,
            writer  = me.getWriter(),
            params,
            filters,
            filter, i;

        
        
        if (writer && request.operation.allowWrite()) {
            request = writer.write(request);
        }

        
        params  = request.params;
        filters = params.filters,
        delete params.filters;
        if (filters && filters.length) {
            for (i = 0; i < filters.length; i++) {
                filter = filters[i];

                if (filter.value) {
                    params[filter.property] = filter.value;
                }
            }
        }

        
        if ((!writer || !writer.encode) && Ext.isArray(records) && records.length > 0) {
            params[me.recordParam] = me.encodeRecords(records);
        }

        
        
        if (me.autoAppendParams) {
            url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
        }

        return url;
    },

    
    abort: function() {
        var lastRequest = this.lastRequest;
        if (lastRequest) {
            Ext.data.JsonP.abort(lastRequest.jsonp);
        }
    },

    
    encodeRecords: function(records) {
        var encoded = [],
            i = 0,
            len = records.length;

        for (; i < len; i++) {
            encoded.push(Ext.encode(records[i].getData()));
        }

        return encoded;
    }
});


Ext.define('Ext.data.JsonPStore', {
    extend:  Ext.data.Store ,
    alias : 'store.jsonp',
               
                               
                              
      

    constructor: function(config) {
        config = Ext.apply({
            proxy: {
                type: 'jsonp',
                reader: 'json'
            }
        }, config);
        this.callParent([config]);
    }
});


Ext.define('Ext.data.JsonStore',  {
    extend:  Ext.data.Store ,
    alias: 'store.json',
               
                              
                               
                              
      

    constructor: function(config) {
        config = Ext.apply({
            proxy: {
                type  : 'ajax',
                reader: 'json',
                writer: 'json'
            }
        }, config);
        this.callParent([config]);
    }
});


Ext.define('Ext.data.NodeInterface', {
               
                         
                              
      

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    


    

    

    

    

    

    

    statics: {
        
        decorate: function(modelClass) {
            var idName, idField, idType;
            
            
            if (typeof modelClass == 'string') {
                modelClass = Ext.ModelManager.getModel(modelClass);
            } else if (modelClass.isModel) {
                modelClass = Ext.ModelManager.getModel(modelClass.modelName);
            }
            
            
            if (modelClass.prototype.isNode) {
                return;
            }

            idName  = modelClass.prototype.idProperty;
            idField = modelClass.prototype.fields.get(idName);
            idType  = modelClass.prototype.fields.get(idName).type.type;

            modelClass.override(this.getPrototypeBody());
            this.applyFields(modelClass, [
                { name : 'parentId',   type : idType,    defaultValue : null,  useNull : idField.useNull },
                { name : 'index',      type : 'int',     defaultValue : 0,     persist : false          , convert: null },
                { name : 'depth',      type : 'int',     defaultValue : 0,     persist : false          , convert: null },
                { name : 'expanded',   type : 'bool',    defaultValue : false, persist : false          , convert: null },
                { name : 'expandable', type : 'bool',    defaultValue : true,  persist : false          , convert: null },
                { name : 'checked',    type : 'auto',    defaultValue : null,  persist : false          , convert: null },
                { name : 'leaf',       type : 'bool',    defaultValue : false                            },
                { name : 'cls',        type : 'string',  defaultValue : '',    persist : false          , convert: null },
                { name : 'iconCls',    type : 'string',  defaultValue : '',    persist : false          , convert: null },
                { name : 'icon',       type : 'string',  defaultValue : '',    persist : false          , convert: null },
                { name : 'root',       type : 'boolean', defaultValue : false, persist : false          , convert: null },
                { name : 'isLast',     type : 'boolean', defaultValue : false, persist : false          , convert: null },
                { name : 'isFirst',    type : 'boolean', defaultValue : false, persist : false          , convert: null },
                { name : 'allowDrop',  type : 'boolean', defaultValue : true,  persist : false          , convert: null },
                { name : 'allowDrag',  type : 'boolean', defaultValue : true,  persist : false          , convert: null },
                { name : 'loaded',     type : 'boolean', defaultValue : false, persist : false          , convert: null },
                { name : 'loading',    type : 'boolean', defaultValue : false, persist : false          , convert: null },
                { name : 'href',       type : 'string',  defaultValue : '',    persist : false          , convert: null },
                { name : 'hrefTarget', type : 'string',  defaultValue : '',    persist : false          , convert: null },
                { name : 'qtip',       type : 'string',  defaultValue : '',    persist : false          , convert: null },
                { name : 'qtitle',     type : 'string',  defaultValue : '',    persist : false          , convert: null },
                { name : 'qshowDelay', type : 'int',     defaultValue : 0,     persist : false          , convert: null },
                { name : 'children',   type : 'auto',    defaultValue : null,  persist : false          , convert: null }
            ]);
        },
        
        applyFields: function(modelClass, addFields) {
            var modelPrototype = modelClass.prototype,
                fields = modelPrototype.fields,
                keys = fields.keys,
                ln = addFields.length,
                addField, i;

            for (i = 0; i < ln; i++) {
                addField = addFields[i];
                if (!Ext.Array.contains(keys, addField.name)) {
                    fields.add(new Ext.data.Field(addField));
                }
            }

        },

        getPrototypeBody: function() {
            var bubbledEvents = {
                idchanged     : true,
                append        : true,
                remove        : true,
                move          : true,
                insert        : true,
                beforeappend  : true,
                beforeremove  : true,
                beforemove    : true,
                beforeinsert  : true,
                expand        : true,
                collapse      : true,
                beforeexpand  : true,
                beforecollapse: true,
                sort          : true,
                rootchange    : true
            };
            return {
                
                isNode: true,
                
                constructor: function() {
                    var me = this;
                    me.callParent(arguments);
                    me.firstChild = me.lastChild = me.parentNode = me.previousSibling = me.nextSibling = null;
                    me.childNodes = [];

                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    return me;
                },
                
                createNode: function(node) {
                    if (!node.isModel) {
                        node = Ext.ModelManager.create(node, this.modelName);
                    }
                    
                    
                    
                    if (!node.childNodes) {
                        node.firstChild = node.lastChild = node.parentNode = node.previousSibling = node.nextSibling = null;
                        node.childNodes = [];
                    }
                    return node;
                },

                
                isLeaf : function() {
                    return this.get('leaf') === true;
                },

                
                setFirstChild : function(node) {
                    this.firstChild = node;
                },

                
                setLastChild : function(node) {
                    this.lastChild = node;
                },

                
                updateInfo: function(commit, info) {
                    var me = this,
                        oldDepth = me.data.depth,
                        childInfo = {},
                        children = me.childNodes,
                        childCount = children.length,
                        i,
                        phantom = me.phantom,
                        dataObject = me[me.persistenceProperty],
                        propName, newValue,
                        field;
                        
                    if (!info) {
                        Ext.Error.raise('NodeInterface expects update info to be passed');
                    }

                    
                    
                    
                    for (propName in info) {
                        field = me.fields.get(propName);
                        newValue = info[propName];
                        
                        
                        if (field && field.persist) {
                            me.dirty = me.dirty || !me.isEqual(dataObject[propName], newValue);
                        }
                        dataObject[propName] = newValue;
                    }
                    if (commit) {
                        me.commit();
                        me.phantom = phantom;
                    }

                    
                    if (me.data.depth !== oldDepth) {
                        childInfo = {
                            depth: me.data.depth + 1
                        };
                        for (i = 0; i < childCount; i++) {
                            children[i].updateInfo(commit, childInfo);
                        }
                    }
                },

                
                isLast : function() {
                   return this.get('isLast');
                },

                
                isFirst : function() {
                   return this.get('isFirst');
                },

                
                hasChildNodes : function() {
                    return !this.isLeaf() && this.childNodes.length > 0;
                },

                
                isExpandable : function() {
                    var me = this;

                    if (me.get('expandable')) {
                        return !(me.isLeaf() || (me.isLoaded() && !me.hasChildNodes()));
                    }
                    return false;
                },
                
                triggerUIUpdate: function() {
                    
                    
                    this.afterEdit([]);    
                },

                
                appendChild : function(node, suppressEvents, commit) {
                    var me = this,
                        i, ln,
                        index,
                        oldParent,
                        previousSibling,
                        childInfo = {
                            isLast: true,
                            parentId: me.getId(),
                            depth: (me.data.depth||0) + 1
                        };

                    
                    if (Ext.isArray(node)) {
                        
                        me.callStore('suspendAutoSync');
                        for (i = 0, ln = node.length - 1; i < ln; i++) {
                            me.appendChild(node[i], suppressEvents, commit);
                        }
                        
                        me.callStore('resumeAutoSync');
                        me.appendChild(node[ln], suppressEvents, commit);
                    } else {
                        
                        node = me.createNode(node);

                        if (suppressEvents !== true && me.fireEventArgs("beforeappend", [me, node]) === false) {
                            return false;
                        }

                        index = me.childNodes.length;
                        oldParent = node.parentNode;

                        
                        if (oldParent) {
                            if (suppressEvents !== true && node.fireEventArgs("beforemove", [node, oldParent, me, index]) === false) {
                                return false;
                            }
                            oldParent.removeChild(node, false, false, true);
                        }

                        
                        Ext.suspendLayouts();

                        index = me.childNodes.length;
                        if (index === 0) {
                            me.setFirstChild(node);
                        }

                        me.childNodes[index] = node;
                        node.parentNode = me;
                        node.nextSibling = null;

                        me.setLastChild(node);

                        previousSibling = me.childNodes[index - 1];
                        if (previousSibling) {
                            node.previousSibling = previousSibling;
                            previousSibling.nextSibling = node;
                            previousSibling.updateInfo(commit, {
                                isLast: false
                            });
                            previousSibling.triggerUIUpdate();
                        } else {
                            node.previousSibling = null;
                        }

                        
                        childInfo.isFirst = index === 0;
                        childInfo.index = index;
                        node.updateInfo(commit, childInfo);

                        
                        if (!me.isLoaded()) {
                            me.set('loaded', true);
                        } else if (me.childNodes.length === 1) {
                            me.triggerUIUpdate();
                        }

                        
                        if (index && me.childNodes[index - 1].isExpanded()) {
                            me.childNodes[index - 1].cascadeBy(me.triggerUIUpdate);
                        }

                        if(!node.isLeaf() && node.phantom) {
                            node.set('loaded', true);
                        }

                        
                        Ext.resumeLayouts(true);

                        if (suppressEvents !== true) {
                            me.fireEventArgs("append", [me, node, index]);

                            if (oldParent) {
                                node.fireEventArgs("move", [node, oldParent, me, index]);
                            }
                        }

                        return node;
                    }
                },

                
                getOwnerTree: function() {
                    var node = this,
                        store;
                        
                    while (node.parentNode) {
                        node = node.parentNode;
                    }
                    store = node.store;
                    if (store) {
                        if (store.treeStore) {
                            store = store.treeStore;
                        }
                        
                        if (store.tree) {
                            return store.ownerTree;
                        }
                    }
                    return undefined;
                },

                
                removeChild : function(node, destroy, suppressEvents, isMove) {
                    var me = this,
                        index = me.indexOf(node),
                        i, childCount,
                        previousSibling;

                    if (index === -1 || (suppressEvents !== true && me.fireEventArgs("beforeremove", [me, node, !!isMove]) === false)) {
                        return false;
                    }

                    
                    Ext.suspendLayouts();

                    
                    Ext.Array.erase(me.childNodes, index, 1);

                    
                    if (me.firstChild === node) {
                        me.setFirstChild(node.nextSibling);
                    }
                    if (me.lastChild === node) {
                        me.setLastChild(node.previousSibling);
                    }

                    
                    
                    if (previousSibling = node.previousSibling) {
                        node.previousSibling.nextSibling = node.nextSibling;
                    }
                    
                    
                    if (node.nextSibling) {
                        node.nextSibling.previousSibling = node.previousSibling;

                        
                        if (index === 0) {
                            node.nextSibling.updateInfo(false, {
                                isFirst: true
                            });
                        }

                        
                        for (i = index, childCount = me.childNodes.length; i < childCount; i++) {
                            me.childNodes[i].updateInfo(false, {
                                index: i
                            });
                        }
                    }

                    
                    
                    else if (previousSibling) {
                        previousSibling.updateInfo(false, {
                            isLast: true
                        });

                        
                        
                        if (previousSibling.isExpanded()) {
                            previousSibling.cascadeBy(me.triggerUIUpdate);
                        }
                        
                        else {
                            previousSibling.triggerUIUpdate();
                        }
                    }

                    
                    if (!me.childNodes.length) {
                        me.triggerUIUpdate();
                    }

                    
                    Ext.resumeLayouts(true);

                    if (suppressEvents !== true) {
                        
                        node.removeContext = {
                            parentNode: node.parentNode,
                            previousSibling: node.previousSibling,
                            nextSibling: node.nextSibling
                        };

                        node.previousSibling = node.nextSibling = node.parentNode = null;
                        me.fireEventArgs('remove', [me, node, !!isMove]);

                        
                        node.removeContext = null;
                    }

                    
                    
                    if (destroy) {
                        node.destroy(true);
                    } else {
                        node.clear();
                    }

                    return node;
                },

                
                copy: function(newId, deep) {
                    var me = this,
                        result = me.callParent(arguments),
                        len = me.childNodes ? me.childNodes.length : 0,
                        i;

                    
                    if (deep) {
                        for (i = 0; i < len; i++) {
                            result.appendChild(me.childNodes[i].copy(undefined, true));
                        }
                    }
                    return result;
                },

                
                clear : function(destroy) {
                    var me = this;

                    
                    me.parentNode = me.previousSibling = me.nextSibling = null;
                    if (destroy) {
                        me.firstChild = me.lastChild = null;
                    }
                },

                
                destroy : function(silent) {
                    
                    var me      = this,
                        options = me.destroyOptions,
                        nodes   = me.childNodes,
                        nLen    = nodes.length,
                        n;

                    if (silent === true) {
                        me.clear(true);

                        for (n = 0; n < nLen; n++) {
                            nodes[n].destroy(true);
                        }

                        me.childNodes = null;
                        delete me.destroyOptions;
                        me.callParent([options]);
                    } else {
                        me.destroyOptions = silent;
                        
                        me.remove(true);
                    }
                },

                
                insertBefore : function(node, refNode, suppressEvents) {
                    var me = this,
                        index     = me.indexOf(refNode),
                        oldParent = node.parentNode,
                        refIndex  = index,
                        childCount, previousSibling, i;

                    if (!refNode) { 
                        return me.appendChild(node);
                    }

                    
                    if (node === refNode) {
                        return false;
                    }

                    
                    node = me.createNode(node);

                    if (suppressEvents !== true && me.fireEventArgs("beforeinsert", [me, node, refNode]) === false) {
                        return false;
                    }

                    
                    if (oldParent === me && me.indexOf(node) < index) {
                        refIndex--;
                    }

                    
                    if (oldParent) {
                        if (suppressEvents !== true && node.fireEventArgs("beforemove", [node, oldParent, me, index, refNode]) === false) {
                            return false;
                        }
                        oldParent.removeChild(node, false, false, true);
                    }

                    if (refIndex === 0) {
                        me.setFirstChild(node);
                    }

                    Ext.Array.splice(me.childNodes, refIndex, 0, node);
                    node.parentNode = me;

                    node.nextSibling = refNode;
                    refNode.previousSibling = node;

                    previousSibling = me.childNodes[refIndex - 1];
                    if (previousSibling) {
                        node.previousSibling = previousSibling;
                        previousSibling.nextSibling = node;
                    } else {
                        node.previousSibling = null;
                    }

                    
                    node.updateInfo(false, {
                        parentId: me.getId(),
                        index: refIndex,
                        isFirst: refIndex === 0,
                        isLast: false,
                        depth: (me.data.depth||0) + 1
                    });

                    
                    for (i = refIndex + 1, childCount = me.childNodes.length; i < childCount; i++) {
                        me.childNodes[i].updateInfo(false, {
                            index: i
                        });
                    }

                    if (!me.isLoaded()) {
                        me.set('loaded', true);
                    }
                    
                    else if (me.childNodes.length === 1) {
                        me.triggerUIUpdate();
                    }

                    if(!node.isLeaf() && node.phantom) {
                        node.set('loaded', true);
                    }

                    if (suppressEvents !== true) {
                        me.fireEventArgs("insert", [me, node, refNode]);

                        if (oldParent) {
                            node.fireEventArgs("move", [node, oldParent, me, refIndex, refNode]);
                        }
                    }

                    return node;
                },

                
                insertChild: function(index, node) {
                    var sibling = this.childNodes[index];
                    if (sibling) {
                        return this.insertBefore(node, sibling);
                    }
                    else {
                        return this.appendChild(node);
                    }
                },

                
                remove : function(destroy, suppressEvents) {
                    var me = this,
                        parentNode = me.parentNode;

                    if (parentNode) {
                        parentNode.removeChild(me, destroy, suppressEvents);
                    } else if (destroy) {
                        
                        me.destroy(true);
                    }
                    return me;
                },

                
                removeAll : function(destroy, suppressEvents, fromParent) {
                    
                    
                    
                    var me = this,
                        childNodes = me.childNodes,
                        i = 0,
                        len = childNodes.length,
                        node;

                    
                    if (!len) {
                        return;
                    }

                    
                    
                    me.fireEventArgs('bulkremove', [me, childNodes, false]);

                    for (; i < len; ++i) {
                        node = childNodes[i];
                        
                        
                        node.removeContext = {
                            parentNode: node.parentNode,
                            previousSibling: node.previousSibling,
                            nextSibling: node.nextSibling
                        };

                        node.previousSibling = node.nextSibling = node.parentNode = null;
                        me.fireEventArgs('remove', [me, node, false]);

                        
                        node.removeContext = null;

                        
                        if (destroy) {
                            node.destroy(true);
                        }
                        
                        else {
                            node.removeAll(false, suppressEvents, true);
                        }
                    }

                    me.firstChild = me.lastChild = null;

                    
                    if (fromParent) {
                        
                        me.childNodes = null;
                    } else {
                        
                        me.childNodes.length = 0;
                        me.triggerUIUpdate();
                    }
                    
                    return me;
                },

                
                getChildAt : function(index) {
                    return this.childNodes[index];
                },

                
                replaceChild : function(newChild, oldChild, suppressEvents) {
                    var s = oldChild ? oldChild.nextSibling : null;

                    this.removeChild(oldChild, false, suppressEvents);
                    this.insertBefore(newChild, s, suppressEvents);
                    return oldChild;
                },

                
                indexOf : function(child) {
                    return Ext.Array.indexOf(this.childNodes, child);
                },
                
                
                indexOfId: function(id) {
                    var childNodes = this.childNodes,
                        len = childNodes.length,
                        i = 0;
                        
                    for (; i < len; ++i) {
                        if (childNodes[i].getId() === id) {
                            return i;
                        }    
                    }
                    return -1;
                },

                
                getPath: function(field, separator) {
                    field = field || this.idProperty;
                    separator = separator || '/';

                    var path = [this.get(field)],
                        parent = this.parentNode;

                    while (parent) {
                        path.unshift(parent.get(field));
                        parent = parent.parentNode;
                    }
                    return separator + path.join(separator);
                },

                
                getDepth : function() {
                    return this.get('depth');
                },

                
                bubble : function(fn, scope, args) {
                    var p = this;
                    while (p) {
                        if (fn.apply(scope || p, args || [p]) === false) {
                            break;
                        }
                        p = p.parentNode;
                    }
                },

                cascade: function() {
                    if (Ext.isDefined(Ext.global.console)) {
                        Ext.global.console.warn('Ext.data.Node: cascade has been deprecated. Please use cascadeBy instead.');
                    }
                    return this.cascadeBy.apply(this, arguments);
                },

                
                cascadeBy : function(fn, scope, args) {
                    if (fn.apply(scope || this, args || [this]) !== false) {
                        var childNodes = this.childNodes,
                            length     = childNodes.length,
                            i;

                        for (i = 0; i < length; i++) {
                            childNodes[i].cascadeBy(fn, scope, args);
                        }
                    }
                },

                
                eachChild : function(fn, scope, args) {
                    var childNodes = this.childNodes,
                        length     = childNodes.length,
                        i;

                    for (i = 0; i < length; i++) {
                        if (fn.apply(scope || this, args || [childNodes[i]]) === false) {
                            break;
                        }
                    }
                },

                
                findChild : function(attribute, value, deep) {
                    return this.findChildBy(function() {
                        return this.get(attribute) == value;
                    }, null, deep);
                },

                
                findChildBy : function(fn, scope, deep) {
                    var cs = this.childNodes,
                        len = cs.length,
                        i = 0, n, res;

                    for (; i < len; i++) {
                        n = cs[i];
                        if (fn.call(scope || n, n) === true) {
                            return n;
                        }
                        else if (deep) {
                            res = n.findChildBy(fn, scope, deep);
                            if (res !== null) {
                                return res;
                            }
                        }
                    }

                    return null;
                },

                
                contains : function(node) {
                    return node.isAncestor(this);
                },

                
                isAncestor : function(node) {
                    var p = this.parentNode;
                    while (p) {
                        if (p === node) {
                            return true;
                        }
                        p = p.parentNode;
                    }
                    return false;
                },

                
                sort : function(sortFn, recursive, suppressEvent) {
                    var cs  = this.childNodes,
                        ln = cs.length,
                        i, n, info = {
                            isFirst: true
                        };

                    if (ln > 0) {
                        Ext.Array.sort(cs, sortFn);
                        this.setFirstChild(cs[0]);
                        this.setLastChild(cs[ln - 1]);

                        for (i = 0; i < ln; i++) {
                            n = cs[i];
                            n.previousSibling = cs[i-1];
                            n.nextSibling = cs[i+1];
                            
                            
                            info.isLast = (i === ln - 1);
                            info.index = i;
                            n.updateInfo(false, info);
                            info.isFirst = false;

                            if (recursive && !n.isLeaf()) {
                                n.sort(sortFn, true, true);
                            }
                        }

                        if (suppressEvent !== true) {
                            this.fireEventArgs('sort', [this, cs]);
                        }
                    }
                },

                
                isExpanded: function() {
                    return this.get('expanded');
                },

                
                isLoaded: function() {
                    return this.get('loaded');
                },

                
                isLoading: function() {
                    return this.get('loading');
                },

                
                isRoot: function() {
                    return !this.parentNode;
                },

                
                isVisible: function() {
                    var parent = this.parentNode;
                    while (parent) {
                        if (!parent.isExpanded()) {
                            return false;
                        }
                        parent = parent.parentNode;
                    }
                    return true;
                },

                
                expand: function(recursive, callback, scope) {
                    var me = this,
                        owner;

                    
                    

                    
                    if (!me.isLeaf()) {
                        
                        if (me.isLoading()) {
                            me.on('expand', function() {
                                me.expand(recursive, callback, scope);
                            }, me, {single: true});
                        } else {
                            
                            if (!me.isExpanded()) {

                                
                                
                                
                                
                                me.fireEventArgs('beforeexpand', [me, me.onChildNodesAvailable, me, [recursive, callback, scope]]);
                            } else if (recursive) {
                                
                                owner = me.getOwnerTree();
                                me.expandChildren(true, owner ? owner.singleExpand : false, callback, scope);
                            } else {
                                Ext.callback(callback, scope || me, [me.childNodes]);
                            }
                        }
                    } else {
                        
                        Ext.callback(callback, scope || me); 
                    }
                },

                
                onChildNodesAvailable: function(records, recursive, callback, scope) {
                    var me = this,
                        owner;

                    
                    
                    Ext.suspendLayouts();

                    
                    me.set('expanded', true);

                    
                    me.fireEventArgs('expand', [me, me.childNodes, false]);

                    
                    if (recursive) {
                        owner = me.getOwnerTree();
                        me.expandChildren(true, owner ? owner.singleExpand : false, callback, scope);
                    } else {
                        Ext.callback(callback, scope || me, [me.childNodes]);
                    }

                    Ext.resumeLayouts(true);
                },

                
                expandChildren: function(recursive, singleExpand, callback, scope) {
                    var me = this,
                        i,
                        allNodes = me.childNodes,
                        expandNodes = [],
                        ln = singleExpand ? Math.min(allNodes.length, 1) : allNodes.length,
                        node;

                    for (i = 0; i < ln; ++i) {
                        node = allNodes[i];
                        if (!node.isLeaf()) {
                            expandNodes[expandNodes.length] = node;
                        }
                    }
                    ln = expandNodes.length;

                    for (i = 0; i < ln; ++i) {
                        expandNodes[i].expand(recursive);
                    }

                    if (callback) {
                        Ext.callback(callback, scope || me, [me.childNodes]);
                    }
                },

                
                collapse: function(recursive, callback, scope) {
                    var me = this,
                        expanded = me.isExpanded(),
                        len = me.childNodes.length,
                        i, collapseChildren;

                    
                    
                    
                    
                    if (!me.isLeaf() && ((!expanded && recursive) || me.fireEventArgs('beforecollapse', [me]) !== false)) {

                        
                        
                        Ext.suspendLayouts();

                        
                        if (me.isExpanded()) {
                            
                            
                            
                            
                            
                            
                            if (recursive) {
                                collapseChildren = function() {
                                    for (i = 0; i < len; i++) {
                                        me.childNodes[i].setCollapsed(true);
                                    }
                                };
                                if (callback) {
                                    callback = Ext.Function.createSequence(collapseChildren, callback);
                                } else {
                                    callback = collapseChildren;
                                }
                            }

                            
                            me.set('expanded', false);

                            
                            
                            
                            me.fireEventArgs('collapse', [me, me.childNodes, false, callback ? Ext.Function.bind(callback, scope, [me.childNodes]) : null, null]);

                            
                            callback = null;
                        }

                        
                        
                        
                        
                        else if (recursive) {
                            for (i = 0; i < len; i++) {
                                me.childNodes[i].setCollapsed(true);
                            }
                        }

                        Ext.resumeLayouts(true);
                    }

                    
                    Ext.callback(callback, scope || me, [me.childNodes]);
                },

                
                setCollapsed: function(recursive) {
                    var me = this,
                        len = me.childNodes.length,
                        i;

                    
                    if (!me.isLeaf() && me.fireEventArgs('beforecollapse', [me, Ext.emptyFn]) !== false) {

                        
                        me.data.expanded = false;

                        
                        
                        
                        me.fireEventArgs('collapse', [me, me.childNodes, false, null, null]);

                        if (recursive) {
                            for (i = 0; i < len; i++) {
                                me.childNodes[i].setCollapsed(true);
                            }
                        }
                    }
                },

                
                collapseChildren: function(recursive, callback, scope) {
                    var me = this,
                        i,
                        allNodes = me.childNodes,
                        ln = allNodes.length,
                        collapseNodes = [],
                        node;

                    
                    for (i = 0; i < ln; ++i) {
                        node = allNodes[i];
                        if (!node.isLeaf() && node.isLoaded() && node.isExpanded()) {
                            collapseNodes.push(node);
                        }
                    }
                    ln = collapseNodes.length;

                    
                    
                    for (i = 0; i < ln; ++i) {
                        node = collapseNodes[i];
                        if (i === ln - 1) {
                            node.collapse(recursive, callback, scope);
                        } else {
                            node.collapse(recursive);
                        }
                    }
                },

                
                
                
                fireEventArgs: function(eventName, args) {
                    
                    
                    
                    var fireEventArgs = Ext.data.Model.prototype.fireEventArgs,
                        result, eventSource, tree, treeStore, rootNode;

                    
                    if (bubbledEvents[eventName]) {
                        for (eventSource = this; result !== false && eventSource; eventSource = (rootNode = eventSource).parentNode) {
                            if (eventSource.hasListeners[eventName]) {
                                result = fireEventArgs.call(eventSource, eventName, args);
                            }
                        }

                        
                        tree = rootNode.rootOf;
                        if (result !== false && tree) {
                            treeStore = tree.treeStore;
                            if (treeStore && treeStore.hasListeners[eventName]) {
                                result = treeStore.fireEventArgs.call(treeStore, eventName, args);
                            }
                            if (result !== false && tree.hasListeners[eventName]) {
                                result = tree.fireEventArgs.call(tree, eventName, args);
                            }
                        }
                        return result;
                    }
                    
                    else {
                        return fireEventArgs.apply(this, arguments)
                    }
                },

                
                serialize: function() {
                    var result = Ext.data.writer.Json.prototype.getRecordData(this),
                        childNodes = this.childNodes,
                        len = childNodes.length,
                        children, i;

                    if (len > 0) {
                        children = [];
                        for (i = 0; i < len; i++) {
                            children.push(childNodes[i].serialize());
                        }
                        result.children = children;
                    }
                    return result;
                }
            };
        }
    }
});


Ext.define('Ext.data.NodeStore', {
    extend:  Ext.data.Store ,
    alias: 'store.node',
                                         

    
    isNodeStore: true,

    
    node: null,

    
    recursive: false,

    
    rootVisible: false,

    

    
    isExpandingOrCollapsing: 0,

    constructor: function(config) {
        var me = this,
            node;

        config = config || {};
        Ext.apply(me, config);


        config.proxy = {type: 'proxy'};
        me.callParent([config]);

        node = me.node;
        if (node) {
            me.node = null;
            me.setNode(node);
        }
    },

    
    
    
    getTotalCount: function() {
        return this.getCount();
    },

    setNode: function(node) {
        var me = this;
        if (me.node && me.node != node) {
            
            me.mun(me.node, {
                expand: me.onNodeExpand,
                collapse: me.onNodeCollapse,
                append: me.onNodeAppend,
                insert: me.onNodeInsert,
                bulkremove: me.onBulkRemove,
                remove: me.onNodeRemove,
                sort: me.onNodeSort,
                scope: me
            });
            me.node = null;
        }

        if (node) {
            Ext.data.NodeInterface.decorate(node.self);
            me.removeAll();
            if (me.rootVisible) {
                me.add(node);
            } else if (!node.isExpanded() && me.treeStore.autoLoad !== false) {
                node.expand();
            }

            me.mon(node, {
                expand: me.onNodeExpand,
                collapse: me.onNodeCollapse,
                append: me.onNodeAppend,
                insert: me.onNodeInsert,
                bulkremove: me.onBulkRemove,
                remove: me.onNodeRemove,
                sort: me.onNodeSort,
                scope: me
            });
            me.node = node;
            if (node.isExpanded() && node.isLoaded()) {
                me.onNodeExpand(node, node.childNodes, true);
            }
        }
    },
 
    onNodeSort: function(node, childNodes) {
        var me = this;

        if ((me.indexOf(node) !== -1 || (node === me.node && !me.rootVisible) && node.isExpanded())) {
            Ext.suspendLayouts();
            me.onNodeCollapse(node, childNodes, true);
            me.onNodeExpand(node, childNodes, true);
            Ext.resumeLayouts(true);
        }
    },

    
    onNodeExpand: function(parent, records, suppressEvent) {
        var me = this,
            insertIndex = me.indexOf(parent) + 1,
            toAdd = [];

        
        
        
        if (!suppressEvent) {
            me.fireEvent('beforeexpand', parent, records, insertIndex);
        }

        me.handleNodeExpand(parent, records, toAdd);

        
        
        
        me.insert(insertIndex, toAdd);

        
        
        if (!suppressEvent) {
            me.fireEvent('expand', parent, records);
        }
    },

    
    
    handleNodeExpand: function(parent, records, toAdd) {
        var me = this,
            ln = records ? records.length : 0,
            i, record;

        
        if (!me.recursive && parent !== me.node) {
            return;
        }

        if (parent !== this.node && !me.isVisible(parent)) {
            return;
        }

        if (ln) {
            
            
            for (i = 0; i < ln; i++) {
                record = records[i];

                
                
                
                toAdd.push(record);

                if (record.isExpanded()) {
                    if (record.isLoaded()) {
                        
                        me.handleNodeExpand(record, record.childNodes, toAdd);
                    }
                    else {
                        
                        record.set('expanded', false);
                        record.expand();
                    }
                }
            }
        }
    },
    
    
    onBulkRemove: function(parent, childNodes, isMove) {
        this.onNodeCollapse(parent, childNodes, true);
    },

    
    onNodeCollapse: function(parent, records, suppressEvent, callback, scope) {
        var me = this,
            collapseIndex = me.indexOf(parent) + 1,
            node, lastNodeIndexPlus, sibling, found;

        if (!me.recursive && parent !== me.node) {
            return;
        }

        
        
        
        
        if (!suppressEvent) {
            me.fireEvent('beforecollapse', parent, records, collapseIndex, callback, scope);
        }

        
        
        
        
        if (records.length && me.data.contains(records[0])) {
            
            
            
            
            node = parent;
            while (node.parentNode) {
                sibling = node.nextSibling;
                if (sibling) {
                    found = true;
                    lastNodeIndexPlus = me.indexOf(sibling); 
                    break;
                } else {
                    node = node.parentNode;
                }
            }
            if (!found) {
                lastNodeIndexPlus = me.getCount();
            }

            
            me.removeAt(collapseIndex, lastNodeIndexPlus - collapseIndex);
        }

        
        
        if (!suppressEvent) {
            me.fireEvent('collapse', parent, records, collapseIndex);
        }
    },

    onNodeAppend: function(parent, node, index) {
        var me = this,
            refNode, sibling;

        
        if (me.isVisible(node)) {
            if (index === 0) {
                refNode = parent;
            } else {
                sibling = node.previousSibling;
                while (sibling.isExpanded() && sibling.lastChild) {
                    sibling = sibling.lastChild;
                }
                refNode = sibling;
            }
            me.insert(me.indexOf(refNode) + 1, node);
            if (!node.isLeaf() && node.isExpanded()) {
                if (node.isLoaded()) {
                    
                    me.onNodeExpand(node, node.childNodes, true);
                } else if (!me.treeStore.fillCount ) {
                    
                    
                    
                    
                    node.set('expanded', false);
                    node.expand();
                }
            }
        }
    },

    onNodeInsert: function(parent, node, refNode) {
        var me = this,
            index = this.indexOf(refNode);

        if (index != -1 && me.isVisible(node)) {
            me.insert(index, node);
            if (!node.isLeaf() && node.isExpanded()) {
                if (node.isLoaded()) {
                    
                    me.onNodeExpand(node, node.childNodes, true);
                }
                else {
                    node.set('expanded', false);
                    node.expand();
                }
            }
        }
    },

    onNodeRemove: function(parent, node, isMove) {
        var me = this;
        if (me.indexOf(node) != -1) {

            
            
            if (!node.isLeaf() && node.isExpanded()) {

                
                
                
                
                node.parentNode = node.removeContext.parentNode;
                node.nextSibling = node.removeContext.nextSibling;
                me.onNodeCollapse(node, node.childNodes, true);
                node.parentNode = node.nextSibling = null;
            }
            me.remove(node);
        }
    },

    isVisible: function(node) {
        var parent = node.parentNode;
        while (parent) {
            
            if (parent === this.node && parent.data.expanded) {
                return true;
            }

            
            if (!parent.data.expanded) {
                return false;
            }

            parent = parent.parentNode;
        }
        
        return false;
    }
});


Ext.define('Ext.data.Request', {
    
    action: undefined,
    
    
    params: undefined,
    
    
    method: 'GET',
    
    
    url: undefined,

    
    constructor: function(config) {
        Ext.apply(this, config);
    }
});


Ext.define('Ext.data.SequentialIdGenerator', {
    extend:  Ext.data.IdGenerator ,
    alias: 'idgen.sequential',

    constructor: function() {
        var me = this;

        me.callParent(arguments);

        me.parts = [ me.prefix, ''];
    },

    
    prefix: '',

    
    seed: 1,

    
    generate: function () {
        var me = this,
            parts = me.parts;

        parts[1] = me.seed++;
        return parts.join('');
    }
});


Ext.define('Ext.data.Tree', {
    alias: 'data.tree',

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    root: null,

    
    constructor: function(root) {
        var me = this;

        me.mixins.observable.constructor.call(me);

        if (root) {
            me.setRootNode(root);
        }
        
        
        me.on({
            scope: me,
            idchanged: me.onNodeIdChanged,
            insert: me.onNodeInsert,
            append: me.onNodeAppend,
            remove: me.onNodeRemove
        });
    },

    
    getRootNode : function() {
        return this.root;
    },

    
    setRootNode : function(node) {
        var me = this;

        me.root = node;

        
        if (node.rootOf) {
            node.rootOf.removeRootNode();
        }

        
        else if (node.parentNode) {
            node.parentNode.removeChild(node);
        }

        
        node.rootOf = me;

        if (node.fireEventArgs('beforeappend', [null, node]) !== false) {
            node.set('root', true);
            
            node.updateInfo(true, {
                isFirst: true,
                isLast: true,
                depth: 0,
                index: 0,
                parentId: null
            });

            
            
            
            
            
            
            
            
            
            
            
            
            
            
            

            me.nodeHash = {};
            node.fireEvent('append', null, node);
            node.fireEvent('rootchange', node);
        }

        return node;
    },

    
    removeRootNode: function() {
        var me = this,
            root = me.root;

        root.set('root', false);
        root.fireEvent('remove', null, root, false);
        root.fireEvent('rootchange', null);

        
        
        root.rootOf = me.root = null;
        return root;
    },

    
    flatten: function(){
        return Ext.Object.getValues(this.nodeHash);
    },

    
    onNodeInsert: function(parent, node) {
        this.registerNode(node, true);
    },

    
    onNodeAppend: function(parent, node) {
        this.registerNode(node, true);
    },

    
    onNodeRemove: function(parent, node) {
        this.unregisterNode(node, true);
    },

    
    onNodeIdChanged: function(node, oldId, newId, oldInternalId) {
        var nodeHash = this.nodeHash;
    
        nodeHash[node.internalId] = node;
        delete nodeHash[oldInternalId];
    },

    
    getNodeById : function(id) {
        return this.nodeHash[id];
    },

    
    registerNode : function(node, includeChildren) {
        var me = this,
            children, length, i;

        me.nodeHash[node.internalId] = node;
        if (includeChildren === true) {
            children = node.childNodes;
            length = children.length;
            for (i = 0; i < length; i++) {
                me.registerNode(children[i], true);
            }
        }
    },

    
    unregisterNode : function(node, includeChildren) {
        var me = this,
            children, length, i;

        delete me.nodeHash[node.internalId];
        if (includeChildren === true) {
            children = node.childNodes;
            length = children.length;
            for (i = 0; i < length; i++) {
                me.unregisterNode(children[i], true);
            }
        }
    },

    
    sort: function(sorterFn, recursive) {
        this.getRootNode().sort(sorterFn, recursive);
    },

     
    filter: function(filters, recursive) {
        this.getRootNode().filter(filters, recursive);
    }
});


Ext.define('Ext.data.TreeModel', {
    extend:  Ext.data.Model  
               
                                
     
},
function () {
    Ext.data.NodeInterface.decorate(this);
});


Ext.define('Ext.data.TreeStore', {
    extend:  Ext.data.AbstractStore ,
    alias: 'store.tree',
               
                          
                        
                                
      

    

    
    clearOnLoad : true,

    
    clearRemovedOnLoad: true,

    
    nodeParam: 'node',

    
    defaultRootId: 'root',
    
    
    defaultRootText: 'Root',

    
    defaultRootProperty: 'children',
    
    
    rootProperty: 'children',
    
    fillCount: 0,

    
    folderSort: false,

    constructor: function(config) {
        var me = this,
            root,
            fields,
            defaultRoot;

        config = Ext.apply({}, config);

        
        fields = config.fields || me.fields;
        if (!fields) {
            config.fields = [
                {name: 'text', type: 'string'}
            ];
            defaultRoot = config.defaultRootProperty || me.defaultRootProperty;
            if (defaultRoot !== me.defaultRootProperty) {
                config.fields.push({
                    name: defaultRoot,   
                    type: 'auto',   
                    defaultValue: null, 
                    persist: false
                });
            }
        }

        me.callParent([config]);

        
        me.tree = new Ext.data.Tree();
        
        
        me.tree.treeStore = me;

        
        
        
        
        
        
        
        
        
        
        
        
        
        

        me.tree.on({
            scope: me,
            remove: me.onNodeRemove,
            
            
            beforeexpand: me.onBeforeNodeExpand,
            append: me.onNodeAdded,
            insert: me.onNodeAdded,
            sort: me.onNodeSort
        });

        me.onBeforeSort();

        root = me.root;
        if (root) {
            delete me.root;
            me.setRootNode(root);
        }

        if (Ext.isDefined(me.nodeParameter)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.data.TreeStore: nodeParameter has been deprecated. Please use nodeParam instead.');
            }
            me.nodeParam = me.nodeParameter;
            delete me.nodeParameter;
        }
    },

    
    setProxy: function(proxy) {
        var reader,
            needsRoot;

        if (proxy instanceof Ext.data.proxy.Proxy) {
            
            needsRoot = Ext.isEmpty(proxy.getReader().root);
        } else if (Ext.isString(proxy)) {
            
            needsRoot = true;
        } else {
            
            reader = proxy.reader;
            needsRoot = !(reader && !Ext.isEmpty(reader.root));
        }
        proxy = this.callParent(arguments);

        
        
        
        proxy.idParam = this.nodeParam;

        if (needsRoot) {
            reader = proxy.getReader();
            reader.root = this.defaultRootProperty;
            
            reader.buildExtractors(true);
        }
        return proxy;
    },

    
    onBeforeSort: function() {
        if (this.folderSort) {
            this.sort({
                property: 'leaf',
                direction: 'ASC'
            }, 'prepend', false);
        }
    },

    
    onBeforeNodeExpand: function(node, callback, scope, args) {
        var me = this,
            reader, dataRoot, data,
            callbackArgs;
        
        
        if (node.isLoaded()) {
            callbackArgs = [node.childNodes];
            if (args) {
                callbackArgs.push.apply(callbackArgs, args);
            }
            Ext.callback(callback, scope || node, callbackArgs);
        }
        
        else if (dataRoot = (data = (node.raw || node[node.persistenceProperty])[(reader = me.getProxy().getReader()).root])) {
            me.fillNode(node, reader.extractData(dataRoot));
            delete data[reader.root];
            callbackArgs = [node.childNodes];
            if (args) {
                callbackArgs.push.apply(callbackArgs, args);
            }
            Ext.callback(callback, scope || node, callbackArgs);
        }
        
        else if (node.isLoading()) {
            me.on('load', function() {
                callbackArgs = [node.childNodes];
                if (args) {
                    callbackArgs.push.apply(callbackArgs, args);
                }
                Ext.callback(callback, scope || node, callbackArgs);
            }, me, {single: true});
        }
        
        else {
            me.read({
                node: node,
                callback: function() {
                    
                    
                    delete me.lastOptions.callback;
                    callbackArgs = [node.childNodes];
                    if (args) {
                        callbackArgs.push.apply(callbackArgs, args);
                    }
                    Ext.callback(callback, scope || node, callbackArgs);
                }
            });
        }
    },

    
    getNewRecords: function() {
        return Ext.Array.filter(this.tree.flatten(), this.filterNew);
    },

    
    getUpdatedRecords: function() {
        return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
    },

    onNodeRemove: function(parent, node, isMove) {
        var me = this;

        node.unjoin(me);
        
        
        
        if (!node.phantom && !isMove) {
            Ext.Array.include(me.removed, node);
        }

        if (me.autoSync && !me.autoSyncSuspended && !isMove) {
            me.sync();
        }
    },

    onNodeAdded: function(parent, node) {
        var me = this,
            proxy = me.getProxy(),
            reader = proxy.getReader(),
            data = node.raw || node[node.persistenceProperty],
            dataRoot;

        Ext.Array.remove(me.removed, node);
        node.join(me);

        
        if (!node.isLeaf() && !me.lazyFill) {
            dataRoot = reader.getRoot(data);
            if (dataRoot) {
                me.fillNode(node, reader.extractData(dataRoot));
                delete data[reader.root];
            }
        }

        if (me.autoSync && !me.autoSyncSuspended && (node.phantom || node.dirty)) {
            me.sync();
        }
    },

    onNodeSort: function() {
        if (this.autoSync && !this.autoSyncSuspended) {
            this.sync();
        }
    },

    
    setRootNode: function(root,  preventLoad) {
        var me = this,
            model = me.model,
            idProperty = model.prototype.idProperty

        root = root || {};
        if (!root.isModel) {
            root = Ext.apply({}, root);
            
            Ext.applyIf(root, {
                id: me.defaultRootId,
                text: me.defaultRootText,
                allowDrag: false
            });
            if (root[idProperty] === undefined) {
                root[idProperty] = me.defaultRootId;
            }
            Ext.data.NodeInterface.decorate(model);
            root = Ext.ModelManager.create(root, model);
        } else if (root.isModel && !root.isNode) {
            Ext.data.NodeInterface.decorate(model);
        }


        
        
        me.getProxy().getReader().buildExtractors(true);

        
        me.tree.setRootNode(root);

        
        
        
        
        
        if (preventLoad !== true && !root.isLoaded() && (me.autoLoad === true || root.isExpanded())) {
            root.data.expanded = false;
            root.expand();
        }

        return root;
    },

    
    getRootNode: function() {
        return this.tree.getRootNode();
    },

    
    getNodeById: function(id) {
        return this.tree.getNodeById(id);
    },
    
    
    getById: function(id) {
        return this.getNodeById(id);    
    },

    
    load: function(options) {
        options = options || {};
        options.params = options.params || {};

        var me = this,
            node = options.node || me.tree.getRootNode();

        
        
        if (!node) {
            node = me.setRootNode({
                expanded: true
            }, true);
        }
        
        
        
        options.id = node.getId();

        if (me.clearOnLoad) {
            if(me.clearRemovedOnLoad) {
                
                me.clearRemoved(node);
            }
            
            me.tree.un('remove', me.onNodeRemove, me);
            
            node.removeAll(false);
            
            me.tree.on('remove', me.onNodeRemove, me);
        }

        Ext.applyIf(options, {
            node: node
        });

        me.callParent([options]);
        
        if (me.loading && node) {
            node.set('loading', true);
        }
        
        return me;
    },

    
    clearRemoved: function(node) {
        var me = this,
            removed = me.removed,
            id = node.getId(),
            removedLength = removed.length,
            i = removedLength,
            recordsToClear = {},
            newRemoved = [],
            removedHash = {},
            removedNode,
            targetNode,
            targetId;

        if(node === me.getRootNode()) {
            
            me.removed = [];
            return;
        }

        
        for(; i--;) {
            removedNode = removed[i];
            removedHash[removedNode.getId()] = removedNode;
        }

        for(i = removedLength; i--;) {
            removedNode = removed[i];
            targetNode = removedNode;
            while(targetNode && targetNode.getId() !== id) {
                
                targetId = targetNode.get('parentId');
                targetNode = targetNode.parentNode || me.getNodeById(targetId) || removedHash[targetId];
            }
            if(targetNode) {
                
                recordsToClear[removedNode.getId()] = removedNode;
            }
        }

        
        for(i = 0; i < removedLength; i++) {
            removedNode = removed[i];
            if(!recordsToClear[removedNode.getId()]) {
                newRemoved.push(removedNode);
            }
        }

        me.removed = newRemoved;
    },

    
    fillNode: function(node, newNodes) {
        var me = this,
            ln = newNodes ? newNodes.length : 0,
            sorters = me.sorters,
            i, sortCollection,
            needsIndexSort = false,
            performLocalSort = ln && me.sortOnLoad && !me.remoteSort && sorters && sorters.items && sorters.items.length,
            node1, node2, rootFill;

        
        for (i = 1; i < ln; i++) {
            node1 = newNodes[i];
            node2 = newNodes[i - 1];
            needsIndexSort = node1[node1.persistenceProperty].index != node2[node2.persistenceProperty].index;
            if (needsIndexSort) {
                break;
            }
        }

        
        if (performLocalSort) {
            
            if (needsIndexSort) {
                me.sorters.insert(0, me.indexSorter);
            }
            sortCollection = new Ext.util.MixedCollection();
            sortCollection.addAll(newNodes);
            sortCollection.sort(me.sorters.items);
            newNodes = sortCollection.items;

            
            me.sorters.remove(me.indexSorter);
        } else if (needsIndexSort) {
            Ext.Array.sort(newNodes, me.sortByIndex);
        }

        node.set('loaded', true);
        
        
        
        
        
        rootFill = me.fillCount === 0;
        if (rootFill) {
            
            me.fireEvent('beforefill', me, node, newNodes);
        }
        ++me.fillCount;

        if (newNodes.length) {
            node.appendChild(newNodes, undefined, true);
        }
        
        if (rootFill) {
            
            me.fireEvent('fillcomplete', me, node, newNodes);
        }
        --me.fillCount;
        
        return newNodes;
    },

    
    sortByIndex: function(node1, node2) {
        return node1[node1.persistenceProperty].index - node2[node2.persistenceProperty].index;
    },
    
    onIdChanged: function(model, oldId, newId, oldInternalId){
        this.tree.onNodeIdChanged(model, oldId, newId, oldInternalId);
        this.callParent(arguments);    
    },

    
    onProxyLoad: function(operation) {
        var me = this,
            successful = operation.wasSuccessful(),
            records = operation.getRecords(),
            node = operation.node;

        me.loading = false;
        node.set('loading', false);
        if (successful) {
            if (!me.clearOnLoad) {
                records = me.cleanRecords(node, records);
            }
            records = me.fillNode(node, records);
        }
        
        
        
        
        me.fireEvent('read', me, operation.node, records, successful);
        me.fireEvent('load', me, operation.node, records, successful);
        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
    },
    
    cleanRecords: function(node, records){
        var nodeHash = {},
            childNodes = node.childNodes,
            i = 0,
            len  = childNodes.length,
            out = [],
            rec;
            
        
        for (; i < len; ++i) {
            nodeHash[childNodes[i].getId()] = true;
        }
        
        for (i = 0, len = records.length; i < len; ++i) {
            rec = records[i];
            if (!nodeHash[rec.getId()]) {
                out.push(rec);    
            }
        }
        
        return out;
    },

    
    removeAll: function() {
        var root = this.getRootNode();
        if (root) {
            root.destroy(true);
        }
        this.fireEvent('clear', this);
    },

    
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.tree.sort(sorterFn, true);
            me.fireEvent('datachanged', me);
            me.fireEvent('refresh', me);
        }
        me.fireEvent('sort', me, me.sorters.getRange());
    }
}, function() {
    var proto = this.prototype;
    proto.indexSorter = new Ext.util.Sorter({
        sorterFn: proto.sortByIndex
    });
});


Ext.define('Ext.data.UuidGenerator', (function () {
    var twoPow14 = Math.pow(2, 14),
        twoPow16 = Math.pow(2, 16),
        twoPow28 = Math.pow(2, 28),
        twoPow32 = Math.pow(2, 32);

    function toHex (value, length) {
        var ret = value.toString(16);
        if (ret.length > length) {
            ret = ret.substring(ret.length - length); 
        } else if (ret.length < length) {
            ret = Ext.String.leftPad(ret, length, '0');
        }
        return ret;
    }

    function rand (lo, hi) {
        var v = Math.random() * (hi - lo + 1);
        return Math.floor(v) + lo;
    }

    function split (bignum) {
        if (typeof(bignum) == 'number') {
            var hi = Math.floor(bignum / twoPow32);
            return {
                lo: Math.floor(bignum - hi * twoPow32),
                hi: hi
            };
        }
        return bignum;
    }

    return {
        extend:  Ext.data.IdGenerator ,

        alias: 'idgen.uuid',

        id: 'uuid', 

        

        

        
        version: 4,

        constructor: function() {
            var me = this;

            me.callParent(arguments);

            me.parts = [];
            me.init();
        },

        generate: function () {
            var me = this,
                parts = me.parts,
                ts = me.timestamp;

            
            parts[0] = toHex(ts.lo, 8);
            parts[1] = toHex(ts.hi & 0xFFFF, 4);
            parts[2] = toHex(((ts.hi >>> 16) & 0xFFF) | (me.version << 12), 4);
            parts[3] = toHex(0x80 | ((me.clockSeq >>> 8) & 0x3F), 2) +
                       toHex(me.clockSeq & 0xFF, 2);
            parts[4] = toHex(me.salt.hi, 4) + toHex(me.salt.lo, 8);

            if (me.version == 4) {
                me.init(); 
            } else {
                
                ++ts.lo;
                if (ts.lo >= twoPow32) { 
                    ts.lo = 0;
                    ++ts.hi;
                }
            }

            return parts.join('-').toLowerCase();
        },

        getRecId: function (rec) {
            return rec.getId();
        },

        
        init: function () {
            var me = this,
                salt, time;

            if (me.version == 4) {
                
                
                
                
                me.clockSeq = rand(0, twoPow14-1);

                
                salt = me.salt || (me.salt = {});
                time = me.timestamp || (me.timestamp = {});

                
                salt.lo = rand(0, twoPow32-1);
                salt.hi = rand(0, twoPow16-1);
                time.lo = rand(0, twoPow32-1);
                time.hi = rand(0, twoPow28-1);
            } else {
                
                me.salt = split(me.salt);
                me.timestamp = split(me.timestamp);

                
                
                me.salt.hi |= 0x100;
            }
        },

        
        reconfigure: function (config) {
            Ext.apply(this, config);
            this.init();
        }
    };
}()));


Ext.define('Ext.data.reader.Xml', {
    extend:  Ext.data.reader.Reader ,
    alternateClassName: 'Ext.data.XmlReader',
    alias : 'reader.xml',

    

    

    
    createAccessor: function(expr) {
        var me = this;

        if (Ext.isEmpty(expr)) {
            return Ext.emptyFn;
        }

        if (Ext.isFunction(expr)) {
            return expr;
        }

        return function(root) {
            return me.getNodeValue(Ext.DomQuery.selectNode(expr, root));
        };
    },

    getNodeValue: function(node) {
        if (node) {
            
            
            
            if (typeof node.normalize === 'function') {
                node.normalize();
            }
            node = node.firstChild;
            if (node) {
                return node.nodeValue;
            }
        }
        return undefined;
    },

    
    getResponseData: function(response) {
        var xml = response.responseXML,
            error,
            msg;

        if (!xml) {
            msg = 'XML data not found in the response';               

            error = new Ext.data.ResultSet({
                total  : 0,
                count  : 0,
                records: [],
                success: false,
                message: msg
            });

            this.fireEvent('exception', this, response, error);

            Ext.Logger.warn(msg);

            return error;
        }

        return this.readRecords(xml);
    },

    
    getData: function(data) {
        return data.documentElement || data;
    },

    
    getRoot: function(data) {
        var nodeName = data.nodeName,
            root     = this.root;

        if (!root || (nodeName && nodeName == root)) {
            return data;
        } else if (Ext.DomQuery.isXml(data)) {
            
            
            
            return Ext.DomQuery.selectNode(root, data);
        }
    },

    
    extractData: function(root) {
        var recordName = this.record;


        if (recordName != root.nodeName) {
            root = Ext.DomQuery.select(recordName, root);
        } else {
            root = [root];
        }
        return this.callParent([root]);
    },

    
    getAssociatedDataRoot: function(data, associationName) {
        return Ext.DomQuery.select(associationName, data)[0];
    },

    
    readRecords: function(doc) {
        
        
        if (Ext.isArray(doc)) {
            doc = doc[0];
        }

        
        this.xmlData = doc;
        return this.callParent([doc]);
    },

    
    createFieldAccessExpression: function(field, fieldVarName, dataName) {
        var namespace = this.namespace,
            selector, result;

        selector = field.mapping || ((namespace ? namespace + '|' : '') + field.name); 

        if (typeof selector === 'function') {
            result = fieldVarName + '.mapping(' + dataName + ', this)';
        } else {
            result = 'me.getNodeValue(Ext.DomQuery.selectNode("' + selector + '", ' + dataName + '))';
        }
        return result;
    }
});


Ext.define('Ext.data.writer.Xml', {
    
    
    
    extend:  Ext.data.writer.Writer ,
    alternateClassName: 'Ext.data.XmlWriter',
    
    alias: 'writer.xml',
    
    
    
    
    documentRoot: 'xmlData',
    
    
    defaultDocumentRoot: 'xmlData',

    
    header: '',

    
    record: 'record',

    
    writeRecords: function(request, data) {
        var me = this,
            xml = [],
            i = 0,
            len = data.length,
            root = me.documentRoot,
            record = me.record,
            needsRoot = data.length !== 1,
            item,
            key;
            
        
        xml.push(me.header || '');
        
        if (!root && needsRoot) {
            root = me.defaultDocumentRoot;
        }
        
        if (root) {
            xml.push('<', root, '>');
        }
            
        for (; i < len; ++i) {
            item = data[i];
            xml.push('<', record, '>');
            for (key in item) {
                if (item.hasOwnProperty(key)) {
                    xml.push('<', key, '>', item[key], '</', key, '>');
                }
            }
            xml.push('</', record, '>');
        }
        
        if (root) {
            xml.push('</', root, '>');
        }
            
        request.xmlData = xml.join('');
        return request;
    }
});


Ext.define('Ext.data.XmlStore', {
    extend:  Ext.data.Store ,
    alias: 'store.xml',

               
                              
                              
                             
      
    
    constructor: function(config){
        config = Ext.apply({
            proxy: {
                type: 'ajax',
                reader: 'xml',
                writer: 'xml'
            }
        }, config);

        this.callParent([config]);
    }
});


Ext.define('Ext.data.association.BelongsTo', {
    extend:  Ext.data.association.Association ,
    alternateClassName: 'Ext.data.BelongsToAssociation',
    alias: 'association.belongsto',

    

    

    

    
    constructor: function(config) {
        this.callParent(arguments);

        var me             = this,
            ownerProto     = me.ownerModel.prototype,
            associatedName = me.associatedName,
            getterName     = me.getterName || 'get' + associatedName,
            setterName     = me.setterName || 'set' + associatedName;

        Ext.applyIf(me, {
            name        : associatedName,
            foreignKey  : associatedName.toLowerCase() + "_id",
            instanceName: associatedName + 'BelongsToInstance',
            associationKey: associatedName.toLowerCase()
        });

        ownerProto[getterName] = me.createGetter();
        ownerProto[setterName] = me.createSetter();
    },

    
    createSetter: function() {
        var me = this,
            foreignKey = me.foreignKey,
            instanceName = me.instanceName;

        
        return function(value, options, scope) {
            
            var setByRecord = value && value.isModel,
                valueToSet = setByRecord ? value.getId() : value;

            
            if (setByRecord) {
                this[instanceName] = value;
            }

            
            else if (this[instanceName] instanceof Ext.data.Model && !this.isEqual(this.get(foreignKey), valueToSet)) {
                delete this[instanceName];
            }

            
            this.set(foreignKey, valueToSet);

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }

            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },

    
    createGetter: function() {
        var me              = this,
            associatedName  = me.associatedName,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey,
            instanceName    = me.instanceName;

        
        return function(options, scope) {
            options = options || {};

            var model = this,
                foreignKeyId = model.get(foreignKey),
                success,
                instance,
                args;

            if (options.reload === true || model[instanceName] === undefined) {
                instance = Ext.ModelManager.create({}, associatedName);
                instance.set(primaryKey, foreignKeyId);

                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || model
                    };
                }
                
                
                success = options.success;
                options.success = function(rec){
                    model[instanceName] = rec;
                    if (success) {
                        success.apply(this, arguments);
                    }
                };

                associatedModel.load(foreignKeyId, options);
                
                model[instanceName] = instance;
                return instance;
            } else {
                instance = model[instanceName];
                args = [instance];
                scope = scope || options.scope || model;

                
                
                
                Ext.callback(options, scope, args);
                Ext.callback(options.success, scope, args);
                Ext.callback(options.failure, scope, args);
                Ext.callback(options.callback, scope, args);

                return instance;
            }
        };
    },

    
    read: function(record, reader, associationData){
        record[this.instanceName] = reader.read([associationData]).records[0];
    }
});


Ext.define('Ext.util.Inflector', {

    

    singleton: true,

    

    
    plurals: [
        [(/(quiz)$/i),                "$1zes"  ],
        [(/^(ox)$/i),                 "$1en"   ],
        [(/([m|l])ouse$/i),           "$1ice"  ],
        [(/(matr|vert|ind)ix|ex$/i),  "$1ices" ],
        [(/(x|ch|ss|sh)$/i),          "$1es"   ],
        [(/([^aeiouy]|qu)y$/i),       "$1ies"  ],
        [(/(hive)$/i),                "$1s"    ],
        [(/(?:([^f])fe|([lr])f)$/i),  "$1$2ves"],
        [(/sis$/i),                   "ses"    ],
        [(/([ti])um$/i),              "$1a"    ],
        [(/(buffal|tomat|potat)o$/i), "$1oes"  ],
        [(/(bu)s$/i),                 "$1ses"  ],
        [(/(alias|status|sex)$/i),    "$1es"   ],
        [(/(octop|vir)us$/i),         "$1i"    ],
        [(/(ax|test)is$/i),           "$1es"   ],
        [(/^person$/),                "people" ],
        [(/^man$/),                   "men"    ],
        [(/^(child)$/),               "$1ren"  ],
        [(/s$/i),                     "s"      ],
        [(/$/),                       "s"      ]
    ],

    
    singulars: [
      [(/(quiz)zes$/i),                                                    "$1"     ],
      [(/(matr)ices$/i),                                                   "$1ix"   ],
      [(/(vert|ind)ices$/i),                                               "$1ex"   ],
      [(/^(ox)en/i),                                                       "$1"     ],
      [(/(alias|status)es$/i),                                             "$1"     ],
      [(/(octop|vir)i$/i),                                                 "$1us"   ],
      [(/(cris|ax|test)es$/i),                                             "$1is"   ],
      [(/(shoe)s$/i),                                                      "$1"     ],
      [(/(o)es$/i),                                                        "$1"     ],
      [(/(bus)es$/i),                                                      "$1"     ],
      [(/([m|l])ice$/i),                                                   "$1ouse" ],
      [(/(x|ch|ss|sh)es$/i),                                               "$1"     ],
      [(/(m)ovies$/i),                                                     "$1ovie" ],
      [(/(s)eries$/i),                                                     "$1eries"],
      [(/([^aeiouy]|qu)ies$/i),                                            "$1y"    ],
      [(/([lr])ves$/i),                                                    "$1f"    ],
      [(/(tive)s$/i),                                                      "$1"     ],
      [(/(hive)s$/i),                                                      "$1"     ],
      [(/([^f])ves$/i),                                                    "$1fe"   ],
      [(/(^analy)ses$/i),                                                  "$1sis"  ],
      [(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i), "$1$2sis"],
      [(/([ti])a$/i),                                                      "$1um"   ],
      [(/(n)ews$/i),                                                       "$1ews"  ],
      [(/people$/i),                                                       "person" ],
      [(/s$/i),                                                            ""       ]
    ],

    
     uncountable: [
        "sheep",
        "fish",
        "series",
        "species",
        "money",
        "rice",
        "information",
        "equipment",
        "grass",
        "mud",
        "offspring",
        "deer",
        "means"
    ],

    
    singular: function(matcher, replacer) {
        this.singulars.unshift([matcher, replacer]);
    },

    
    plural: function(matcher, replacer) {
        this.plurals.unshift([matcher, replacer]);
    },

    
    clearSingulars: function() {
        this.singulars = [];
    },

    
    clearPlurals: function() {
        this.plurals = [];
    },

    
    isTransnumeral: function(word) {
        return Ext.Array.indexOf(this.uncountable, word) != -1;
    },

    
    pluralize: function(word) {
        if (this.isTransnumeral(word)) {
            return word;
        }

        var plurals = this.plurals,
            length  = plurals.length,
            tuple, regex, i;

        for (i = 0; i < length; i++) {
            tuple = plurals[i];
            regex = tuple[0];

            if (regex == word || (regex.test && regex.test(word))) {
                return word.replace(regex, tuple[1]);
            }
        }

        return word;
    },

    
    singularize: function(word) {
        if (this.isTransnumeral(word)) {
            return word;
        }

        var singulars = this.singulars,
            length    = singulars.length,
            tuple, regex, i;

        for (i = 0; i < length; i++) {
            tuple = singulars[i];
            regex = tuple[0];

            if (regex == word || (regex.test && regex.test(word))) {
                return word.replace(regex, tuple[1]);
            }
        }

        return word;
    },

    
    classify: function(word) {
        return Ext.String.capitalize(this.singularize(word));
    },

    
    ordinalize: function(number) {
        var parsed = parseInt(number, 10),
            mod10  = parsed % 10,
            mod100 = parsed % 100;

        
        if (11 <= mod100 && mod100 <= 13) {
            return number + "th";
        } else {
            switch(mod10) {
                case 1 : return number + "st";
                case 2 : return number + "nd";
                case 3 : return number + "rd";
                default: return number + "th";
            }
        }
    }
}, function() {
    
    var irregulars = {
            alumnus: 'alumni',
            cactus : 'cacti',
            focus  : 'foci',
            nucleus: 'nuclei',
            radius: 'radii',
            stimulus: 'stimuli',
            ellipsis: 'ellipses',
            paralysis: 'paralyses',
            oasis: 'oases',
            appendix: 'appendices',
            index: 'indexes',
            beau: 'beaux',
            bureau: 'bureaux',
            tableau: 'tableaux',
            woman: 'women',
            child: 'children',
            man: 'men',
            corpus:	'corpora',
            criterion: 'criteria',
            curriculum:	'curricula',
            genus: 'genera',
            memorandum:	'memoranda',
            phenomenon:	'phenomena',
            foot: 'feet',
            goose: 'geese',
            tooth: 'teeth',
            antenna: 'antennae',
            formula: 'formulae',
            nebula: 'nebulae',
            vertebra: 'vertebrae',
            vita: 'vitae'
        },
        singular;

    for (singular in irregulars) {
        this.plural(singular, irregulars[singular]);
        this.singular(irregulars[singular], singular);
    }
});


Ext.define('Ext.data.association.HasMany', {
    extend:  Ext.data.association.Association ,
    alternateClassName: 'Ext.data.HasManyAssociation',
                                     

    alias: 'association.hasmany',

    
    
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        var me = this,
            ownerProto,
            name;
            
        me.callParent(arguments);
        
        me.name = me.name || Ext.util.Inflector.pluralize(me.associatedName.toLowerCase());
        
        ownerProto = me.ownerModel.prototype;
        name = me.name;
        
        Ext.applyIf(me, {
            storeName : name + "Store",
            foreignKey: me.ownerName.toLowerCase() + "_id"
        });
        
        ownerProto[name] = me.createStore();
    },
    
    
    createStore: function() {
        var that            = this,
            associatedModel = that.associatedModel,
            storeName       = that.storeName,
            foreignKey      = that.foreignKey,
            primaryKey      = that.primaryKey,
            filterProperty  = that.filterProperty,
            autoLoad        = that.autoLoad,
            storeConfig     = that.storeConfig || {};
        
        return function() {
            var me = this,
                config, filter,
                modelDefaults = {};
                
            if (me[storeName] === undefined) {
                if (filterProperty) {
                    filter = {
                        property  : filterProperty,
                        value     : me.get(filterProperty),
                        exactMatch: true
                    };
                } else {
                    filter = {
                        property  : foreignKey,
                        value     : me.get(primaryKey),
                        exactMatch: true
                    };
                }
                
                modelDefaults[foreignKey] = me.get(primaryKey);
                
                config = Ext.apply({}, storeConfig, {
                    model        : associatedModel,
                    filters      : [filter],
                    remoteFilter : false,
                    modelDefaults: modelDefaults,
                    disableMetaChangeEvent: true
                });
                
                me[storeName] = Ext.data.AbstractStore.create(config);
                if (autoLoad) {
                    me[storeName].load();
                }
            }
            
            return me[storeName];
        };
    },
    
    
    read: function(record, reader, associationData){
        var store = record[this.name](),
            inverse,
            items, iLen, i;
    
        store.add(reader.read(associationData).records);
    
        
        
        inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
            return assoc.type === 'belongsTo' && assoc.associatedName === record.$className;
        });
    
        
        if (inverse) {
            items = store.data.items;
            iLen  = items.length;

            for (i = 0; i < iLen; i++) {
                items[i][inverse.instanceName] = record;
            }
        }
    }
});


Ext.define('Ext.data.association.HasOne', {
    extend:  Ext.data.association.Association ,
    alternateClassName: 'Ext.data.HasOneAssociation',

    alias: 'association.hasone',
    
    

    

    

    
    
    constructor: function(config) {
        this.callParent(arguments);

        var me             = this,
            ownerProto     = me.ownerModel.prototype,
            associatedName = me.associatedName,
            getterName     = me.getterName || 'get' + associatedName,
            setterName     = me.setterName || 'set' + associatedName;

        Ext.applyIf(me, {
            name        : associatedName,
            foreignKey  : associatedName.toLowerCase() + "_id",
            instanceName: associatedName + 'HasOneInstance',
            associationKey: associatedName.toLowerCase()
        });

        ownerProto[getterName] = me.createGetter();
        ownerProto[setterName] = me.createSetter();
    },
    
    
    createSetter: function() {
        var me              = this,
            foreignKey      = me.foreignKey,
            instanceName = me.instanceName;

        
        return function(value, options, scope) {
            
            var setByRecord = value && value.isModel,
                valueToSet = setByRecord ? value.getId() : value;

            
            if (setByRecord) {
                this[instanceName] = value;
            }

            
            else if (this[instanceName] instanceof Ext.data.Model && !this.isEqual(this.get(foreignKey), valueToSet)) {
                delete this[instanceName];
            }

            
            this.set(foreignKey, valueToSet);

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }

            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },

    
    createGetter: function() {
        var me              = this,
            ownerModel      = me.ownerModel,
            associatedName  = me.associatedName,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey,
            instanceName    = me.instanceName;

        
        return function(options, scope) {
            options = options || {};

            var model = this,
                foreignKeyId = model.get(foreignKey),
                success,
                instance,
                args;

            if (options.reload === true || model[instanceName] === undefined) {
                instance = Ext.ModelManager.create({}, associatedName);
                instance.set(primaryKey, foreignKeyId);

                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || model
                    };
                }
                
                
                success = options.success;
                options.success = function(rec){
                    model[instanceName] = rec;
                    if (success) {
                        success.apply(this, arguments);
                    }
                };

                associatedModel.load(foreignKeyId, options);
                
                model[instanceName] = instance;
                return instance;
            } else {
                instance = model[instanceName];
                args = [instance];
                scope = scope || options.scope || model;

                
                
                
                Ext.callback(options, scope, args);
                Ext.callback(options.success, scope, args);
                Ext.callback(options.failure, scope, args);
                Ext.callback(options.callback, scope, args);

                return instance;
            }
        };
    },
    
    
    read: function(record, reader, associationData){
        var inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
            return assoc.type === 'belongsTo' && assoc.associatedName === record.$className;
        }), newRecord = reader.read([associationData]).records[0];
        
        record[this.instanceName] = newRecord;
    
        
        if (inverse) {
            newRecord[inverse.instanceName] = record;
        }
    }
});


Ext.define('Ext.data.proxy.WebStorage', {
    extend:  Ext.data.proxy.Client ,
    alternateClassName: 'Ext.data.WebStorageProxy',
               
                                        
      

    
    id: undefined,

    

    

    
    constructor: function(config) {
        this.callParent(arguments);

        
        this.cache = {};


        
        this.id = this.id || (this.store ? this.store.storeId : undefined);


        this.initialize();
    },

    
    create: function(operation, callback, scope) {
        var me = this,
            records = operation.records,
            length = records.length,
            ids = me.getIds(),
            id, record, i;

        operation.setStarted();

        if(me.isHierarchical === undefined) {
            
            
            me.isHierarchical = !!records[0].isNode;
            if(me.isHierarchical) {
                me.getStorageObject().setItem(me.getTreeKey(), true);
            }
        }

        for (i = 0; i < length; i++) {
            record = records[i];

            if (record.phantom) {
                record.phantom = false;
                id = me.getNextId();
            } else {
                id = record.getId();
            }

            me.setRecord(record, id);
            record.commit();
            ids.push(id);
        }

        me.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }
    },

    
    read: function(operation, callback, scope) {
        

        var me = this,
            records = [],
            i = 0,
            success = true,
            Model = me.model,
            ids, length, record, data, id;

        operation.setStarted();

        if(me.isHierarchical) {
            records = me.getTreeData();
        } else {
            ids = me.getIds();
            length = ids.length;
            id = operation.id;
            
            if (id) {
                data = me.getRecord(id);
                if (data !== null) {
                    record = new Model(data, id, data);
                }

                if (record) {
                    records.push(record);
                } else {
                    success = false;
                }
            } else {
                for (; i < length; i++) {
                    id = ids[i];
                    data = me.getRecord(id);
                    records.push(new Model(data, id, data));
                }
            }

        }

        if(success) {
            operation.setSuccessful();
        }
        operation.setCompleted();

        operation.resultSet = Ext.create('Ext.data.ResultSet', {
            records: records,
            total  : records.length,
            loaded : true
        });

        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }
    },

    
    update: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),
            record, id, i;

        operation.setStarted();

        for (i = 0; i < length; i++) {
            record = records[i];
            this.setRecord(record);
            record.commit();

            
            
            id = record.getId();
            if (id !== undefined && Ext.Array.indexOf(ids, id) == -1) {
                ids.push(id);
            }
        }
        this.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    destroy: function(operation, callback, scope) {
        var me = this,
            records = operation.records,
            ids = me.getIds(),
            idLength = ids.length,
            newIds = [],
            removedHash = {},
            i = records.length,
            id;

        operation.setStarted();

        for (; i--;) {
            Ext.apply(removedHash, me.removeRecord(records[i]));
        }

        for(i = 0; i < idLength; i++) {
            id = ids[i];
            if(!removedHash[id]) {
                newIds.push(id);
            }
        }

        me.setIds(newIds);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }
    },

    
    getRecord: function(id) {
        var me = this,
            cache = me.cache,
            data = !cache[id] ? Ext.decode(me.getStorageObject().getItem(me.getRecordKey(id))) : cache[id];

        if(!data) {
            return null;
        }

        cache[id] = data;
        data[me.model.prototype.idProperty] = id;

        return data;
    },

    
    setRecord: function(record, id) {
        if (id) {
            record.setId(id);
        } else {
            id = record.getId();
        }

        var me = this,
            rawData = record.data,
            data    = {},
            model   = me.model,
            fields  = model.prototype.fields.items,
            length  = fields.length,
            i = 0,
            field, name, obj, key;

        for (; i < length; i++) {
            field = fields[i];
            name  = field.name;

            if(field.persist) {
                data[name] = rawData[name];
            }
        }

        
        delete data[me.model.prototype.idProperty];

        
        if(record.isNode && record.get('depth') === 1) {
            delete data.parentId;
        }

        obj = me.getStorageObject();
        key = me.getRecordKey(id);

        
        me.cache[id] = data;

        
        obj.removeItem(key);
        obj.setItem(key, Ext.encode(data));
    },

    
    removeRecord: function(record) {
        var me = this,
            id = record.getId(),
            records = {},
            i, childNodes;

        records[id] = record;
        me.getStorageObject().removeItem(me.getRecordKey(id));
        delete me.cache[id];

        if(record.childNodes) {
            childNodes = record.childNodes;
            for(i = childNodes.length; i--;) {
                Ext.apply(records, me.removeRecord(childNodes[i]));
            }
        }

        return records;
    },

    
    getRecordKey: function(id) {
        if (id.isModel) {
            id = id.getId();
        }

        return Ext.String.format("{0}-{1}", this.id, id);
    },

    
    getRecordCounterKey: function() {
        return Ext.String.format("{0}-counter", this.id);
    },

    
    getTreeKey: function() {
        return Ext.String.format("{0}-tree", this.id);
    },

    
    getIds: function() {
        var me = this,
            ids = (me.getStorageObject().getItem(me.id) || "").split(","),
            model = me.model,
            length = ids.length,
            isString = model.prototype.fields.get(model.prototype.idProperty).type.type === 'string',
            i;

        if (length == 1 && ids[0] === "") {
            ids = [];
        } else {
            for (i = 0; i < length; i++) {
                ids[i] = isString ? ids[i] : +ids[i];
            }
        }

        return ids;
    },

    
    setIds: function(ids) {
        var obj = this.getStorageObject(),
            str = ids.join(",");

        obj.removeItem(this.id);

        if (!Ext.isEmpty(str)) {
            obj.setItem(this.id, str);
        }
    },

    
    getNextId: function() {
        var me = this,
            obj = me.getStorageObject(),
            key = me.getRecordCounterKey(),
            model = me.model,
            isString = model.prototype.fields.get(model.prototype.idProperty).type.type === 'string',
            id;

        id = me.idGenerator.generate();

        obj.setItem(key, id);

        if(!isString) {
            id = +id;
        }

        return id;
    },

    
    getTreeData: function() {
        var me = this,
            ids = me.getIds(),
            length = ids.length,
            records = [],
            recordHash = {},
            root = [],
            i = 0,
            Model = me.model,
            idProperty = Model.prototype.idProperty,
            rootLength, record, parent, parentId, children, id;

        for(; i < length; i++) {
            id = ids[i];
            
            record = me.getRecord(id);
            
            records.push(record);
            
            recordHash[id] = record;
            if(!record.parentId) {
                
                root.push(record);
            }
        }

        rootLength = root.length;

        
        Ext.Array.sort(records, me.sortByParentId);

        
        for(i = rootLength; i < length; i++) {
            record = records[i];
            parentId = record.parentId;
            if(!parent || parent[idProperty] !== parentId) {
                
                parent = recordHash[parentId];
                parent.children = children = [];
            }

            
            children.push(record);
        }

        for(i = length; i--;) {
            record = records[i];
            if(!record.children && !record.leaf) {
                
                record.loaded = true;
            }
        }

        
        for(i = rootLength; i--;) {
            record = root[i];
            root[i] = new Model(record, record[idProperty], record);
        }

        return root;
    },

    
    sortByParentId: function(node1, node2) {
        return (node1.parentId || 0) - (node2.parentId || 0);
    },

    
    initialize: function() {
        var me = this,
            storageObject = me.getStorageObject(),
            lastId = +storageObject.getItem(me.getRecordCounterKey());

        storageObject.setItem(me.id, storageObject.getItem(me.id) || "");
        if(storageObject.getItem(me.getTreeKey())) {
            me.isHierarchical = true;
        }

        me.idGenerator = new Ext.data.SequentialIdGenerator({
            seed: lastId ? lastId + 1 : 1
        });
    },

    
    clear: function() {
        var me = this,
            obj = me.getStorageObject(),
            ids = me.getIds(),
            len = ids.length,
            i;

        
        for (i = 0; i < len; i++) {
            obj.removeItem(me.getRecordKey(ids[i]));
        }

        
        obj.removeItem(me.getRecordCounterKey());
        obj.removeItem(me.getTreeKey());
        obj.removeItem(me.id);

        
        me.cache = {};
    },

    
    getStorageObject: function() {
    }
});


Ext.define('Ext.data.proxy.LocalStorage', {
    extend:  Ext.data.proxy.WebStorage ,
    alias: 'proxy.localstorage',
    alternateClassName: 'Ext.data.LocalStorageProxy',
    
    
    getStorageObject: function() {
        return window.localStorage;
    }
});


Ext.define('Ext.data.proxy.Rest', {
    extend:  Ext.data.proxy.Ajax ,
    alternateClassName: 'Ext.data.RestProxy',
    alias : 'proxy.rest',

    
    actionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'PUT',
        destroy: 'DELETE'
    },

    
    appendId: true,

    

    
    batchActions: false,

    
    buildUrl: function(request) {
        var me        = this,
            operation = request.operation,
            records   = operation.records || [],
            record    = records[0],
            format    = me.format,
            url       = me.getUrl(request),
            id        = record ? record.getId() : operation.id;

        if (me.appendId && me.isValidId(id)) {
            if (!url.match(/\/$/)) {
                url += '/';
            }

            url += id;
        }

        if (format) {
            if (!url.match(/\.$/)) {
                url += '.';
            }

            url += format;
        }

        request.url = url;

        return me.callParent(arguments);
    },
    
    isValidId: function(id) {
        return id || id === 0;
    }
});


Ext.define('Ext.data.proxy.SessionStorage', {
    extend:  Ext.data.proxy.WebStorage ,
    alias: 'proxy.sessionstorage',
    alternateClassName: 'Ext.data.SessionStorageProxy',
    
    
    getStorageObject: function() {
        return window.sessionStorage;
    }
});





Ext.define('Ext.dd.DDTarget', {
    extend:  Ext.dd.DragDrop ,

    
    constructor: function(id, sGroup, config) {
        if (id) {
            this.initTarget(id, sGroup, config);
        }
    },

    
    getDragEl: Ext.emptyFn,
    
    isValidHandleChild: Ext.emptyFn,
    
    startDrag: Ext.emptyFn,
    
    endDrag: Ext.emptyFn,
    
    onDrag: Ext.emptyFn,
    
    onDragDrop: Ext.emptyFn,
    
    onDragEnter: Ext.emptyFn,
    
    onDragOut: Ext.emptyFn,
    
    onDragOver: Ext.emptyFn,
    
    onInvalidDrop: Ext.emptyFn,
    
    onMouseDown: Ext.emptyFn,
    
    onMouseUp: Ext.emptyFn,
    
    setXConstraint: Ext.emptyFn,
    
    setYConstraint: Ext.emptyFn,
    
    resetConstraints: Ext.emptyFn,
    
    clearConstraints: Ext.emptyFn,
    
    clearTicks: Ext.emptyFn,
    
    setInitPosition: Ext.emptyFn,
    
    setDragElId: Ext.emptyFn,
    
    setHandleElId: Ext.emptyFn,
    
    setOuterHandleElId: Ext.emptyFn,
    
    addInvalidHandleClass: Ext.emptyFn,
    
    addInvalidHandleId: Ext.emptyFn,
    
    addInvalidHandleType: Ext.emptyFn,
    
    removeInvalidHandleClass: Ext.emptyFn,
    
    removeInvalidHandleId: Ext.emptyFn,
    
    removeInvalidHandleType: Ext.emptyFn,

    toString: function() {
        return ("DDTarget " + this.id);
    }
});


Ext.define('Ext.dd.DragTracker', {

                              

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    active: false,

    

    
    trackOver: false,

    

    

    
    tolerance: 5,

    
    autoStart: false,

    

    

    

    constructor : function(config){
        var me = this;
        Ext.apply(me, config);
        me.addEvents(
            
            'mouseover',

            
            'mouseout',

            
            'mousedown',

            
            'mouseup',

            
            'mousemove',

            
            'beforedragstart',

            
            'dragstart',

            
            'dragend',

            
            'drag'
        );

        me.dragRegion = new Ext.util.Region(0,0,0,0);

        if (me.el) {
            me.initEl(me.el);
        }

        
        me.mixins.observable.constructor.call(me);
        if (me.disabled) {
            me.disable();
        }

    },

    
    initEl: function(el) {
        var me = this;

        me.el = Ext.get(el);

        
        me.handle = Ext.get(me.delegate);

        
        me.delegate = me.handle ? undefined : me.delegate;

        if (!me.handle) {
            me.handle = me.el;
        }

        
        
        me.handleListeners = {
            scope: me,
            delegate: me.delegate,
            mousedown: me.onMouseDown
        };

        
        
        
        if (me.trackOver || me.overCls) {
            Ext.apply(me.handleListeners, {
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut
            });
        }
        me.mon(me.handle, me.handleListeners);
    },

    disable: function() {
        this.disabled = true;
    },

    enable: function() {
        this.disabled = false;
    },

    destroy : function() {
        var me = this;

        if (me.active) {
            
            me.endDrag({});
        }
        me.clearListeners();
        me.mun(me.handle, me.handleListeners);
        me.el = me.handle = null;
    },

    
    
    onMouseOver: function(e, target) {
        var me = this;
        if (!me.disabled) {
            if (Ext.EventManager.contains(e) || me.delegate) {
                me.mouseIsOut = false;
                if (me.overCls) {
                    me.el.addCls(me.overCls);
                }
                me.fireEvent('mouseover', me, e, me.delegate ? e.getTarget(me.delegate, target) : me.handle);
            }
        }
    },

    
    
    onMouseOut: function(e) {
        var me = this;

        if (me.mouseIsDown) {
            me.mouseIsOut = true;
        } else {
            if (me.overCls) {
                me.el.removeCls(me.overCls);
            }
            me.fireEvent('mouseout', me, e);
        }
    },

    onMouseDown: function(e, target){
        var me = this,
            el;

        
        if (me.disabled ||e.dragTracked) {
            return;
        }

        
        me.dragTarget = me.delegate ? target : me.handle.dom;
        me.startXY = me.lastXY = e.getXY();
        me.startRegion = Ext.fly(me.dragTarget).getRegion();

        if (me.fireEvent('mousedown', me, e) === false ||
            me.fireEvent('beforedragstart', me, e) === false ||
            me.onBeforeStart(e) === false) {
            return;
        }

        
        
        me.mouseIsDown = true;

        
        e.dragTracked = true;

        
        el = me.el.dom;
        if (Ext.isIE && el.setCapture) {
            el.setCapture();
        }

        if (me.preventDefault !== false) {
            e.preventDefault();
        }
        Ext.getDoc().on({
            scope: me,
            mouseup: me.onMouseUp,
            mousemove: me.onMouseMove,
            selectstart: me.stopSelect
        });
        if (me.autoStart) {
            me.timer =  Ext.defer(me.triggerStart, me.autoStart === true ? 1000 : me.autoStart, me, [e]);
        }
    },

    onMouseMove: function(e, target){
        var me = this,
            xy = e.getXY(),
            s = me.startXY;

        e.preventDefault();

        me.lastXY = xy;
        if (!me.active) {
            if (Math.max(Math.abs(s[0]-xy[0]), Math.abs(s[1]-xy[1])) > me.tolerance) {
                me.triggerStart(e);
            } else {
                return;
            }
        }

        
        if (me.fireEvent('mousemove', me, e) === false) {
            me.onMouseUp(e);
        } else {
            me.onDrag(e);
            me.fireEvent('drag', me, e);
        }
    },

    onMouseUp: function(e) {
        var me = this;
        
        
        me.mouseIsDown = false;

        
        if (me.mouseIsOut) {
            me.mouseIsOut = false;
            me.onMouseOut(e);
        }
        e.preventDefault();

        
        if (Ext.isIE && document.releaseCapture) {
            document.releaseCapture();
        }

        me.fireEvent('mouseup', me, e);
        me.endDrag(e);
    },

    
    endDrag: function(e) {
        var me = this,
            wasActive = me.active;

        Ext.getDoc().un({
            mousemove: me.onMouseMove,
            mouseup: me.onMouseUp,
            selectstart: me.stopSelect,
            scope: me
        });
        me.clearStart();
        me.active = false;
        if (wasActive) {
            me.onEnd(e);
            me.fireEvent('dragend', me, e);
        }
        
        
        me._constrainRegion = Ext.EventObject.dragTracked = null
    },

    triggerStart: function(e) {
        var me = this;
        me.clearStart();
        me.active = true;
        me.onStart(e);
        me.fireEvent('dragstart', me, e);
    },

    clearStart : function() {
        var timer = this.timer;
        if (timer) {
            clearTimeout(timer);
            this.timer = null;
        }
    },

    stopSelect : function(e) {
        e.stopEvent();
        return false;
    },

    
    onBeforeStart : function(e) {

    },

    
    onStart : function(xy) {

    },

    
    onDrag : function(e) {

    },

    
    onEnd : function(e) {

    },

    
    getDragTarget : function(){
        return this.dragTarget;
    },

    
    getDragCt : function(){
        return this.el;
    },

    
    getConstrainRegion: function() {
        var me = this;

        if (me.constrainTo) {
            if (me.constrainTo instanceof Ext.util.Region) {
                return me.constrainTo;
            }
            if (!me._constrainRegion) {
                me._constrainRegion = Ext.fly(me.constrainTo).getViewRegion();
            }
        } else {
            if (!me._constrainRegion) {
                me._constrainRegion = me.getDragCt().getViewRegion();
            }
        }
        return me._constrainRegion;
    },

    getXY : function(constrain){
        return constrain ? this.constrainModes[constrain](this, this.lastXY) : this.lastXY;
    },

    
    getOffset : function(constrain){
        var xy = this.getXY(constrain),
            s = this.startXY;

        return [xy[0]-s[0], xy[1]-s[1]];
    },

    constrainModes: {
        
        point: function(me, xy) {
            var dr = me.dragRegion,
                constrainTo = me.getConstrainRegion();

            
            if (!constrainTo) {
                return xy;
            }

            dr.x = dr.left = dr[0] = dr.right = xy[0];
            dr.y = dr.top = dr[1] = dr.bottom = xy[1];
            dr.constrainTo(constrainTo);

            return [dr.left, dr.top];
        },

        
        dragTarget: function(me, xy) {
            var s = me.startXY,
                dr = me.startRegion.copy(),
                constrainTo = me.getConstrainRegion(),
                adjust;

            
            if (!constrainTo) {
                return xy;
            }

            
            
            
            dr.translateBy(xy[0]-s[0], xy[1]-s[1]);

            
            if (dr.right > constrainTo.right) {
                xy[0] += adjust = (constrainTo.right - dr.right);    
                dr.left += adjust;
            }
            if (dr.left < constrainTo.left) {
                xy[0] += (constrainTo.left - dr.left);      
            }

            
            if (dr.bottom > constrainTo.bottom) {
                xy[1] += adjust = (constrainTo.bottom - dr.bottom);  
                dr.top += adjust;
            }
            if (dr.top < constrainTo.top) {
                xy[1] += (constrainTo.top - dr.top);        
            }
            return xy;
        }
    }
});


Ext.define('Ext.dd.DragZone', {
    extend:  Ext.dd.DragSource ,

    
    constructor : function(el, config){
        var me = this,
            scroll = me.containerScroll;
        
        me.callParent([el, config]);
        if (scroll) {
            el = me.scrollEl || el;
            el = Ext.get(el);
            if (Ext.isObject(scroll)) {
                el.ddScrollConfig = scroll;
            }
            Ext.dd.ScrollManager.register(el);
        }
    },

    
    
    

    
    getDragData : function(e){
        return Ext.dd.Registry.getHandleFromEvent(e);
    },

    
    onInitDrag : function(x, y){
        this.proxy.update(this.dragData.ddel.cloneNode(true));
        this.onStartDrag(x, y);
        return true;
    },

    
    getRepairXY : function(e){
        return Ext.fly(this.dragData.ddel).getXY();
    },

    destroy : function(){
        this.callParent();
        if (this.containerScroll) {
            Ext.dd.ScrollManager.unregister(this.scrollEl || this.el);
        }
    }
});


Ext.define('Ext.dd.ScrollManager', {
    singleton: true,
               
                                
      

    constructor: function() {
        var ddm = Ext.dd.DragDropManager;
        ddm.fireEvents = Ext.Function.createSequence(ddm.fireEvents, this.onFire, this);
        ddm.stopDrag = Ext.Function.createSequence(ddm.stopDrag, this.onStop, this);
        this.doScroll = Ext.Function.bind(this.doScroll, this);
        this.ddmInstance = ddm;
        this.els = {};
        this.dragEl = null;
        this.proc = {};
    },

    onStop: function(e){
        var sm = Ext.dd.ScrollManager;
        sm.dragEl = null;
        sm.clearProc();
    },

    triggerRefresh: function() {
        if (this.ddmInstance.dragCurrent) {
            this.ddmInstance.refreshCache(this.ddmInstance.dragCurrent.groups);
        }
    },

    doScroll: function() {
        if (this.ddmInstance.dragCurrent) {
            var proc   = this.proc,
                procEl = proc.el,
                ddScrollConfig = proc.el.ddScrollConfig,
                inc = ddScrollConfig ? ddScrollConfig.increment : this.increment;

            if (!this.animate) {
                if (procEl.scroll(proc.dir, inc)) {
                    this.triggerRefresh();
                }
            } else {
                procEl.scroll(proc.dir, inc, true, this.animDuration, this.triggerRefresh);
            }
        }
    },

    clearProc: function() {
        var proc = this.proc;
        if (proc.id) {
            clearInterval(proc.id);
        }
        proc.id = 0;
        proc.el = null;
        proc.dir = "";
    },

    startProc: function(el, dir) {
        this.clearProc();
        this.proc.el = el;
        this.proc.dir = dir;
        var group = el.ddScrollConfig ? el.ddScrollConfig.ddGroup : undefined,
            freq  = (el.ddScrollConfig && el.ddScrollConfig.frequency)
                  ? el.ddScrollConfig.frequency
                  : this.frequency;

        if (group === undefined || this.ddmInstance.dragCurrent.ddGroup == group) {
            this.proc.id = setInterval(this.doScroll, freq);
        }
    },

    onFire: function(e, isDrop) {
        if (isDrop || !this.ddmInstance.dragCurrent) {
            return;
        }
        if (!this.dragEl || this.dragEl != this.ddmInstance.dragCurrent) {
            this.dragEl = this.ddmInstance.dragCurrent;
            
            this.refreshCache();
        }

        var xy = e.getXY(),
            pt = e.getPoint(),
            proc = this.proc,
            els = this.els,
            id, el, r, c;

        for (id in els) {
            el = els[id];
            r = el._region;
            c = el.ddScrollConfig ? el.ddScrollConfig : this;
            if (r && r.contains(pt) && el.isScrollable()) {
                if (r.bottom - pt.y <= c.vthresh) {
                    if(proc.el != el){
                        this.startProc(el, "down");
                    }
                    return;
                }else if (r.right - pt.x <= c.hthresh) {
                    if (proc.el != el) {
                        this.startProc(el, "left");
                    }
                    return;
                } else if(pt.y - r.top <= c.vthresh) {
                    if (proc.el != el) {
                        this.startProc(el, "up");
                    }
                    return;
                } else if(pt.x - r.left <= c.hthresh) {
                    if (proc.el != el) {
                        this.startProc(el, "right");
                    }
                    return;
                }
            }
        }
        this.clearProc();
    },

    
    register : function(el){
        if (Ext.isArray(el)) {
            for(var i = 0, len = el.length; i < len; i++) {
                    this.register(el[i]);
            }
        } else {
            el = Ext.get(el);
            this.els[el.id] = el;
        }
    },

    
    unregister : function(el){
        if(Ext.isArray(el)){
            for (var i = 0, len = el.length; i < len; i++) {
                this.unregister(el[i]);
            }
        }else{
            el = Ext.get(el);
            delete this.els[el.id];
        }
    },

    
    vthresh : 25,

    
    hthresh : 25,

    
    increment : 100,

    
    frequency : 500,

    
    animate: true,

    
    animDuration: 0.4,

    
    ddGroup: undefined,

    
    refreshCache : function(){
        var els = this.els,
            id;
        for (id in els) {
            if(typeof els[id] == 'object'){ 
                els[id]._region = els[id].getRegion();
            }
        }
    }
});


Ext.define('Ext.dd.DropTarget', {
    extend:  Ext.dd.DDTarget ,
                                       

    
    constructor : function(el, config){
        this.el = Ext.get(el);

        Ext.apply(this, config);

        if(this.containerScroll){
            Ext.dd.ScrollManager.register(this.el);
        }

        this.callParent([this.el.dom, this.ddGroup || this.group,
              {isTarget: true}]);
    },

    
    
    
    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',

    
    isTarget : true,

    
    isNotifyTarget : true,

    
    notifyEnter : function(dd, e, data){
        if(this.overClass){
            this.el.addCls(this.overClass);
        }
        return this.dropAllowed;
    },

    
    notifyOver : function(dd, e, data){
        return this.dropAllowed;
    },

    
    notifyOut : function(dd, e, data){
        if(this.overClass){
            this.el.removeCls(this.overClass);
        }
    },

    
    notifyDrop : function(dd, e, data){
        return false;
    },

    destroy : function(){
        this.callParent();
        if(this.containerScroll){
            Ext.dd.ScrollManager.unregister(this.el);
        }
    }
});


Ext.define('Ext.dd.Registry', {
    singleton: true,
    constructor: function() {
        this.elements = {}; 
        this.handles = {}; 
        this.autoIdSeed = 0;
    },
    
    getId: function(el, autogen){
        if(typeof el == "string"){
            return el;
        }
        var id = el.id;
        if(!id && autogen !== false){
            id = "extdd-" + (++this.autoIdSeed);
            el.id = id;
        }
        return id;
    },
    
    
    register : function(el, data){
        data = data || {};
        if (typeof el == "string") {
            el = document.getElementById(el);
        }
        data.ddel = el;
        this.elements[this.getId(el)] = data;
        if (data.isHandle !== false) {
            this.handles[data.ddel.id] = data;
        }
        if (data.handles) {
            var hs = data.handles,
                i, len;
            for (i = 0, len = hs.length; i < len; i++) {
                this.handles[this.getId(hs[i])] = data;
            }
        }
    },

    
    unregister : function(el){
        var id = this.getId(el, false),
            data = this.elements[id],
            hs, i, len;
        if(data){
            delete this.elements[id];
            if(data.handles){
                hs = data.handles;
                for (i = 0, len = hs.length; i < len; i++) {
                    delete this.handles[this.getId(hs[i], false)];
                }
            }
        }
    },

    
    getHandle : function(id){
        if(typeof id != "string"){ 
            id = id.id;
        }
        return this.handles[id];
    },

    
    getHandleFromEvent : function(e){
        var t = e.getTarget();
        return t ? this.handles[t.id] : null;
    },

    
    getTarget : function(id){
        if(typeof id != "string"){ 
            id = id.id;
        }
        return this.elements[id];
    },

    
    getTargetFromEvent : function(e){
        var t = e.getTarget();
        return t ? this.elements[t.id] || this.handles[t.id] : null;
    }
});


Ext.define('Ext.dd.DropZone', {
    extend:  Ext.dd.DropTarget ,
                                  

    
    getTargetFromEvent : function(e){
        return Ext.dd.Registry.getTargetFromEvent(e);
    },

    
    onNodeEnter : function(n, dd, e, data){
        
    },

    
    onNodeOver : function(n, dd, e, data){
        return this.dropAllowed;
    },

    
    onNodeOut : function(n, dd, e, data){
        
    },

    
    onNodeDrop : function(n, dd, e, data){
        return false;
    },

    
    onContainerOver : function(dd, e, data){
        return this.dropNotAllowed;
    },

    
    onContainerDrop : function(dd, e, data){
        return false;
    },

    
    notifyEnter : function(dd, e, data){
        return this.dropNotAllowed;
    },

    
    notifyOver : function(dd, e, data){
        var n = this.getTargetFromEvent(e);
        if(!n) { 
            if(this.lastOverNode){
                this.onNodeOut(this.lastOverNode, dd, e, data);
                this.lastOverNode = null;
            }
            return this.onContainerOver(dd, e, data);
        }
        if(this.lastOverNode != n){
            if(this.lastOverNode){
                this.onNodeOut(this.lastOverNode, dd, e, data);
            }
            this.onNodeEnter(n, dd, e, data);
            this.lastOverNode = n;
        }
        return this.onNodeOver(n, dd, e, data);
    },

    
    notifyOut : function(dd, e, data){
        if(this.lastOverNode){
            this.onNodeOut(this.lastOverNode, dd, e, data);
            this.lastOverNode = null;
        }
    },

    
    notifyDrop : function(dd, e, data){
        var me = this,
            n = me.getTargetFromEvent(e),
            result = n ?
                me.onNodeDrop(n, dd, e, data) :
                me.onContainerDrop(dd, e, data);

        
        
        if (me.lastOverNode) {
            me.onNodeOut(me.lastOverNode, dd, e, data);
            me.lastOverNode = null;
        }
        return result;
    },

    
    triggerCacheRefresh : function() {
        Ext.dd.DDM.refreshCache(this.groups);
    }
});


Ext.define('Ext.direct.Event', {
    alias: 'direct.event',

    status: true,

    
    constructor: function(config) {
        Ext.apply(this, config);
    },
    
    
    getName: function() {
        return this.name;
    },

    
    getData: function() {
        return this.data;
    }
});


Ext.define('Ext.direct.RemotingEvent', {
    extend:  Ext.direct.Event ,
    alias:  'direct.rpc',
    
    
    getTransaction: function() {
        var me = this;
        
        return me.transaction || Ext.direct.Manager.getTransaction(me.tid);
    }
});


Ext.define('Ext.direct.ExceptionEvent', {
    extend:  Ext.direct.RemotingEvent ,
    alias:  'direct.exception',
   
   status: false
});



Ext.define('Ext.direct.JsonProvider', {
    extend:  Ext.direct.Provider ,
    alias:  'direct.jsonprovider',

           
                                    
                            
      

   
   parseResponse: function(response) {
        if (!Ext.isEmpty(response.responseText)) {
            if (Ext.isObject(response.responseText)) {
                return response.responseText;
            }

            return Ext.decode(response.responseText);
        }

        return null;
    },

    
    createEvents: function(response) {
        var me = this,
            data = null,
            events = [],
            event, i, len;

        try {
            data = me.parseResponse(response);
        }
        catch (e) {
            event = new Ext.direct.ExceptionEvent({
                data: e,
                xhr: response,
                code: Ext.direct.Manager.exceptions.PARSE,
                message: 'Error parsing json response: \n\n ' + e
            });

            return [event];
        }

        if (Ext.isArray(data)) {
            for (i = 0, len = data.length; i < len; ++i) {
                events.push(me.createEvent(data[i]));
            }
        }
        else if (Ext.isObject(data)) {
            events.push(me.createEvent(data));
        }

        return events;
    },

    
    createEvent: function(response) {
        if (typeof response !== 'object'|| !('type' in response)) {
            return new Ext.direct.ExceptionEvent({
                data: response,
                code: Ext.direct.Manager.exceptions.DATA,
                message: 'Invalid data: event type is not specified'
            });
        }
    
        return Ext.create('direct.' + response.type, response);
    }
});


Ext.define('Ext.direct.PollingProvider', {
    extend:  Ext.direct.JsonProvider ,
    alias:  'direct.pollingprovider',
    
               
                   
                              
      
    
           
                                    
                            
      
    
    
    interval: 3000,

    
    
    

    constructor: function(config) {
        var me = this;
        
        me.callParent(arguments);
        
        me.addEvents(
            
            'beforepoll',
            
            
            'poll'
        );
    },

    
    isConnected: function() {
        return !!this.pollTask;
    },

    
    connect: function() {
        var me = this,
            url = me.url;
        
        if (url && !me.pollTask) {
            me.pollTask = Ext.TaskManager.start({
                run: me.runPoll,
                interval: me.interval,
                scope: me
            });
            
            me.fireEvent('connect', me);
        }
    },

    
    disconnect: function() {
        var me = this;
        
        if (me.pollTask) {
            Ext.TaskManager.stop(me.pollTask);
            delete me.pollTask;
            me.fireEvent('disconnect', me);
        }
    },
    
    
    runPoll: function() {
        var me = this,
            url = me.url;
        
        if (me.fireEvent('beforepoll', me) !== false) {
            if (Ext.isFunction(url)) {
                url(me.baseParams);
            }
            else {
                Ext.Ajax.request({
                    url: url,
                    callback: me.onData,
                    scope: me,
                    params: me.baseParams
                });
            }
            
            me.fireEvent('poll', me);
        }
    },

    
    onData: function(opt, success, response) {
        var me = this, 
            i, len, events;
        
        if (success) {
            events = me.createEvents(response);
            
            for (i = 0, len = events.length; i < len; ++i) {
                me.fireEvent('data', me, events[i]);
            }
        }
        else {
            events = new Ext.direct.ExceptionEvent({
                data: null,
                code: Ext.direct.Manager.exceptions.TRANSPORT,
                message: 'Unable to connect to the server.',
                xhr: response
            });
            
            me.fireEvent('data', me, events);
        }
    }
});


Ext.define('Ext.direct.RemotingMethod', {

    constructor: function(config) {
        var me = this,
            params = Ext.isDefined(config.params) ? config.params : config.len,
            name, pLen, p, param;

        me.name = config.name;
        me.formHandler = config.formHandler;

        if (Ext.isNumeric(params)) {
            
            me.len = params;
            me.ordered = true;
        }
        else {
            
            me.params = {};
			pLen = params.length;

            for (p = 0; p < pLen; p++) {
                param = params[p];
                name  = Ext.isObject(param) ? param.name : param;
                me.params[name] = true;
            }
        }
    },
    
    getArgs: function(params, paramOrder, paramsAsHash) {
        var me = this,
            args = [],
            i, len;
        
        if (me.ordered) {
            if (me.len > 0) {
                
                if (paramOrder) {
                    for (i = 0, len = paramOrder.length; i < len; i++) {
                        args.push(params[paramOrder[i]]);
                    }
                }
                else if (paramsAsHash) {
                    
                    args.push(params);
                }
            }
        }
        else {
            args.push(params);
        } 
        
        return args;
    },

    
    getCallData: function(args) {
        var me = this,
            data = null,
            len  = me.len,
            params = me.params,
            callback, scope, name, options;

        if (me.ordered) {
            callback = args[len];
            scope    = args[len + 1];
            options  = args[len + 2];
            
            if (len !== 0) {
                data = args.slice(0, len);
            }
        }
        else {
            data     = Ext.apply({}, args[0]);
            callback = args[1];
            scope    = args[2];
            options  = args[3];

            
            for (name in data) {
                if (data.hasOwnProperty(name) && !params[name]) {
                    delete data[name];
                }
            }
        }

        return {
            data: data,
            callback: callback,
            scope: scope,
            options: options
        };
    }
});


Ext.define('Ext.direct.Transaction', {
    alias: 'direct.transaction',
    alternateClassName: 'Ext.Direct.Transaction',
   
    statics: {
        TRANSACTION_ID: 0
    },
    
    
   
    
    constructor: function(config) {
        var me = this;
        
        Ext.apply(me, config);

        me.id = me.tid = ++me.self.TRANSACTION_ID;
        me.retryCount = 0;
    },
   
    send: function() {
        var me = this;
        
        me.provider.queueTransaction(me);
    },

    retry: function() {
        var me = this;
        
        me.retryCount++;
        me.send();
    },

    getProvider: function() {
        return this.provider;
    }
});


Ext.define('Ext.direct.RemotingProvider', {
    extend:  Ext.direct.JsonProvider , 
    alias:  'direct.remotingprovider',
    
               
                                    
                                
                                 
                                   
      
   
   
    
    
    
    
    
    
    
    
    
    
    enableBuffer: 10,
    
    
    maxRetries: 1,
    
    
    
    constructor: function(config) {
        var me = this;

        me.callParent(arguments);

        me.addEvents(
                        
            'beforecall',

                        
            'call',

            
            'beforecallback'
        );

        me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || Ext.global;
        me.transactions = new Ext.util.MixedCollection();
        me.callBuffer = [];
    },
    
    
    getNamespace: function(root, action) {
        var parts, ns, i, l;
        
        root  = root || Ext.global;
        parts = action.toString().split('.');

        for (i = 0, l = parts.length; i < l; i++) {
            ns   = parts[i];
            root = root[ns];

            if (typeof root === 'undefined') {
                return root;
            }
        }

        return root;
    },

    
    createNamespaces: function(root, action) {
        var parts, ns;
        
        root  = root || Ext.global;
        parts = action.toString().split('.');
        
        for ( var i = 0, l = parts.length; i < l; i++ ) {
            ns = parts[i];
            
            root[ns] = root[ns] || {};
            root     = root[ns];
        };
        
        return root;
    },
    
    
    initAPI: function() {
        var me = this,
            actions = me.actions,
            namespace = me.namespace,
            action, cls, methods, i, len, method;
            
        for (action in actions) {
            if (actions.hasOwnProperty(action)) {
                if (me.disableNestedActions) {
                    cls = namespace[action];
                    
                    if (!cls) {
                        cls = namespace[action] = {};
                    }
                }
                else {
                    cls = me.getNamespace(namespace, action);

                    if (!cls) {
                        cls = me.createNamespaces(namespace, action);
                    }
                }

                methods = actions[action];

                for (i = 0, len = methods.length; i < len; ++i) {
                    method = new Ext.direct.RemotingMethod(methods[i]);
                    cls[method.name] = me.createHandler(action, method);
                }
            }
        }
    },
    
    
    createHandler: function(action, method) {
        var me = this,
            slice = Array.prototype.slice,
            handler;
        
        if (!method.formHandler) {
            handler = function() {
                me.configureRequest(action, method, slice.call(arguments, 0));
            };
        }
        else {
            handler = function(form, callback, scope) {
                me.configureFormRequest(action, method, form, callback, scope);
            };
        }

        handler.directCfg = {
            action: action,
            method: method
        };

        return handler;
    },
    
    
    isConnected: function() {
        return !!this.connected;
    },

    
    connect: function() {
        var me = this;
        
        if (me.url) {
            me.initAPI();
            me.connected = true;
            me.fireEvent('connect', me);
        }
    },

    
    disconnect: function() {
        var me = this;
        
        if (me.connected) {
            me.connected = false;
            me.fireEvent('disconnect', me);
        }
    },
    
    
    runCallback: function(transaction, event) {
        var success = !!event.status,
            funcName = success ? 'success' : 'failure',
            callback, options, result;
        
        if (transaction && transaction.callback) {
            callback = transaction.callback;
            options  = transaction.callbackOptions;
            result   = typeof event.result !== 'undefined' ? event.result : event.data;

            if (Ext.isFunction(callback)) {
                callback(result, event, success, options);
            }
            else {
                Ext.callback(callback[funcName], callback.scope, [result, event, success, options]);
                Ext.callback(callback.callback,  callback.scope, [result, event, success, options]);
            }
        }
    },
    
    
    onData: function(options, success, response) {
        var me = this,
            i, len, events, event, transaction, transactions;
            
        if (success) {
            events = me.createEvents(response);

            for (i = 0, len = events.length; i < len; ++i) {
                event = events[i];
                transaction = me.getTransaction(event);
                me.fireEvent('data', me, event);

                if (transaction && me.fireEvent('beforecallback', me, event, transaction) !== false) {
                    me.runCallback(transaction, event, true);
                    Ext.direct.Manager.removeTransaction(transaction);
                }
            }
        }
        else {
            transactions = [].concat(options.transaction);
            
            for (i = 0, len = transactions.length; i < len; ++i) {
                transaction = me.getTransaction(transactions[i]);

                if (transaction && transaction.retryCount < me.maxRetries) {
                    transaction.retry();
                }
                else {
                    event = new Ext.direct.ExceptionEvent({
                        data: null,
                        transaction: transaction,
                        code: Ext.direct.Manager.exceptions.TRANSPORT,
                        message: 'Unable to connect to the server.',
                        xhr: response
                    });

                    me.fireEvent('data', me, event);

                    if (transaction && me.fireEvent('beforecallback', me, transaction) !== false) {
                        me.runCallback(transaction, event, false);
                        Ext.direct.Manager.removeTransaction(transaction);
                    }
                }
            }
        }
    },
    
    
    getTransaction: function(options) {
        return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
    },
    
    
    configureRequest: function(action, method, args) {
        var me = this,
            callData, data, callback, scope, opts, transaction, params;

        callData = method.getCallData(args);
        data     = callData.data;
        callback = callData.callback;
        scope    = callData.scope;
        opts     = callData.options || {};

        params = Ext.apply({}, {
            provider: me,
            args: args,
            action: action,
            method: method.name,
            data: data,
            callbackOptions: opts,
            callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback
        });

        if (opts.timeout) {
            Ext.applyIf(params, {
                timeout: opts.timeout
            });
        };

        transaction = new Ext.direct.Transaction(params);

        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            me.queueTransaction(transaction);
            me.fireEvent('call', me, transaction, method);
        }
    },
    
    
    getCallData: function(transaction) {
        return {
            action: transaction.action,
            method: transaction.method,
            data: transaction.data,
            type: 'rpc',
            tid: transaction.id
        };
    },
    
    
    sendRequest: function(data) {
        var me = this,
            request, callData, params,
            enableUrlEncode = me.enableUrlEncode,
            i, len;

        request = {
            url: me.url,
            callback: me.onData,
            scope: me,
            transaction: data,
            timeout: me.timeout
        };

        
        if (data.timeout) {
            request.timeout = data.timeout;
        }

        if (Ext.isArray(data)) {
            callData = [];

            for (i = 0, len = data.length; i < len; ++i) {
                callData.push(me.getCallData(data[i]));
            }
        }
        else {
            callData = me.getCallData(data);
        }

        if (enableUrlEncode) {
            params = {};
            params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData);
            request.params = params;
        }
        else {
            request.jsonData = callData;
        }

        Ext.Ajax.request(request);
    },
    
    
    queueTransaction: function(transaction) {
        var me = this,
            enableBuffer = me.enableBuffer;
        
        if (transaction.form) {
            me.sendFormRequest(transaction);
            return;
        }

        if (enableBuffer === false || typeof transaction.timeout !== 'undefined') {
            me.sendRequest(transaction);
            return;
        }
        
        me.callBuffer.push(transaction);

        if (enableBuffer) {
            if (!me.callTask) {
                me.callTask = new Ext.util.DelayedTask(me.combineAndSend, me);
            }

            me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
        }
        else {
            me.combineAndSend();
        }
    },
    
    
    combineAndSend : function() {
        var me = this,
            buffer = me.callBuffer,
            len = buffer.length;
            
        if (len > 0) {
            me.sendRequest(len == 1 ? buffer[0] : buffer);
            me.callBuffer = [];
        }
    },
    
    
    configureFormRequest: function(action, method, form, callback, scope) {
        var me = this,
            transaction, isUpload, params;
            
        transaction = new Ext.direct.Transaction({
            provider: me,
            action: action,
            method: method.name,
            args: [form, callback, scope],
            callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback,
            isForm: true
        });

        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data';
            
            params = {
                extTID: transaction.id,
                extAction: action,
                extMethod: method.name,
                extType: 'rpc',
                extUpload: String(isUpload)
            };
            
            
            
            Ext.apply(transaction, {
                form: Ext.getDom(form),
                isUpload: isUpload,
                params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
            });

            me.fireEvent('call', me, transaction, method);
            me.sendFormRequest(transaction);
        }
    },
    
    
    sendFormRequest: function(transaction) {
        var me = this;

        Ext.Ajax.request({
            url: me.url,
            params: transaction.params,
            callback: me.onData,
            scope: me,
            form: transaction.form,
            isUpload: transaction.isUpload,
            transaction: transaction
        });
    }
});


Ext.define('Ext.dom.Layer', {
    extend:  Ext.Element ,
                         
    alternateClassName: 'Ext.Layer',

    

    

    

    

    

    

    

    

    

    

    
    statics: {
        shims: []
    },
    
    isLayer: true,

    localXYNames: {
        get: 'getLocalXY',
        set: 'setLocalXY'
    },

    
    constructor: function(config, existingEl) {
        config = config || {};
        var me = this,
            dh = Ext.DomHelper,
            cp = config.parentEl,
            pel = cp ? Ext.getDom(cp) : document.body,
            hm = config.hideMode,
            cls = Ext.baseCSSPrefix + (config.fixed && !(Ext.isIE6 || Ext.isIEQuirks) ? 'fixed-layer' : 'layer');

        
        
        
        me.el = me;

        if (existingEl) {
            me.dom = Ext.getDom(existingEl);
        }
        if (!me.dom) {
            me.dom = dh.append(pel, config.dh || {
                tag: 'div',
                cls: cls 
            });
        } else {
            me.addCls(cls);
            if (!me.dom.parentNode) {
                pel.appendChild(me.dom);
            }
        }
        
        if (config.preventSync) {
            me.preventSync = true;
        }

        if (config.id) {
            me.id = me.dom.id = config.id;
        } else {
            me.id = Ext.id(me.dom);
        }

        Ext.Element.addToCache(me);

        if (config.cls) {
            me.addCls(config.cls);
        }
        me.constrain = config.constrain !== false;

        
        
        
        if (hm) {
            me.setVisibilityMode(Ext.Element[hm.toUpperCase()]);
            if (me.visibilityMode == Ext.Element.ASCLASS) {
                me.visibilityCls = config.visibilityCls;
            }
        } else if (config.useDisplay) {
            me.setVisibilityMode(Ext.Element.DISPLAY);
        } else {
            me.setVisibilityMode(Ext.Element.VISIBILITY);
        }

        if (config.shadow) {
            me.shadowOffset = config.shadowOffset || 4;
            me.shadow = new Ext.Shadow({
                offset: me.shadowOffset,
                mode: config.shadow,
                fixed: config.fixed
            });
            me.disableShadow();
        } else {
            me.shadowOffset = 0;
        }
        me.useShim = config.shim !== false && Ext.useShims;
        if (config.hidden === true) {
            me.hide();
        } else {
            me.show();
        }
    },

    getZIndex: function() {
        return parseInt((this.getShim() || this).getStyle('z-index'), 10);
    },

    getShim: function() {
        var me = this,
            shim, pn;

        if (!me.useShim) {
            return null;
        }
        if (!me.shim) {
            shim = me.self.shims.shift();
            if (!shim) {
                shim = me.createShim();
                shim.enableDisplayMode('block');
                shim.hide();
            }
            pn = me.dom.parentNode;
            if (shim.dom.parentNode != pn) {
                pn.insertBefore(shim.dom, me.dom);
            }
            me.shim = shim;
        }
        return me.shim;
    },

    hideShim: function() {
        var me = this;
        
        if (me.shim) {
            me.shim.setDisplayed(false);
            me.self.shims.push(me.shim);
            delete me.shim;
        }
    },

    disableShadow: function() {
        var me = this;
        
        if (me.shadow && !me.shadowDisabled) {
            me.shadowDisabled = true;
            me.shadow.hide();
            me.lastShadowOffset = me.shadowOffset;
            me.shadowOffset = 0;
        }
    },

    enableShadow: function(show) {
        var me = this;
        
        if (me.shadow && me.shadowDisabled) {
            me.shadowDisabled = false;
            me.shadowOffset = me.lastShadowOffset;
            delete me.lastShadowOffset;
            if (show) {
                me.sync(true);
            }
        }
    },

    
    sync: function(doShow) {
        var me = this,
            shadow = me.shadow,
            shadowPos, shimStyle, shadowSize,
            shim, xy, x, y, w, h, shimIndex;
            
        if (me.preventSync) {
            return;
        }

        if (!me.updating && me.isVisible() && (shadow || me.useShim)) {
            shim = me.getShim();
            xy = me[me.localXYNames.get]();
            x = xy[0];
            y = xy[1];
            w = me.dom.offsetWidth;
            h = me.dom.offsetHeight;

            if (shadow && !me.shadowDisabled) {
                if (doShow && !shadow.isVisible()) {
                    shadow.show(me);
                } else {
                    shadow.realign(x, y, w, h);
                }
                if (shim) {
                    
                    shimIndex = shim.getStyle('z-index');
                    if (shimIndex > me.zindex) {
                        me.shim.setStyle('z-index', me.zindex - 2);
                    }
                    shim.show();
                    
                    if (shadow.isVisible()) {
                        shadowPos = shadow.el.getXY();
                        shimStyle = shim.dom.style;
                        shadowSize = shadow.el.getSize();
                        if (Ext.supports.CSS3BoxShadow) {
                            shadowSize.height += 6;
                            shadowSize.width += 4;
                            shadowPos[0] -= 2;
                            shadowPos[1] -= 4;
                        }
                        shimStyle.left = (shadowPos[0]) + 'px';
                        shimStyle.top = (shadowPos[1]) + 'px';
                        shimStyle.width = (shadowSize.width) + 'px';
                        shimStyle.height = (shadowSize.height) + 'px';
                    } else {
                        shim.setSize(w, h);
                        shim[me.localXYNames.set](x, y);
                    }
                }
            } else if (shim) {
                
                shimIndex = shim.getStyle('z-index');
                if (shimIndex > me.zindex) {
                    me.shim.setStyle('z-index', me.zindex - 2);
                }
                shim.show();
                shim.setSize(w, h);
                shim[me.localXYNames.set](x, y);
            }
        }
        return me;
    },

    remove: function() {
        this.hideUnders();
        this.callParent();
    },

    
    beginUpdate: function() {
        this.updating = true;
    },

    
    endUpdate: function() {
        this.updating = false;
        this.sync(true);
    },

    
    hideUnders: function() {
        if (this.shadow) {
            this.shadow.hide();
        }
        this.hideShim();
    },

    
    constrainXY: function() {
        if (this.constrain) {
            var vw = Ext.Element.getViewWidth(),
                vh = Ext.Element.getViewHeight(),
                s = Ext.getDoc().getScroll(),
                xy = this.getXY(),
                x = xy[0],
                y = xy[1],
                so = this.shadowOffset,
                w = this.dom.offsetWidth + so,
                h = this.dom.offsetHeight + so,
                moved = false; 
            
            if ((x + w) > vw + s.left) {
                x = vw - w - so;
                moved = true;
            }
            if ((y + h) > vh + s.top) {
                y = vh - h - so;
                moved = true;
            }
            
            if (x < s.left) {
                x = s.left;
                moved = true;
            }
            if (y < s.top) {
                y = s.top;
                moved = true;
            }
            if (moved) {
                Ext.Layer.superclass.setXY.call(this, [x, y]);
                this.sync();
            }
        }
        return this;
    },

    getConstrainOffset: function() {
        return this.shadowOffset;
    },

    
    setVisible: function(visible, animate, duration, callback, easing) {
        var me = this,
            cb;

        
        cb = function() {
            if (visible) {
                me.sync(true);
            }
            if (callback) {
                callback();
            }
        };

        
        if (!visible) {
            me.hideUnders(true);
        }
        me.callParent([visible, animate, duration, callback, easing]);
        if (!animate) {
            cb();
        }
        return me;
    },

    
    beforeFx: function() {
        this.beforeAction();
        return this.callParent(arguments);
    },

    
    afterFx: function() {
        this.callParent(arguments);
        this.sync(this.isVisible());
    },

    
    beforeAction: function() {
        if (!this.updating && this.shadow) {
            this.shadow.hide();
        }
    },

    
    setLeft: function(left) {
        this.callParent(arguments);
        return this.sync();
    },

    setTop: function(top) {
        this.callParent(arguments);
        return this.sync();
    },

    setLeftTop: function(left, top) {
        this.callParent(arguments);
        return this.sync();
    },

    setLocalX: function() {
        this.callParent(arguments);
        return this.sync();
    },

    setLocalXY: function() {
        this.callParent(arguments);
        return this.sync();
    },

    setLocalY: function() {
        this.callParent(arguments);
        return this.sync();
    },

    setXY: function(xy, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.fixDisplay();
        me.beforeAction();
        me.callParent([xy, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return me;
    },

    
    createCB: function(callback) {
        var me = this,
            showShadow = me.shadow && me.shadow.isVisible();

        return function() {
            me.constrainXY();
            me.sync(showShadow);
            if (callback) {
                callback();
            }
        };
    },

    
    setX: function(x, animate, duration, callback, easing) {
        this.setXY([x, this.getY()], animate, duration, callback, easing);
        return this;
    },

    
    setY: function(y, animate, duration, callback, easing) {
        this.setXY([this.getX(), y], animate, duration, callback, easing);
        return this;
    },

    
    setSize: function(w, h, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.beforeAction();
        me.callParent([w, h, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return me;
    },

    
    setWidth: function(w, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.beforeAction();
        me.callParent([w, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return me;
    },

    
    setHeight: function(h, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.beforeAction();
        me.callParent([h, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return me;
    },

    
    setBounds: function(x, y, width, height, animate, duration, callback, easing) {
        var me = this;
        
        
        callback = me.createCB(callback);

        me.beforeAction();
        if (!animate) {
            Ext.Layer.superclass.setXY.call(me, [x, y]);
            Ext.Layer.superclass.setSize.call(me, width, height);
            callback();
        } else {
            me.callParent([x, y, width, height, animate, duration, callback, easing]);
        }
        return me;
    },

    
    setZIndex: function(zindex) {
        var me = this;
        
        me.zindex = zindex;
        if (me.getShim()) {
            me.shim.setStyle('z-index', zindex++);
        }
        if (me.shadow) {
            me.shadow.setZIndex(zindex++);
        }
        return me.setStyle('z-index', zindex);
    },
    
    onOpacitySet: function(opacity){
        var shadow = this.shadow;
        if (shadow) {
            shadow.setOpacity(opacity);
        }
    }
});


Ext.define('Ext.draw.Matrix', {

    

                                

    

    constructor: function(a, b, c, d, e, f) {
        if (a != null) {
            this.matrix = [[a, c, e], [b, d, f], [0, 0, 1]];
        }
        else {
            this.matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
        }
    },

    add: function(a, b, c, d, e, f) {
        var me = this,
            out = [[], [], []],
            matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
            x,
            y,
            z,
            res;

        for (x = 0; x < 3; x++) {
            for (y = 0; y < 3; y++) {
                res = 0;
                for (z = 0; z < 3; z++) {
                    res += me.matrix[x][z] * matrix[z][y];
                }
                out[x][y] = res;
            }
        }
        me.matrix = out;
    },

    prepend: function(a, b, c, d, e, f) {
        var me = this,
            out = [[], [], []],
            matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
            x,
            y,
            z,
            res;

        for (x = 0; x < 3; x++) {
            for (y = 0; y < 3; y++) {
                res = 0;
                for (z = 0; z < 3; z++) {
                    res += matrix[x][z] * me.matrix[z][y];
                }
                out[x][y] = res;
            }
        }
        me.matrix = out;
    },

    invert: function() {
        var matrix = this.matrix,
            a = matrix[0][0],
            b = matrix[1][0],
            c = matrix[0][1],
            d = matrix[1][1],
            e = matrix[0][2],
            f = matrix[1][2],
            x = a * d - b * c;
        return new Ext.draw.Matrix(d / x, -b / x, -c / x, a / x, (c * f - d * e) / x, (b * e - a * f) / x);
    },

    clone: function() {
        var matrix = this.matrix,
            a = matrix[0][0],
            b = matrix[1][0],
            c = matrix[0][1],
            d = matrix[1][1],
            e = matrix[0][2],
            f = matrix[1][2];
        return new Ext.draw.Matrix(a, b, c, d, e, f);
    },

    translate: function(x, y) {
        this.prepend(1, 0, 0, 1, x, y);
    },

    scale: function(x, y, cx, cy) {
        var me = this;
        if (y == null) {
            y = x;
        }
        me.add(x, 0, 0, y, cx * (1 - x), cy * (1 - y));
    },

    rotate: function(a, x, y) {
        a = Ext.draw.Draw.rad(a);
        var me = this,
            cos = +Math.cos(a).toFixed(9),
            sin = +Math.sin(a).toFixed(9);
        me.add(cos, sin, -sin, cos, x - cos * x + sin * y, -(sin * x) + y - cos * y);
    },

    x: function(x, y) {
        var matrix = this.matrix;
        return x * matrix[0][0] + y * matrix[0][1] + matrix[0][2];
    },

    y: function(x, y) {
        var matrix = this.matrix;
        return x * matrix[1][0] + y * matrix[1][1] + matrix[1][2];
    },

    get: function(i, j) {
        return + this.matrix[i][j].toFixed(4);
    },

    toString: function() {
        var me = this;
        return [me.get(0, 0), me.get(0, 1), me.get(1, 0), me.get(1, 1), 0, 0].join();
    },

    toSvg: function() {
        var me = this;
        return "matrix(" + [me.get(0, 0), me.get(1, 0), me.get(0, 1), me.get(1, 1), me.get(0, 2), me.get(1, 2)].join() + ")";
    },

    toFilter: function(dx, dy) {
        var me = this;
        dx = dx || 0;
        dy = dy || 0;
        return "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', filterType='bilinear', M11=" + me.get(0, 0) +
            ", M12=" + me.get(0, 1) + ", M21=" + me.get(1, 0) + ", M22=" + me.get(1, 1) +
            ", Dx=" + (me.get(0, 2) + dx) + ", Dy=" + (me.get(1, 2) + dy) + ")";
    },

    offset: function() {
        var matrix = this.matrix;
        return [(matrix[0][2] || 0).toFixed(4), (matrix[1][2] || 0).toFixed(4)];
    },

    
    split: function () {
        function norm(a) {
            return a[0] * a[0] + a[1] * a[1];
        }
        function normalize(a) {
            var mag = Math.sqrt(norm(a));
            a[0] /= mag;
            a[1] /= mag;
        }
        var matrix = this.matrix,
            out = {
                translateX: matrix[0][2],
                translateY: matrix[1][2]
            },
            row;

        
        row = [[matrix[0][0], matrix[0][1]], [matrix[1][1], matrix[1][1]]];
        out.scaleX = Math.sqrt(norm(row[0]));
        normalize(row[0]);

        out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
        row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];

        out.scaleY = Math.sqrt(norm(row[1]));
        normalize(row[1]);
        out.shear /= out.scaleY;

        
        out.rotate = Math.asin(-row[0][1]);

        out.isSimple = !+out.shear.toFixed(9) && (out.scaleX.toFixed(9) == out.scaleY.toFixed(9) || !out.rotate);

        return out;
    }
});


Ext.define('Ext.draw.SpriteDD', {
    extend:  Ext.dd.DragSource ,

    constructor : function(sprite, cfg){
        var me = this,
            el = sprite.el;
        me.sprite = sprite;
        me.el = el;
        me.dragData = {el: el, sprite: sprite};
        me.callParent([el, cfg]);
        me.sprite.setStyle('cursor', 'move');
    },

    showFrame: Ext.emptyFn,
    createFrame : Ext.emptyFn,

    getDragEl : function(e){
        return this.el;
    },
    
    getRegion: function() {
        var me = this,
            el = me.el,
            pos, x1, x2, y1, y2, t, r, b, l, bbox, sprite;
        
        sprite = me.sprite;
        bbox = sprite.getBBox();
        
        try {
            pos = Ext.Element.getXY(el);
        } catch (e) { }

        if (!pos) {
            return null;
        }

        x1 = pos[0];
        x2 = x1 + bbox.width;
        y1 = pos[1];
        y2 = y1 + bbox.height;
        
        return new Ext.util.Region(y1, x2, y2, x1);
    },

    
     
    startDrag: function(x, y) {
        var me = this,
            attr = me.sprite.attr;
        me.prev = me.sprite.surface.transformToViewBox(x, y);
    },

    onDrag: function(e) {
        var xy = e.getXY(),
            me = this,
            sprite = me.sprite,
            attr = sprite.attr, dx, dy;
        xy = me.sprite.surface.transformToViewBox(xy[0], xy[1]);
        dx = xy[0] - me.prev[0];
        dy = xy[1] - me.prev[1];
        sprite.setAttributes({
            translate: {
                x: attr.translation.x + dx,
                y: attr.translation.y + dy
            }
        }, true);
        me.prev = xy;
    },

    setDragElPos: function () {
        
        return false;
    }
});


Ext.define('Ext.draw.Sprite', {

    

    mixins: {
        observable:  Ext.util.Observable ,
        animate:  Ext.util.Animate 
    },

                                    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    dirty: false,
    dirtyHidden: false,
    dirtyTransform: false,
    dirtyPath: true,
    dirtyFont: true,
    zIndexDirty: true,

    
    isSprite: true,
    zIndex: 0,
    fontProperties: [
        'font',
        'font-size',
        'font-weight',
        'font-style',
        'font-family',
        'text-anchor',
        'text'
    ],
    pathProperties: [
        'x',
        'y',
        'd',
        'path',
        'height',
        'width',
        'radius',
        'r',
        'rx',
        'ry',
        'cx',
        'cy'
    ],
    constructor: function(config) {
        var me = this;
        config = Ext.merge({}, config || {});
        me.id = Ext.id(null, 'ext-sprite-');
        me.transformations = [];
        Ext.copyTo(this, config, 'surface,group,type,draggable');
        
        me.bbox = {};
        me.attr = {
            zIndex: 0,
            translation: {
                x: null,
                y: null
            },
            rotation: {
                degrees: null,
                x: null,
                y: null
            },
            scaling: {
                x: null,
                y: null,
                cx: null,
                cy: null
            }
        };
        
        delete config.surface;
        delete config.group;
        delete config.type;
        delete config.draggable;
        me.setAttributes(config);
        me.addEvents(
            
            'beforedestroy',
            
            'destroy',
            
            'render',
            
            'mousedown',
            
            'mouseup',
            
            'mouseover',
            
            'mouseout',
            
            'mousemove',
            
            'click'
        );
        me.mixins.observable.constructor.apply(this, arguments);
    },

    

    initDraggable: function() {
        var me = this;
        
        if (!me.el) {
            me.surface.createSpriteElement(me);
        }
        me.dd = new Ext.draw.SpriteDD(me, Ext.isBoolean(me.draggable) ? null : me.draggable);
        me.on('beforedestroy', me.dd.destroy, me.dd);
    },

    
    setAttributes: function(attrs, redraw) {
        var me = this,
            fontProps = me.fontProperties,
            fontPropsLength = fontProps.length,
            pathProps = me.pathProperties,
            pathPropsLength = pathProps.length,
            hasSurface = !!me.surface,
            custom = hasSurface && me.surface.customAttributes || {},
            spriteAttrs = me.attr,
            dirtyBBox = false,
            attr, i, newTranslation, translation, newRotate, rotation, newScaling, scaling;

        attrs = Ext.apply({}, attrs);
        for (attr in custom) {
            if (attrs.hasOwnProperty(attr) && typeof custom[attr] == "function") {
                Ext.apply(attrs, custom[attr].apply(me, [].concat(attrs[attr])));
            }
        }

        
        if (!!attrs.hidden !== !!spriteAttrs.hidden) {
            me.dirtyHidden = true;
        }

        
        for (i = 0; i < pathPropsLength; i++) {
            attr = pathProps[i];
            if (attr in attrs && attrs[attr] !== spriteAttrs[attr]) {
                me.dirtyPath = true;
                dirtyBBox = true;
                break;
            }
        }

        
        if ('zIndex' in attrs) {
            me.zIndexDirty = true;
        }

        
        if ('text' in attrs) {
            me.dirtyFont = true;
            dirtyBBox = true;
        }

        for (i = 0; i < fontPropsLength; i++) {
            attr = fontProps[i];
            if (attr in attrs && attrs[attr] !== spriteAttrs[attr]) {
                me.dirtyFont = true;
                dirtyBBox = true;
                break;
            }
        }

        newTranslation = attrs.translation || attrs.translate;
        delete attrs.translate;
        delete attrs.translation;
        translation = spriteAttrs.translation;
        if (newTranslation) {
            if (('x' in newTranslation && newTranslation.x !== translation.x) ||
                ('y' in newTranslation && newTranslation.y !== translation.y)) {
                me.dirtyTransform = true;
                translation.x = newTranslation.x;
                translation.y = newTranslation.y;
            }
        }

        newRotate = attrs.rotation || attrs.rotate;
        rotation = spriteAttrs.rotation;
        delete attrs.rotate;
        delete attrs.rotation;
        if (newRotate) {
            if (('x' in newRotate && newRotate.x !== rotation.x) ||
                ('y' in newRotate && newRotate.y !== rotation.y) ||
                ('degrees' in newRotate && newRotate.degrees !== rotation.degrees)) {
                me.dirtyTransform = true;
                rotation.x = newRotate.x;
                rotation.y = newRotate.y;
                rotation.degrees = newRotate.degrees;
            }
        }

        newScaling = attrs.scaling || attrs.scale;
        scaling = spriteAttrs.scaling;
        delete attrs.scale;
        delete attrs.scaling;
        if (newScaling) {
            if (('x' in newScaling && newScaling.x !== scaling.x) ||
                ('y' in newScaling && newScaling.y !== scaling.y) ||
                ('cx' in newScaling && newScaling.cx !== scaling.cx) ||
                ('cy' in newScaling && newScaling.cy !== scaling.cy)) {
                me.dirtyTransform = true;
                scaling.x = newScaling.x;
                scaling.y = newScaling.y;
                scaling.cx = newScaling.cx;
                scaling.cy = newScaling.cy;
            }
        }

        
        if (!me.dirtyTransform && dirtyBBox) {
            if (spriteAttrs.scaling.x === null ||
                spriteAttrs.scaling.y === null ||
                spriteAttrs.rotation.y === null ||
                spriteAttrs.rotation.y === null) {
                me.dirtyTransform = true;
            }
        }

        Ext.apply(spriteAttrs, attrs);
        me.dirty = true;

        if (redraw === true && hasSurface) {
            me.redraw();
        }
        return this;
    },

    
    getBBox: function() {
        return this.surface.getBBox(this);
    },

    setText: function(text) {
        return this.surface.setText(this, text);
    },

    
    hide: function(redraw) {
        this.setAttributes({
            hidden: true
        }, redraw);
        return this;
    },

    
    show: function(redraw) {
        this.setAttributes({
            hidden: false
        }, redraw);
        return this;
    },

    
    remove: function() {
        if (this.surface) {
            this.surface.remove(this);
            return true;
        }
        return false;
    },

    onRemove: function() {
        this.surface.onRemove(this);
    },

    
    destroy: function() {
        var me = this;
        if (me.fireEvent('beforedestroy', me) !== false) {
            me.remove();
            me.surface.onDestroy(me);
            me.clearListeners();
            me.fireEvent('destroy');
        }
    },

    
    redraw: function() {
        this.surface.renderItem(this);
        return this;
    },

    
    setStyle: function() {
        this.el.setStyle.apply(this.el, arguments);
        return this;
    },

    
    addCls: function(obj) {
        this.surface.addCls(this, obj);
        return this;
    },

    
    removeCls: function(obj) {
        this.surface.removeCls(this, obj);
        return this;
    }
});


Ext.define('Ext.draw.Text', {
    extend:  Ext.draw.Component ,
                           
    alias: 'widget.text',

    
    text: '',

    

    

    focusable: false,
    viewBox: false,
    autoSize: true,
    baseCls: Ext.baseCSSPrefix + 'surface ' + Ext.baseCSSPrefix + 'draw-text',

    initComponent: function() {
        var me = this;

        me.textConfig = Ext.apply({
            type: 'text',
            text: me.text,
            rotate: {
                degrees: me.degrees || 0
            }
        }, me.textStyle);
        Ext.apply(me.textConfig, me.getStyles(me.styleSelectors || me.styleSelector));

        
        
        me.initialConfig.items = [me.textConfig];
        me.callParent(arguments);
    },

    
    getStyles: function(selectors) {
        selectors = Ext.Array.from(selectors);
        var i = 0,
            len = selectors.length,
            rule,
            style,
            prop,
            result = {};

        for (; i < len; i++) {
            
            rule = Ext.util.CSS.getRule(selectors[i]);
            if (rule) {
                style = rule.style;
                if (style) {
                    Ext.apply(result, {
                        'font-family': style.fontFamily,
                        'font-weight': style.fontWeight,
                        'line-height': style.lineHeight,
                        'font-size': style.fontSize,
                        fill: style.color
                    });
                }
            }
        }
        return result;
    },

    
    setAngle: function(degrees) {
        var me = this,
            surface,
            sprite;
            
        if (me.rendered) {
            surface = me.surface;
            sprite = surface.items.items[0];

            me.degrees = degrees;
            sprite.setAttributes({
                rotate: {
                    degrees: degrees
                }
            }, true);
            if (me.autoSize || me.viewBox) {
                me.updateLayout();
            }
        } else {
            me.degrees = degrees;
        }
    },

    
    setText: function(text) {
        var me = this,
            surface,
            sprite;
            
        if (me.rendered) {
            surface = me.surface;
            sprite = surface.items.items[0];

            me.text = text || '';
            surface.remove(sprite);
            me.textConfig.type = 'text';
            me.textConfig.text = me.text;
            sprite = surface.add(me.textConfig);
            sprite.setAttributes({
                rotate: {
                    degrees: me.degrees
                }
            }, true);
            if (me.autoSize || me.viewBox) {
                me.updateLayout();
            }
        } else {
            me.on({
                render: function() {
                    me.setText(text);
                },
                single: true
            });
        }
    }
});


Ext.define('Ext.draw.engine.ImageExporter', {
    singleton: true,

    
    defaultUrl: 'http://svg.sencha.io',

    
    supportedTypes: ['image/png', 'image/jpeg'],

    
    widthParam: 'width',

    
    heightParam: 'height',

    
    typeParam: 'type',

    
    svgParam: 'svg',

    formCls: Ext.baseCSSPrefix + 'hide-display',

    
    generate: function(surface, config) {
        config = config || {};
        var me = this,
            type = config.type,
            form;

        if (Ext.Array.indexOf(me.supportedTypes, type) === -1) {
            return false;
        }

        form = Ext.getBody().createChild({
            tag: 'form',
            method: 'POST',
            action: config.url || me.defaultUrl,
            cls: me.formCls,
            children: [{
                tag: 'input',
                type: 'hidden',
                name: config.widthParam || me.widthParam,
                value: config.width || surface.width
            }, {
                tag: 'input',
                type: 'hidden',
                name: config.heightParam || me.heightParam,
                value: config.height || surface.height
            }, {
                tag: 'input',
                type: 'hidden',
                name: config.typeParam || me.typeParam,
                value: type
            }, {
                tag: 'input',
                type: 'hidden',
                name: config.svgParam || me.svgParam
            }]
        });

        
        form.last(null, true).value = Ext.draw.engine.SvgExporter.generate(surface);

        form.dom.submit();
        form.remove();
        return true;
    }

});


Ext.define('Ext.draw.engine.Svg', {

    

    extend:  Ext.draw.Surface ,

                                                                                     

    

    engine: 'Svg',

    trimRe: /^\s+|\s+$/g,
    spacesRe: /\s+/,
    xlink: "http:/" + "/www.w3.org/1999/xlink",

    translateAttrs: {
        radius: "r",
        radiusX: "rx",
        radiusY: "ry",
        path: "d",
        lineWidth: "stroke-width",
        fillOpacity: "fill-opacity",
        strokeOpacity: "stroke-opacity",
        strokeLinejoin: "stroke-linejoin"
    },

    parsers: {},

    minDefaults: {
        circle: {
            cx: 0,
            cy: 0,
            r: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        ellipse: {
            cx: 0,
            cy: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        rect: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        text: {
            x: 0,
            y: 0,
            "text-anchor": "start",
            "font-family": null,
            "font-size": null,
            "font-weight": null,
            "font-style": null,
            fill: "#000",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        path: {
            d: "M0,0",
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        image: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            preserveAspectRatio: "none",
            opacity: null
        }
    },

    createSvgElement: function(type, attrs) {
        var el = this.domRef.createElementNS("http:/" + "/www.w3.org/2000/svg", type),
            key;
        if (attrs) {
            for (key in attrs) {
                el.setAttribute(key, String(attrs[key]));
            }
        }
        return el;
    },

    createSpriteElement: function(sprite) {
        
        var el = this.createSvgElement(sprite.type);
        el.id = sprite.id;
        if (el.style) {
            el.style.webkitTapHighlightColor = "rgba(0,0,0,0)";
        }
        sprite.el = Ext.get(el);
        this.applyZIndex(sprite); 
        sprite.matrix = new Ext.draw.Matrix();
        sprite.bbox = {
            plain: 0,
            transform: 0
        };
        this.applyAttrs(sprite);
        this.applyTransformations(sprite);
        sprite.fireEvent("render", sprite);
        return el;
    },
    
    getBBoxText: function (sprite) {
        var bbox = {},
            bb, height, width, i, ln, el;

        if (sprite && sprite.el) {
            el = sprite.el.dom;
            try {
                bbox = el.getBBox();
                return bbox;
            } catch(e) {
                
            }
            bbox = {x: bbox.x, y: Infinity, width: 0, height: 0};
            ln = el.getNumberOfChars();
            for (i = 0; i < ln; i++) {
                bb = el.getExtentOfChar(i);
                bbox.y = Math.min(bb.y, bbox.y);
                height = bb.y + bb.height - bbox.y;
                bbox.height = Math.max(bbox.height, height);
                width = bb.x + bb.width - bbox.x;
                bbox.width = Math.max(bbox.width, width);
            }
            return bbox;
        }
    },

    hide: function() {
        Ext.get(this.el).hide();
    },

    show: function() {
        Ext.get(this.el).show();
    },

    hidePrim: function(sprite) {
        this.addCls(sprite, Ext.baseCSSPrefix + 'hide-visibility');
    },

    showPrim: function(sprite) {
        this.removeCls(sprite, Ext.baseCSSPrefix + 'hide-visibility');
    },

    getDefs: function() {
        return this._defs || (this._defs = this.createSvgElement("defs"));
    },

    transform: function(sprite, matrixOnly) {
        var me = this,
            matrix = new Ext.draw.Matrix(),
            transforms = sprite.transformations,
            transformsLength = transforms.length,
            i = 0,
            transform, type;
            
        for (; i < transformsLength; i++) {
            transform = transforms[i];
            type = transform.type;
            if (type == "translate") {
                matrix.translate(transform.x, transform.y);
            }
            else if (type == "rotate") {
                matrix.rotate(transform.degrees, transform.x, transform.y);
            }
            else if (type == "scale") {
                matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY);
            }
        }
        sprite.matrix = matrix;
        if (!matrixOnly) {
            sprite.el.set({transform: matrix.toSvg()});
        }
    },

    setSize: function(width, height) {
        var me = this,
            el = me.el;
        
        width = +width || me.width;
        height = +height || me.height;
        me.width = width;
        me.height = height;

        el.setSize(width, height);
        el.set({
            width: width,
            height: height
        });
        me.callParent([width, height]);
    },

    
    getRegion: function() {
        
        
        var svgXY = this.el.getXY(),
            rectXY = this.bgRect.getXY(),
            max = Math.max,
            x = max(svgXY[0], rectXY[0]),
            y = max(svgXY[1], rectXY[1]);
        return {
            left: x,
            top: y,
            right: x + this.width,
            bottom: y + this.height
        };
    },

    onRemove: function(sprite) {
        if (sprite.el) {
            sprite.el.destroy();
            delete sprite.el;
        }
        this.callParent(arguments);
    },
    
    setViewBox: function(x, y, width, height) {
        if (isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height)) {
            this.callParent(arguments);
            this.el.dom.setAttribute("viewBox", [x, y, width, height].join(" "));
        }
    },

    render: function (container) {
        var me = this,
            width,
            height,
            el,
            defs,
            bgRect,
            webkitRect;
        if (!me.el) {
            width = me.width || 0;
            height = me.height || 0;
            el = me.createSvgElement('svg', {
                xmlns: "http:/" + "/www.w3.org/2000/svg",
                version: 1.1,
                width: width,
                height: height
            });
            defs = me.getDefs();

            
            
            
            
            bgRect = me.createSvgElement("rect", {
                width: "100%",
                height: "100%",
                fill: "#000",
                stroke: "none",
                opacity: 0
            });
            
            if (Ext.isSafari3) {
                
                webkitRect = me.createSvgElement("rect", {
                    x: -10,
                    y: -10,
                    width: "110%",
                    height: "110%",
                    fill: "none",
                    stroke: "#000"
                });
            }
            el.appendChild(defs);
            if (Ext.isSafari3) {
                el.appendChild(webkitRect);
            }
            el.appendChild(bgRect);
            container.appendChild(el);
            me.el = Ext.get(el);
            me.bgRect = Ext.get(bgRect);
            if (Ext.isSafari3) {
                me.webkitRect = Ext.get(webkitRect);
                me.webkitRect.hide();
            }
            me.el.on({
                scope: me,
                mouseup: me.onMouseUp,
                mousedown: me.onMouseDown,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousemove: me.onMouseMove,
                mouseenter: me.onMouseEnter,
                mouseleave: me.onMouseLeave,
                click: me.onClick,
                dblclick: me.onDblClick
            });
        }
        me.renderAll();
    },

    
    onMouseEnter: function(e) {
        if (this.el.parent().getRegion().contains(e.getPoint())) {
            this.fireEvent('mouseenter', e);
        }
    },

    
    onMouseLeave: function(e) {
        if (!this.el.parent().getRegion().contains(e.getPoint())) {
            this.fireEvent('mouseleave', e);
        }
    },
    
    processEvent: function(name, e) {
        var target = e.getTarget(),
            surface = this.surface,
            sprite;

        this.fireEvent(name, e);
        
        if (target.nodeName == "tspan" && target.parentNode) {
            target = target.parentNode;
        }
        sprite = this.items.get(target.id);
        if (sprite) {
            sprite.fireEvent(name, sprite, e);
        }
    },

    
    tuneText: function (sprite, attrs) {
        var el = sprite.el.dom,
            tspans = [],
            height, tspan, text, i, ln, texts, factor, x;

        if (attrs.hasOwnProperty("text")) {
            
            
            

            
            text = sprite.tspans && Ext.Array.map(sprite.tspans, function(t) { return t.textContent; }).join('');

            if (!sprite.tspans || attrs.text != text) {
                tspans = this.setText(sprite, attrs.text);
                sprite.tspans = tspans;
            
            } else {
                tspans = sprite.tspans || [];
            }
        }
        
        if (tspans.length) {
            height = this.getBBoxText(sprite).height;
            x = sprite.el.dom.getAttribute("x");
            for (i = 0, ln = tspans.length; i < ln; i++) {
                
                
                factor = (Ext.isFF3_0 || Ext.isFF3_5) ? 2 : 4;
                tspans[i].setAttribute("x", x);
                tspans[i].setAttribute("dy", i ? height * 1.2 : height / factor);
            }
            sprite.dirty = true;
        }
    },

    setText: function(sprite, textString) {
         var me = this,
             el = sprite.el.dom,
             tspans = [],
             height, tspan, text, i, ln, texts;
        
        while (el.firstChild) {
            el.removeChild(el.firstChild);
        }
        
        texts = String(textString).split("\n");
        for (i = 0, ln = texts.length; i < ln; i++) {
            text = texts[i];
            if (text) {
                tspan = me.createSvgElement("tspan");
                tspan.appendChild(document.createTextNode(Ext.htmlDecode(text)));
                el.appendChild(tspan);
                tspans[i] = tspan;
            }
        }
        return tspans;
    },

    renderAll: function() {
        this.items.each(this.renderItem, this);
    },

    renderItem: function (sprite) {
        if (!this.el) {
            return;
        }
        if (!sprite.el) {
            this.createSpriteElement(sprite);
        }
        if (sprite.zIndexDirty) {
            this.applyZIndex(sprite);
        }
        if (sprite.dirty) {
            this.applyAttrs(sprite);
            if (sprite.dirtyTransform) {
                this.applyTransformations(sprite);
            }
        }
    },

    redraw: function(sprite) {
        sprite.dirty = sprite.zIndexDirty = true;
        this.renderItem(sprite);
    },

    applyAttrs: function (sprite) {
        var me = this,
            el = sprite.el,
            group = sprite.group,
            sattr = sprite.attr,
            parsers = me.parsers,
            
            
            
            gradientsMap = me.gradientsMap || {},
            safariFix = Ext.isSafari && !Ext.isStrict,
            groups, i, ln, attrs, font, key, style, name, rect;

        if (group) {
            groups = [].concat(group);
            ln = groups.length;
            for (i = 0; i < ln; i++) {
                group = groups[i];
                me.getGroup(group).add(sprite);
            }
            delete sprite.group;
        }
        attrs = me.scrubAttrs(sprite) || {};

        
            sprite.bbox.plain = 0;
            sprite.bbox.transform = 0;
            if (sprite.type == "circle" || sprite.type == "ellipse") {
                attrs.cx = attrs.cx || attrs.x;
                attrs.cy = attrs.cy || attrs.y;
            }
            else if (sprite.type == "rect") {
                attrs.rx = attrs.ry = attrs.r;
            }
            else if (sprite.type == "path" && attrs.d) {
                attrs.d = Ext.draw.Draw.pathToString(Ext.draw.Draw.pathToAbsolute(attrs.d));
            }
            sprite.dirtyPath = false;
        
        
        
        

        if (attrs['clip-rect']) {
            me.setClip(sprite, attrs);
            delete attrs['clip-rect'];
        }
        if (sprite.type == 'text' && attrs.font && sprite.dirtyFont) {
            el.set({ style: "font: " + attrs.font});
        }
        if (sprite.type == "image") {
            el.dom.setAttributeNS(me.xlink, "href", attrs.src);
        }
        Ext.applyIf(attrs, me.minDefaults[sprite.type]);

        if (sprite.dirtyHidden) {
            (sattr.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite);
            sprite.dirtyHidden = false;
        }
        for (key in attrs) {
            if (attrs.hasOwnProperty(key) && attrs[key] != null) {
                
                
                
                
                
                if (safariFix && ('color|stroke|fill'.indexOf(key) > -1) && (attrs[key] in gradientsMap)) {
                    attrs[key] = gradientsMap[attrs[key]];
                }
                
                if (key == 'hidden' && sprite.type == 'text') {
                    continue;
                }
                if (key in parsers) {
                    el.dom.setAttribute(key, parsers[key](attrs[key], sprite, me));
                } else {
                    el.dom.setAttribute(key, attrs[key]);
                }
            }
        }
        
        if (sprite.type == 'text') {
            me.tuneText(sprite, attrs);
        }
        sprite.dirtyFont = false;

        
        style = sattr.style;
        if (style) {
            el.setStyle(style);
        }

        sprite.dirty = false;

        if (Ext.isSafari3) {
            
            me.webkitRect.show();
            setTimeout(function () {
                me.webkitRect.hide();
            });
        }
    },

    setClip: function(sprite, params) {
        var me = this,
            rect = params["clip-rect"],
            clipEl, clipPath;
        if (rect) {
            if (sprite.clip) {
                sprite.clip.parentNode.parentNode.removeChild(sprite.clip.parentNode);
            }
            clipEl = me.createSvgElement('clipPath');
            clipPath = me.createSvgElement('rect');
            clipEl.id = Ext.id(null, 'ext-clip-');
            clipPath.setAttribute("x", rect.x);
            clipPath.setAttribute("y", rect.y);
            clipPath.setAttribute("width", rect.width);
            clipPath.setAttribute("height", rect.height);
            clipEl.appendChild(clipPath);
            me.getDefs().appendChild(clipEl);
            sprite.el.dom.setAttribute("clip-path", "url(#" + clipEl.id + ")");
            sprite.clip = clipPath;
        } 
        
        
        
        
        
        
    },

    
    applyZIndex: function(sprite) {
        var me = this,
            items = me.items,
            idx = items.indexOf(sprite),
            el = sprite.el,
            prevEl;
        if (me.el.dom.childNodes[idx + 2] !== el.dom) { 
            if (idx > 0) {
                
                do {
                    prevEl = items.getAt(--idx).el;
                } while (!prevEl && idx > 0);
            }
            el.insertAfter(prevEl || me.bgRect);
        }
        sprite.zIndexDirty = false;
    },

    createItem: function (config) {
        var sprite = new Ext.draw.Sprite(config);
        sprite.surface = this;
        return sprite;
    },

    addGradient: function(gradient) {
        gradient = Ext.draw.Draw.parseGradient(gradient);
        var me = this,
            ln = gradient.stops.length,
            vector = gradient.vector,
            
            
            
            usePlain = Ext.isSafari && !Ext.isStrict,
            gradientEl, stop, stopEl, i, gradientsMap;
            
        gradientsMap = me.gradientsMap || {};
        
        if (!usePlain) {
            if (gradient.type == "linear") {
                gradientEl = me.createSvgElement("linearGradient");
                gradientEl.setAttribute("x1", vector[0]);
                gradientEl.setAttribute("y1", vector[1]);
                gradientEl.setAttribute("x2", vector[2]);
                gradientEl.setAttribute("y2", vector[3]);
            }
            else {
                gradientEl = me.createSvgElement("radialGradient");
                gradientEl.setAttribute("cx", gradient.centerX);
                gradientEl.setAttribute("cy", gradient.centerY);
                gradientEl.setAttribute("r", gradient.radius);
                if (Ext.isNumber(gradient.focalX) && Ext.isNumber(gradient.focalY)) {
                    gradientEl.setAttribute("fx", gradient.focalX);
                    gradientEl.setAttribute("fy", gradient.focalY);
                }
            }
            gradientEl.id = gradient.id;
            me.getDefs().appendChild(gradientEl);
            for (i = 0; i < ln; i++) {
                stop = gradient.stops[i];
                stopEl = me.createSvgElement("stop");
                stopEl.setAttribute("offset", stop.offset + "%");
                stopEl.setAttribute("stop-color", stop.color);
                stopEl.setAttribute("stop-opacity",stop.opacity);
                gradientEl.appendChild(stopEl);
            }
        } else {
            gradientsMap['url(#' + gradient.id + ')'] = gradient.stops[0].color;
        }
        me.gradientsMap = gradientsMap;
    },

    
    hasCls: function(sprite, className) {
        return className && (' ' + (sprite.el.dom.getAttribute('class') || '') + ' ').indexOf(' ' + className + ' ') != -1;
    },

    addCls: function(sprite, className) {
        var el = sprite.el,
            i,
            len,
            v,
            cls = [],
            curCls =  el.getAttribute('class') || '';
        
        if (!Ext.isArray(className)) {
            if (typeof className == 'string' && !this.hasCls(sprite, className)) {
                el.set({ 'class': curCls + ' ' + className });
            }
        }
        else {
            for (i = 0, len = className.length; i < len; i++) {
                v = className[i];
                if (typeof v == 'string' && (' ' + curCls + ' ').indexOf(' ' + v + ' ') == -1) {
                    cls.push(v);
                }
            }
            if (cls.length) {
                el.set({ 'class': ' ' + cls.join(' ') });
            }
        }
    },

    removeCls: function(sprite, className) {
        var me = this,
            el = sprite.el,
            curCls =  el.getAttribute('class') || '',
            i, idx, len, cls, elClasses;
        if (!Ext.isArray(className)){
            className = [className];
        }
        if (curCls) {
            elClasses = curCls.replace(me.trimRe, ' ').split(me.spacesRe);
            for (i = 0, len = className.length; i < len; i++) {
                cls = className[i];
                if (typeof cls == 'string') {
                    cls = cls.replace(me.trimRe, '');
                    idx = Ext.Array.indexOf(elClasses, cls);
                    if (idx != -1) {
                        Ext.Array.erase(elClasses, idx, 1);
                    }
                }
            }
            el.set({ 'class': elClasses.join(' ') });
        }
    },

    destroy: function() {
        var me = this;
        
        me.callParent();
        if (me.el) {
            me.el.remove();
        }
        if (me._defs) {
            Ext.get(me._defs).destroy();
        }
        if (me.bgRect) {
            Ext.get(me.bgRect).destroy();
        }
        if (me.webkitRect) {
            Ext.get(me.webkitRect).destroy();
        }
        delete me.el;
    }
});


Ext.define('Ext.draw.engine.SvgExporter', function(){
   var commaRe = /,/g,
       fontRegex = /(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)\s('*.*'*)/,
       rgbColorRe = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
       rgbaColorRe = /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,([\d\.]+)\)/g,
       surface, len, width, height,

   init = function(s){
       surface = s;
       len = surface.length;
       width = surface.width;
       height = surface.height;
   },
   spriteProcessor = {
       path: function(sprite){

           var attr = sprite.attr,
               path = attr.path,
               pathString = '',
               props, p, pLen;

           if (Ext.isArray(path[0])) {
               pLen = path.length;
               for (p = 0; p < pLen; p++) {
                   pathString += path[p].join(' ');
               }
           } else if (Ext.isArray(path)) {
               pathString = path.join(' ');
           } else {
               pathString = path.replace(commaRe,' ');
           }

           props = toPropertyString({
               d: pathString,
               fill: attr.fill || 'none',
               stroke: attr.stroke,
               'fill-opacity': attr.opacity,
               'stroke-width': attr['stroke-width'],
               'stroke-opacity': attr['stroke-opacity'],
               "z-index": attr.zIndex,
               transform: sprite.matrix.toSvg()
           });

           return '<path ' + props + '/>';
       },
       text: function(sprite){

           
           

           var attr = sprite.attr,
               match = fontRegex.exec(attr.font),
               size = (match && match[1]) || "12",
               
               family = (match && match[3]) || 'Arial',
               text = attr.text,
               factor = (Ext.isFF3_0 || Ext.isFF3_5) ? 2 : 4,
               tspanString = '',
               props;

           sprite.getBBox();
           tspanString += '<tspan x="' + (attr.x || '') + '" dy="';
           tspanString += (size/factor)+'">';
           tspanString += Ext.htmlEncode(text) + '</tspan>';


           props = toPropertyString({
               x: attr.x,
               y: attr.y,
               'font-size': size,
               'font-family': family,
               'font-weight': attr['font-weight'],
               'text-anchor': attr['text-anchor'],
               
               fill: attr.fill || '#000',
               'fill-opacity': attr.opacity,
               transform: sprite.matrix.toSvg()
           });



           return '<text '+ props + '>' +  tspanString + '</text>';
       },
       rect: function(sprite){

           var attr = sprite.attr,
               props =  toPropertyString({
                   x: attr.x,
                   y: attr.y,
                   rx: attr.rx,
                   ry: attr.ry,
                   width: attr.width,
                   height: attr.height,
                   fill: attr.fill || 'none',
                   'fill-opacity': attr.opacity,
                   stroke: attr.stroke,
                   'stroke-opacity': attr['stroke-opacity'],
                   'stroke-width':attr['stroke-width'],
                   transform: sprite.matrix && sprite.matrix.toSvg()
               });

           return '<rect ' + props + '/>';
       },
       circle: function(sprite){

           var attr = sprite.attr,
               props = toPropertyString({
                   cx: attr.x,
                   cy: attr.y,
                   r: attr.radius,
                   fill: attr.translation.fill || attr.fill || 'none',
                   'fill-opacity': attr.opacity,
                   stroke: attr.stroke,
                   'stroke-opacity': attr['stroke-opacity'],
                   'stroke-width':attr['stroke-width'],
                   transform: sprite.matrix.toSvg()
               });

           return '<circle ' + props + ' />';
       },
       image: function(sprite){

           var attr = sprite.attr,
               props = toPropertyString({
                   x: attr.x - (attr.width/2 >> 0),
                   y: attr.y - (attr.height/2 >> 0),
                   width: attr.width,
                   height: attr.height,
                   'xlink:href': attr.src,
                   transform: sprite.matrix.toSvg()
               });

           return '<image ' + props + ' />';
       }
   },
   svgHeader = function(){
       var svg = '<?xml version="1.0" standalone="yes"?>';
       svg += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
       return svg;
   },
   svgContent = function(){
       var svg = '<svg width="'+width+'px" height="'+height+'px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">',
           defs = '', item, itemsLen, items, gradient,
           getSvgString, colorstops, stop,
           coll, keys, colls, k, kLen, key, collI, i, j, stopsLen, sortedItems, za, zb;

       items = surface.items.items;
       itemsLen = items.length;


       getSvgString = function(node){

           var childs = node.childNodes,
               childLength = childs.length,
               i = 0,
               attrLength,
               j,
               svgString = '', child, attr, tagName, attrItem;

               for(; i < childLength; i++){
                   child = childs[i];
                   attr = child.attributes;
                   tagName = child.tagName;

                   svgString += '<' +tagName;

                   for(j = 0, attrLength = attr.length; j < attrLength; j++){
                       attrItem = attr.item(j);
                       svgString += ' '+attrItem.name+'="'+attrItem.value+'"';
                   }

                   svgString += '>';

                   if(child.childNodes.length > 0){
                       svgString += getSvgString(child);
                   }

                   svgString += '</' + tagName + '>';

               }
           return svgString;
       };


       if(surface.getDefs){
           defs = getSvgString(surface.getDefs());
       }else{
           
           coll = surface.gradientsColl;
           if (coll) {
               keys  = coll.keys;
               colls = coll.items;
               k     = 0;
               kLen  = keys.length;
           }

           for (; k < kLen; k++) {
               key   = keys[k];
               collI = colls[k];

               gradient = surface.gradientsColl.getByKey(key);
               defs += '<linearGradient id="' + key + '" x1="0" y1="0" x2="1" y2="1">';

               var color = gradient.colors.replace(rgbColorRe, 'rgb($1|$2|$3)');
               color = color.replace(rgbaColorRe, 'rgba($1|$2|$3|$4)')
               colorstops = color.split(',');
               for(i=0, stopsLen = colorstops.length; i < stopsLen; i++){
                   stop = colorstops[i].split(' ');
                   color = Ext.draw.Color.fromString(stop[1].replace(/\|/g,','));
                   defs += '<stop offset="'+stop[0]+'" stop-color="' + color.toString() + '" stop-opacity="1"></stop>';
               }
               defs += '</linearGradient>';
           }
       }

       svg += '<defs>' + defs + '</defs>';

       
       svg += spriteProcessor.rect({
           attr: {
                   width: '100%',
                   height: '100%',
                   fill: '#fff',
                   stroke: 'none',
                   opacity: '0'
           }
       });

       
       sortedItems = new Array(itemsLen);
       for(i = 0; i < itemsLen; i++){
           sortedItems[i] = i;
       }
       sortedItems.sort(function (a, b) {
           za = items[a].attr.zIndex || 0;
           zb = items[b].attr.zIndex || 0;
           if (za == zb) {
               return a - b;
           }
           return za - zb;
       });

       for(i = 0; i < itemsLen; i++){
           item = items[sortedItems[i]];
           if(!item.attr.hidden){
               svg += spriteProcessor[item.type](item);
           }
       }

       svg += '</svg>';

       return svg;
   },
   toPropertyString = function(obj){
       var propString = '',
           key;

       for(key in obj){

           if(obj.hasOwnProperty(key) && obj[key] != null){
               propString += key +'="'+ obj[key]+'" ';
           }

       }

       return propString;
   };

   return {
       singleton: true,

       
       generate: function(surface, config){
           config = config || {};
           init(surface);
           return svgHeader() + svgContent();
       }
   };
});


Ext.define('Ext.draw.engine.Vml', {

    

    extend:  Ext.draw.Surface ,

                                                                                                       

    

    engine: 'Vml',

    map: {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
    bitesRe: /([clmz]),?([^clmz]*)/gi,
    valRe: /-?[^,\s\-]+/g,
    fillUrlRe: /^url\(\s*['"]?([^\)]+?)['"]?\s*\)$/i,
    pathlike: /^(path|rect)$/,
    NonVmlPathRe: /[ahqstv]/ig, // Non-VML Pathing ops
    partialPathRe: /[clmz]/g,
    fontFamilyRe: /^['"]+|['"]+$/g,
    baseVmlCls: Ext.baseCSSPrefix + 'vml-base',
    vmlGroupCls: Ext.baseCSSPrefix + 'vml-group',
    spriteCls: Ext.baseCSSPrefix + 'vml-sprite',
    measureSpanCls: Ext.baseCSSPrefix + 'vml-measure-span',
    zoom: 21600,
    coordsize: 1000,
    coordorigin: '0 0',
    zIndexShift: 0,
    // VML uses CSS z-index and therefore doesn't need sprites to be kept in zIndex order
    orderSpritesByZIndex: false,

    
    
    path2vml: function (path) {
        var me = this,
            nonVML = me.NonVmlPathRe,
            map = me.map,
            val = me.valRe,
            zoom = me.zoom,
            bites = me.bitesRe,
            command = Ext.Function.bind(Ext.draw.Draw.pathToAbsolute, Ext.draw.Draw),
            res, pa, p, r, i, ii, j, jj;
        if (String(path).match(nonVML)) {
            command = Ext.Function.bind(Ext.draw.Draw.path2curve, Ext.draw.Draw);
        } else if (!String(path).match(me.partialPathRe)) {
            res = String(path).replace(bites, function (all, command, args) {
                var vals = [],
                    isMove = command.toLowerCase() == "m",
                    res = map[command];
                args.replace(val, function (value) {
                    if (isMove && vals.length === 2) {
                        res += vals + map[command == "m" ? "l" : "L"];
                        vals = [];
                    }
                    vals.push(Math.round(value * zoom));
                });
                return res + vals;
            });
            return res;
        }
        pa = command(path);
        res = [];
        for (i = 0, ii = pa.length; i < ii; i++) {
            p = pa[i];
            r = pa[i][0].toLowerCase();
            if (r == "z") {
                r = "x";
            }
            for (j = 1, jj = p.length; j < jj; j++) {
                r += Math.round(p[j] * me.zoom) + (j != jj - 1 ? "," : "");
            }
            res.push(r);
        }
        return res.join(" ");
    },

    
    translateAttrs: {
        radius: "r",
        radiusX: "rx",
        radiusY: "ry",
        lineWidth: "stroke-width",
        fillOpacity: "fill-opacity",
        strokeOpacity: "stroke-opacity",
        strokeLinejoin: "stroke-linejoin"
    },

    
    minDefaults: {
        circle: {
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        ellipse: {
            cx: 0,
            cy: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        rect: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        text: {
            x: 0,
            y: 0,
            "text-anchor": "start",
            font: '10px "Arial"',
            fill: "#000",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        path: {
            d: "M0,0",
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        image: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            preserveAspectRatio: "none",
            opacity: null
        }
    },

    
    onMouseEnter: function (e) {
        this.fireEvent("mouseenter", e);
    },

    
    onMouseLeave: function (e) {
        this.fireEvent("mouseleave", e);
    },

    
    processEvent: function (name, e) {
        var target = e.getTarget(),
            surface = this.surface,
            sprite;
        this.fireEvent(name, e);
        sprite = this.items.get(target.id);
        if (sprite) {
            sprite.fireEvent(name, sprite, e);
        }
    },

    
    createSpriteElement: function (sprite) {
        var me = this,
            attr = sprite.attr,
            type = sprite.type,
            zoom = me.zoom,
            vml = sprite.vml || (sprite.vml = {}),
            round = Math.round,
            el = (type === 'image') ? me.createNode('image') : me.createNode('shape'),
            path, skew, textPath;

        el.coordsize = zoom + ' ' + zoom;
        el.coordorigin = attr.coordorigin || "0 0";
        Ext.get(el).addCls(me.spriteCls);
        if (type == "text") {
            vml.path = path = me.createNode("path");
            path.textpathok = true;
            vml.textpath = textPath = me.createNode("textpath");
            textPath.on = true;
            el.appendChild(textPath);
            el.appendChild(path);
        }
        el.id = sprite.id;
        sprite.el = Ext.get(el);
        sprite.el.setStyle('zIndex', -me.zIndexShift);
        me.el.appendChild(el);
        if (type !== 'image') {
            skew = me.createNode("skew");
            skew.on = true;
            el.appendChild(skew);
            sprite.skew = skew;
        }
        sprite.matrix = new Ext.draw.Matrix();
        sprite.bbox = {
            plain: null,
            transform: null
        };

        this.applyAttrs(sprite);
        this.applyTransformations(sprite);        
        sprite.fireEvent("render", sprite);
        return sprite.el;
    },

    getBBoxText: function (sprite) {
        var vml = sprite.vml;
        return {
            x: vml.X + (vml.bbx || 0) - vml.W / 2,
            y: vml.Y - vml.H / 2,
            width: vml.W,
            height: vml.H
        };
    },

    applyAttrs: function (sprite) {
        var me = this,
            vml = sprite.vml,
            group = sprite.group,
            spriteAttr = sprite.attr,
            el = sprite.el,
            dom = el.dom,
            style, name, groups, i, ln, scrubbedAttrs, font, key,
            cx, cy, rx, ry;

        if (group) {
            groups = [].concat(group);
            ln = groups.length;
            for (i = 0; i < ln; i++) {
                group = groups[i];
                me.getGroup(group).add(sprite);
            }
            delete sprite.group;
        }
        scrubbedAttrs = me.scrubAttrs(sprite) || {};

        if (sprite.zIndexDirty) {
            me.setZIndex(sprite);
        }

        
        Ext.applyIf(scrubbedAttrs, me.minDefaults[sprite.type]);

        if (sprite.type == 'image') {
            Ext.apply(sprite.attr, {
                x: scrubbedAttrs.x,
                y: scrubbedAttrs.y,
                width: scrubbedAttrs.width,
                height: scrubbedAttrs.height
            });
            el.setStyle({
                width: scrubbedAttrs.width + 'px',
                height: scrubbedAttrs.height + 'px'
            });
            dom.src = scrubbedAttrs.src;
        }

        if (dom.href) {
            dom.href = scrubbedAttrs.href;
        }
        if (dom.title) {
            dom.title = scrubbedAttrs.title;
        }
        if (dom.target) {
            dom.target = scrubbedAttrs.target;
        }
        if (dom.cursor) {
            dom.cursor = scrubbedAttrs.cursor;
        }

        
        if (sprite.dirtyHidden) {
            (scrubbedAttrs.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite);
            sprite.dirtyHidden = false;
        }

        
        if (sprite.dirtyPath) {
            if (sprite.type == "circle" || sprite.type == "ellipse") {
                cx = scrubbedAttrs.x;
                cy = scrubbedAttrs.y;
                rx = scrubbedAttrs.rx || scrubbedAttrs.r || 0;
                ry = scrubbedAttrs.ry || scrubbedAttrs.r || 0;
                dom.path = Ext.String.format("ar{0},{1},{2},{3},{4},{1},{4},{1}",
                    Math.round((cx - rx) * me.zoom),
                    Math.round((cy - ry) * me.zoom),
                    Math.round((cx + rx) * me.zoom),
                    Math.round((cy + ry) * me.zoom),
                    Math.round(cx * me.zoom));
                sprite.dirtyPath = false;
            }
            else if (sprite.type !== "text" && sprite.type !== 'image') {
                sprite.attr.path = scrubbedAttrs.path = me.setPaths(sprite, scrubbedAttrs) || scrubbedAttrs.path;
                dom.path = me.path2vml(scrubbedAttrs.path);
                sprite.dirtyPath = false;
            }
        }

        
        if ("clip-rect" in scrubbedAttrs) {
            me.setClip(sprite, scrubbedAttrs);
        }

        
        if (sprite.type == "text") {
            me.setTextAttributes(sprite, scrubbedAttrs);
        }

        
        if (scrubbedAttrs.opacity || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) {
            me.setFill(sprite, scrubbedAttrs);
        }

        
        if (scrubbedAttrs.stroke || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) {
            me.setStroke(sprite, scrubbedAttrs);
        }

        
        style = spriteAttr.style;
        if (style) {
            el.setStyle(style);
        }

        sprite.dirty = false;
    },

    setZIndex: function (sprite) {
        var me = this,
            zIndex = sprite.attr.zIndex,
            shift = me.zIndexShift,
            items, iLen, item, i;

        if (zIndex < shift) {
            
            
            items = me.items.items;
            iLen = items.length;

            for (i = 0; i < iLen; i++) {
                if ((zIndex = items[i].attr.zIndex) && zIndex < shift) { 
                    shift = zIndex;
                }
            }

            me.zIndexShift = shift;
            for (i = 0; i < iLen; i++) {
                item = items[i];
                if (item.el) {
                    item.el.setStyle('zIndex', item.attr.zIndex - shift);
                }
                item.zIndexDirty = false;
            }
        } else if (sprite.el) {
            sprite.el.setStyle('zIndex', zIndex - shift);
            sprite.zIndexDirty = false;
        }
    },

    
    setPaths: function (sprite, params) {
        var spriteAttr = sprite.attr, thickness = sprite.attr['stroke-width'] || 1;
        
        sprite.bbox.plain = null;
        sprite.bbox.transform = null;
        if (sprite.type == 'circle') {
            spriteAttr.rx = spriteAttr.ry = params.r;
            return Ext.draw.Draw.ellipsePath(sprite);
        }
        else if (sprite.type == 'ellipse') {
            spriteAttr.rx = params.rx;
            spriteAttr.ry = params.ry;
            return Ext.draw.Draw.ellipsePath(sprite);
        }
        else if (sprite.type == 'rect') {
            spriteAttr.rx = spriteAttr.ry = params.r;
            return Ext.draw.Draw.rectPath(sprite);
        }
        else if (sprite.type == 'path' && spriteAttr.path) {
            return Ext.draw.Draw.pathToAbsolute(spriteAttr.path);
        }
        return false;
    },

    setFill: function (sprite, params) {
        var me = this,
            el = sprite.el.dom,
            fillEl = el.fill,
            newfill = false,
            opacity, gradient, fillUrl, rotation, angle;

        if (!fillEl) {
            
            fillEl = el.fill = me.createNode("fill");
            newfill = true;
        }
        if (Ext.isArray(params.fill)) {
            params.fill = params.fill[0];
        }
        if (params.fill == "none") {
            fillEl.on = false;
        }
        else {
            if (typeof params.opacity == "number") {
                fillEl.opacity = params.opacity;
            }
            if (typeof params["fill-opacity"] == "number") {
                fillEl.opacity = params["fill-opacity"];
            }
            fillEl.on = true;
            if (typeof params.fill == "string") {
                fillUrl = params.fill.match(me.fillUrlRe);
                if (fillUrl) {
                    fillUrl = fillUrl[1];
                    
                    if (fillUrl.charAt(0) == "#") {
                        gradient = me.gradientsColl.getByKey(fillUrl.substring(1));
                    }
                    if (gradient) {
                        
                        rotation = params.rotation;
                        angle = -(gradient.angle + 270 + (rotation ? rotation.degrees : 0)) % 360;
                        
                        if (angle === 0) {
                            angle = 180;
                        }
                        fillEl.angle = angle;
                        fillEl.type = "gradient";
                        fillEl.method = "sigma";
                        if (fillEl.colors) {
                            fillEl.colors.value = gradient.colors;
                        } else {
                            fillEl.colors = gradient.colors;
                        }
                    }
                    
                    else {
                        fillEl.src = fillUrl;
                        fillEl.type = "tile";
                    }
                }
                else {
                    fillEl.color = Ext.draw.Color.toHex(params.fill);
                    fillEl.src = "";
                    fillEl.type = "solid";
                }
            }
        }
        if (newfill) {
            el.appendChild(fillEl);
        }
    },

    setStroke: function (sprite, params) {
        var me = this,
            el = sprite.el.dom,
            strokeEl = sprite.strokeEl,
            newStroke = false,
            width, opacity;

        if (!strokeEl) {
            strokeEl = sprite.strokeEl = me.createNode("stroke");
            newStroke = true;
        }
        if (Ext.isArray(params.stroke)) {
            params.stroke = params.stroke[0];
        }
        if (!params.stroke || params.stroke == "none" || params.stroke == 0 || params["stroke-width"] == 0) {
            strokeEl.on = false;
        }
        else {
            strokeEl.on = true;
            if (params.stroke && !params.stroke.match(me.fillUrlRe)) {
                
                strokeEl.color = Ext.draw.Color.toHex(params.stroke);
            }
            strokeEl.dashstyle = params["stroke-dasharray"] ? "dash" : "solid";
            strokeEl.joinstyle = params["stroke-linejoin"];
            strokeEl.endcap = params["stroke-linecap"] || "round";
            strokeEl.miterlimit = params["stroke-miterlimit"] || 8;
            width = parseFloat(params["stroke-width"] || 1) * 0.75;
            opacity = params["stroke-opacity"] || 1;
            
            if (Ext.isNumber(width) && width < 1) {
                strokeEl.weight = 1;
                strokeEl.opacity = opacity * width;
            }
            else {
                strokeEl.weight = width;
                strokeEl.opacity = opacity;
            }
        }
        if (newStroke) {
            el.appendChild(strokeEl);
        }
    },

    setClip: function (sprite, params) {
        var me = this,
            el = sprite.el,
            clipEl = sprite.clipEl,
            rect = String(params["clip-rect"]).split(me.separatorRe);
        if (!clipEl) {
            clipEl = sprite.clipEl = me.el.insertFirst(Ext.getDoc().dom.createElement("div"));
            clipEl.addCls(Ext.baseCSSPrefix + 'vml-sprite');
        }
        if (rect.length == 4) {
            rect[2] = +rect[2] + (+rect[0]);
            rect[3] = +rect[3] + (+rect[1]);
            clipEl.setStyle("clip", Ext.String.format("rect({1}px {2}px {3}px {0}px)", rect[0], rect[1], rect[2], rect[3]));
            clipEl.setSize(me.el.width, me.el.height);
        }
        else {
            clipEl.setStyle("clip", "");
        }
    },

    setTextAttributes: function (sprite, params) {
        var me = this,
            vml = sprite.vml,
            textStyle = vml.textpath.style,
            spanCacheStyle = me.span.style,
            zoom = me.zoom,
            round = Math.round,
            fontObj = {
                fontSize: "font-size",
                fontWeight: "font-weight",
                fontStyle: "font-style"
            },
            fontProp,
            paramProp;
        if (sprite.dirtyFont) {
            if (params.font) {
                textStyle.font = spanCacheStyle.font = params.font;
            }
            if (params["font-family"]) {
                textStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(me.fontFamilyRe, "") + '"';
                spanCacheStyle.fontFamily = params["font-family"];
            }

            for (fontProp in fontObj) {
                paramProp = params[fontObj[fontProp]];
                if (paramProp) {
                    textStyle[fontProp] = spanCacheStyle[fontProp] = paramProp;
                }
            }

            me.setText(sprite, params.text);

            if (vml.textpath.string) {
                me.span.innerHTML = String(vml.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br/>");
            }
            vml.W = me.span.offsetWidth;
            vml.H = me.span.offsetHeight + 2; 

            
            if (params["text-anchor"] == "middle") {
                textStyle["v-text-align"] = "center";
            }
            else if (params["text-anchor"] == "end") {
                textStyle["v-text-align"] = "right";
                vml.bbx = -Math.round(vml.W / 2);
            }
            else {
                textStyle["v-text-align"] = "left";
                vml.bbx = Math.round(vml.W / 2);
            }
        }
        vml.X = params.x;
        vml.Y = params.y;
        vml.path.v = Ext.String.format("m{0},{1}l{2},{1}", Math.round(vml.X * zoom), Math.round(vml.Y * zoom), Math.round(vml.X * zoom) + 1);
        
        sprite.bbox.plain = null;
        sprite.bbox.transform = null;
        sprite.dirtyFont = false;
    },

    setText: function (sprite, text) {
        sprite.vml.textpath.string = Ext.htmlDecode(text);
    },

    hide: function () {
        this.el.hide();
    },

    show: function () {
        this.el.show();
    },

    hidePrim: function (sprite) {
        sprite.el.addCls(Ext.baseCSSPrefix + 'hide-visibility');
    },

    showPrim: function (sprite) {
        sprite.el.removeCls(Ext.baseCSSPrefix + 'hide-visibility');
    },

    setSize: function (width, height) {
        var me = this;
        width = width || me.width;
        height = height || me.height;
        me.width = width;
        me.height = height;

        if (me.el) {
            
            if (width != undefined) {
                me.el.setWidth(width);
            }
            if (height != undefined) {
                me.el.setHeight(height);
            }
        }

        me.callParent(arguments);
    },

    
    applyViewBox: function () {
        var me = this,
            viewBox = me.viewBox,
            width = me.width,
            height = me.height,
            items,
            iLen,
            i;
        
        me.callParent();

        if (viewBox && (width || height)) {
            items = me.items.items;
            iLen = items.length;

            for (i = 0; i < iLen; i++) {
                me.applyTransformations(items[i]);
            }
        }
    },

    onAdd: function (item) {
        this.callParent(arguments);
        if (this.el) {
            this.renderItem(item);
        }
    },

    onRemove: function (sprite) {
        if (sprite.el) {
            sprite.el.remove();
            delete sprite.el;
        }
        this.callParent(arguments);
    },

    render: function (container) {
        var me = this,
            doc = Ext.getDoc().dom,
            el;
        
        if (!me.createNode) {
            try {
                if (!doc.namespaces.rvml) {
                    doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
                }
                me.createNode = function (tagName) {
                    return doc.createElement("<rvml:" + tagName + ' class="rvml">');
                };
            } catch (e) {
                me.createNode = function (tagName) {
                    return doc.createElement("<" + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
                };
            }
        }

        if (!me.el) {
            el = doc.createElement("div");
            me.el = Ext.get(el);
            me.el.addCls(me.baseVmlCls);

            
            me.span = doc.createElement("span");
            Ext.get(me.span).addCls(me.measureSpanCls);
            el.appendChild(me.span);
            me.el.setSize(me.width || 0, me.height || 0);
            container.appendChild(el);
            me.el.on({
                scope: me,
                mouseup: me.onMouseUp,
                mousedown: me.onMouseDown,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousemove: me.onMouseMove,
                mouseenter: me.onMouseEnter,
                mouseleave: me.onMouseLeave,
                click: me.onClick,
                dblclick: me.onDblClick
            });
        }
        me.renderAll();
    },

    renderAll: function () {
        this.items.each(this.renderItem, this);
    },

    redraw: function (sprite) {
        sprite.dirty = true;
        this.renderItem(sprite);
    },

    renderItem: function (sprite) {
        
        if (!this.el) {
            return;
        }

        
        if (!sprite.el) {
            this.createSpriteElement(sprite);
        }

        if (sprite.dirty) {
            this.applyAttrs(sprite);
            if (sprite.dirtyTransform) {
                this.applyTransformations(sprite);
            }
        }
    },

    rotationCompensation: function (deg, dx, dy) {
        var matrix = new Ext.draw.Matrix();
        matrix.rotate(-deg, 0.5, 0.5);
        return {
            x: matrix.x(dx, dy),
            y: matrix.y(dx, dy)
        };
    },

    transform: function (sprite, matrixOnly) {
        var me = this,
            bbox = me.getBBox(sprite, true),
            cx = bbox.x + bbox.width * 0.5,
            cy = bbox.y + bbox.height * 0.5,
            matrix = new Ext.draw.Matrix(),
            transforms = sprite.transformations,
            transformsLength = transforms.length,
            i = 0,
            deltaDegrees = 0,
            deltaScaleX = 1,
            deltaScaleY = 1,
            flip = "",
            el = sprite.el,
            dom = el.dom,
            domStyle = dom.style,
            zoom = me.zoom,
            skew = sprite.skew,
            shift = me.viewBoxShift,
            deltaX, deltaY, transform, type, compensate, y, fill, newAngle, zoomScaleX, zoomScaleY, newOrigin, offset;


        for (; i < transformsLength; i++) {
            transform = transforms[i];
            type = transform.type;
            if (type == "translate") {
                matrix.translate(transform.x, transform.y);
            }
            else if (type == "rotate") {
                matrix.rotate(transform.degrees, transform.x, transform.y);
                deltaDegrees += transform.degrees;
            }
            else if (type == "scale") {
                matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY);
                deltaScaleX *= transform.x;
                deltaScaleY *= transform.y;
            }
        }

        sprite.matrix = matrix.clone();

        if (matrixOnly) {
            return;
        }

        if (shift) {
            matrix.prepend(shift.scale, 0, 0, shift.scale, shift.dx * shift.scale, shift.dy * shift.scale);
        }

        
        if (sprite.type != "image" && skew) {
            skew.origin = "0,0";
            
            skew.matrix = matrix.toString();
            
            
            
            offset = matrix.offset();
            if (offset[0] > 32767) {
                offset[0] = 32767;
            } else if (offset[0] < -32768) {
                offset[0] = -32768;
            }
            if (offset[1] > 32767) {
                offset[1] = 32767;
            } else if (offset[1] < -32768) {
                offset[1] = -32768;
            }
            skew.offset = offset;
        }
        else {
            domStyle.filter = matrix.toFilter();
            domStyle.left = Math.min(
                matrix.x(bbox.x, bbox.y),
                matrix.x(bbox.x + bbox.width, bbox.y),
                matrix.x(bbox.x, bbox.y + bbox.height),
                matrix.x(bbox.x + bbox.width, bbox.y + bbox.height)) + 'px';
            domStyle.top = Math.min(
                matrix.y(bbox.x, bbox.y),
                matrix.y(bbox.x + bbox.width, bbox.y),
                matrix.y(bbox.x, bbox.y + bbox.height),
                matrix.y(bbox.x + bbox.width, bbox.y + bbox.height)) + 'px';
        }
    },

    createItem: function (config) {
        return Ext.create('Ext.draw.Sprite', config);
    },

    getRegion: function () {
        return this.el.getRegion();
    },

    addCls: function (sprite, className) {
        if (sprite && sprite.el) {
            sprite.el.addCls(className);
        }
    },

    removeCls: function (sprite, className) {
        if (sprite && sprite.el) {
            sprite.el.removeCls(className);
        }
    },

    
    addGradient: function (gradient) {
        var gradients = this.gradientsColl || (this.gradientsColl = Ext.create('Ext.util.MixedCollection')),
            colors = [],
            stops = Ext.create('Ext.util.MixedCollection'),
            keys,
            items,
            iLen,
            key,
            item,
            i;

        
        stops.addAll(gradient.stops);
        stops.sortByKey("ASC", function (a, b) {
            a = parseInt(a, 10);
            b = parseInt(b, 10);
            return a > b ? 1 : (a < b ? -1 : 0);
        });

        keys = stops.keys;
        items = stops.items;
        iLen = keys.length;

        for (i = 0; i < iLen; i++) {
            key = keys[i];
            item = items[i];
            colors.push(key + '% ' + item.color);
        }

        gradients.add(gradient.id, {
            colors: colors.join(","),
            angle: gradient.angle
        });
    },

    destroy: function () {
        var me = this;

        me.callParent(arguments);
        if (me.el) {
            me.el.remove();
        }
        delete me.el;
    }
});















Ext.define('Ext.flash.Component', {
    extend:  Ext.Component ,
    alternateClassName: 'Ext.FlashComponent',
    alias: 'widget.flash',

    
    flashVersion : '9.0.115',

    
    backgroundColor: '#ffffff',

    
    wmode: 'opaque',

    

    

    

    

    
    swfWidth: '100%',

    
    swfHeight: '100%',

    
    expressInstall: false,

    

    
    renderTpl: ['<div id="{swfId}"></div>'],

    initComponent: function() {

        this.callParent();
        this.addEvents(
            
            'success',

            
            'failure'
        );
    },
    
    beforeRender: function(){
        this.callParent();
        
        Ext.applyIf(this.renderData, {
            swfId: this.getSwfId()
        });
    },

    afterRender: function() {
        var me = this,
            flashParams = Ext.apply({}, me.flashParams), 
            flashVars = Ext.apply({}, me.flashVars);

        me.callParent();

        flashParams = Ext.apply({
            allowScriptAccess: 'always',
            bgcolor: me.backgroundColor,
            wmode: me.wmode
        }, flashParams);

        flashVars = Ext.apply({
            allowedDomain: document.location.hostname
        }, flashVars);

        new swfobject.embedSWF(
            me.url,
            me.getSwfId(),
            me.swfWidth,
            me.swfHeight,
            me.flashVersion,
            me.expressInstall ? me.statics.EXPRESS_INSTALL_URL : undefined,
            flashVars,
            flashParams,
            me.flashAttributes,
            Ext.bind(me.swfCallback, me)
        );
    },

    
    swfCallback: function(e) {
        var me = this;
        if (e.success) {
            me.swf = Ext.get(e.ref);
            me.onSuccess();
            me.fireEvent('success', me);
        } else {
            me.onFailure();
            me.fireEvent('failure', me);
        }
    },

    
    getSwfId: function() {
        return this.swfId || (this.swfId = "extswf" + this.getAutoId());
    },

    onSuccess: function() {
        
        
        this.swf.setStyle('visibility', 'inherit');
    },

    onFailure: Ext.emptyFn,

    beforeDestroy: function() {
        var me = this,
            swf = me.swf;
        if (swf) {
            swfobject.removeSWF(me.getSwfId());
            Ext.destroy(swf);
            delete me.swf;
        }
        me.callParent();
    },

    statics: {
        
        EXPRESS_INSTALL_URL: 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf'
    }
});


Ext.define('Ext.form.action.Action', {
    alternateClassName: 'Ext.form.Action',

    

    

    

    

    

    

    

    

    

    

    

    

    
    submitEmptyText : true,

    

    

    

    

    
    constructor: function(config) {
        if (config) {
            Ext.apply(this, config);
        }

        
        var params = config.params;
        if (Ext.isString(params)) {
            this.params = Ext.Object.fromQueryString(params);
        }
    },

    
    run: Ext.emptyFn,

    

    

    
    onFailure : function(response){
        this.response = response;
        this.failureType = Ext.form.action.Action.CONNECT_FAILURE;
        this.form.afterAction(this, false);
    },

    
    processResponse : function(response){
        this.response = response;
        if (!response.responseText && !response.responseXML) {
            return true;
        }
        return (this.result = this.handleResponse(response));
    },

    
    getUrl: function() {
        return this.url || this.form.url;
    },

    
    getMethod: function() {
        return (this.method || this.form.method || 'POST').toUpperCase();
    },

    
    getParams: function() {
        return Ext.apply({}, this.params, this.form.baseParams);
    },

    
    createCallback: function() {
        var me = this,
            undef,
            form = me.form;
        return {
            success: me.onSuccess,
            failure: me.onFailure,
            scope: me,
            timeout: (this.timeout * 1000) || (form.timeout * 1000),
            upload: form.fileUpload ? me.onSuccess : undef
        };
    },

    statics: {
        
        CLIENT_INVALID: 'client',

        
        SERVER_INVALID: 'server',

        
        CONNECT_FAILURE: 'connect',

        
        LOAD_FAILURE: 'load'


    }
});


Ext.define('Ext.form.action.Load', {
    extend: Ext.form.action.Action ,
                                      
    alternateClassName: 'Ext.form.Action.Load',
    alias: 'formaction.load',

    type: 'load',

    
    run: function() {
        Ext.Ajax.request(Ext.apply(
            this.createCallback(),
            {
                method: this.getMethod(),
                url: this.getUrl(),
                headers: this.headers,
                params: this.getParams()
            }
        ));
    },

    
    onSuccess: function(response){
        var result = this.processResponse(response),
            form = this.form;
        if (result === true || !result.success || !result.data) {
            this.failureType = Ext.form.action.Action.LOAD_FAILURE;
            form.afterAction(this, false);
            return;
        }
        form.clearInvalid();
        form.setValues(result.data);
        form.afterAction(this, true);
    },

    
    handleResponse: function(response) {
        var reader = this.form.reader,
            rs, data;
        if (reader) {
            rs = reader.read(response);
            data = rs.records && rs.records[0] ? rs.records[0].data : null;
            return {
                success : rs.success,
                data : data
            };
        }
        return Ext.decode(response.responseText);
    }
});



Ext.define('Ext.form.action.Submit', {
    extend: Ext.form.action.Action ,
    alternateClassName: 'Ext.form.Action.Submit',
    alias: 'formaction.submit',

    type: 'submit',

    

    
    run : function(){
        var me = this,
            form = me.form;
            
        if (me.clientValidation === false || form.isValid()) {
            me.doSubmit();
        } else {
            
            me.failureType = Ext.form.action.Action.CLIENT_INVALID;
            form.afterAction(me, false);
        }
    },

    
    doSubmit: function() {
        var me = this,
            ajaxOptions = Ext.apply(me.createCallback(), {
                url: me.getUrl(),
                method: me.getMethod(),
                headers: me.headers
            }),
            form = me.form,
            jsonSubmit = me.jsonSubmit || form.jsonSubmit,
            paramsProp = jsonSubmit ? 'jsonData' : 'params',
            formEl, formInfo;

        
        
        if (form.hasUpload()) {
            formInfo = me.buildForm();
            ajaxOptions.form = formInfo.formEl;
            ajaxOptions.isUpload = true;
        } else {
            ajaxOptions[paramsProp] = me.getParams(jsonSubmit);
        }

        Ext.Ajax.request(ajaxOptions);
        if (formInfo) {
            me.cleanup(formInfo);
        }
    },
    
    cleanup: function(formInfo) {
        var formEl = formInfo.formEl,
            uploadEls = formInfo.uploadEls,
            uploadFields = formInfo.uploadFields,
            len = uploadFields.length,
            i, field;
            
        for (i = 0; i < len; ++i) {
            field = uploadFields[i];
            if (!field.clearOnSubmit) {
                field.restoreInput(uploadEls[i]);
            }    
        }
        
        if (formEl) {
            Ext.removeNode(formEl);
        }    
    },

    
    getParams: function(useModelValues) {
        var falseVal = false,
            configParams = this.callParent(),
            fieldParams = this.form.getValues(falseVal, falseVal, this.submitEmptyText !== falseVal, useModelValues);
        return Ext.apply({}, fieldParams, configParams);
    },

    
    buildForm: function() {
        var me = this,
            fieldsSpec = [],
            formSpec,
            formEl,
            basicForm = me.form,
            params = me.getParams(),
            uploadFields = [],
            uploadEls = [],
            fields = basicForm.getFields().items,
            i,
            len   = fields.length,
            field, key, value, v, vLen,
            el;

        for (i = 0; i < len; ++i) {
            field = fields[i];

            
            if (field.rendered && field.isFileUpload()) {
                uploadFields.push(field);
            }
        }

        for (key in params) {
            if (params.hasOwnProperty(key)) {
                value = params[key];

                if (Ext.isArray(value)) {
                    vLen = value.length;
                    for (v = 0; v < vLen; v++) {
                        fieldsSpec.push(me.getFieldConfig(key, value[v]));
                    }
                } else {
                    fieldsSpec.push(me.getFieldConfig(key, value));
                }
            }
        }

        formSpec = {
            tag: 'form',
            action: me.getUrl(),
            method: me.getMethod(),
            target: me.target || '_self',
            style: 'display:none',
            cn: fieldsSpec
        };

        
        if (uploadFields.length) {
            formSpec.encoding = formSpec.enctype = 'multipart/form-data';
        }

        
        formEl = Ext.DomHelper.append(Ext.getBody(), formSpec);

        
        
        
        len = uploadFields.length;

        for (i = 0; i < len; ++i) {
            el = uploadFields[i].extractFileInput();
            formEl.appendChild(el);
            uploadEls.push(el);
        }

        return {
            formEl: formEl,
            uploadFields: uploadFields,
            uploadEls: uploadEls
        };
    },

    getFieldConfig: function(name, value) {
        return {
            tag: 'input',
            type: 'hidden',
            name: name,
            value: Ext.String.htmlEncode(value)
        };
    },

    
    onSuccess: function(response) {
        var form = this.form,
            success = true,
            result = this.processResponse(response);
        if (result !== true && !result.success) {
            if (result.errors) {
                form.markInvalid(result.errors);
            }
            this.failureType = Ext.form.action.Action.SERVER_INVALID;
            success = false;
        }
        form.afterAction(this, success);
    },

    
    handleResponse: function(response) {
        var form = this.form,
            errorReader = form.errorReader,
            rs, errors, i, len, records, result;
            
        if (errorReader) {
            rs = errorReader.read(response);
            records = rs.records;
            errors = [];
            if (records) {
                for(i = 0, len = records.length; i < len; i++) {
                    errors[i] = records[i].data;
                }
            }
            if (errors.length < 1) {
                errors = null;
            }
            result = {
                success : rs.success,
                errors : errors
            };
        } else {
            try {
                result = Ext.decode(response.responseText);    
            } catch (e) {
                result = {
                    success: false,
                    errors: []
                };
            }
            
        }
        return result;
    }
});


Ext.define('Ext.util.ComponentDragger', {
    extend:  Ext.dd.DragTracker ,

    

    

    

    autoStart: 500,

    
    constructor: function(comp, config) {
        this.comp = comp;
        this.initialConstrainTo = config.constrainTo;
        this.callParent([ config ]);
    },

    onStart: function(e) {
        var me = this,
            comp = me.comp;

        
        me.startPosition = comp.getXY();

        
        
        if (comp.ghost && !comp.liveDrag) {
             me.proxy = comp.ghost();
             me.dragTarget = me.proxy.header.el;
        }

        
        if (me.constrain || me.constrainDelegate) {
            me.constrainTo = me.calculateConstrainRegion();
        }

        if (comp.beginDrag) {
            comp.beginDrag();
        }
    },

    calculateConstrainRegion: function() {
        var me = this,
            comp = me.comp,
            constrainTo = me.initialConstrainTo,
            constraintInsets = comp.constraintInsets,
            constrainEl,
            delegateRegion,
            elRegion,
            dragEl = me.proxy ? me.proxy.el : comp.el,
            shadowSize = (!me.constrainDelegate && dragEl.shadow && comp.constrainShadow && !dragEl.shadowDisabled) ? dragEl.shadow.getShadowSize() : 0;

        
        if (!(constrainTo instanceof Ext.util.Region)) {
            constrainEl = Ext.fly(constrainTo);
            constrainTo =  constrainEl.getViewRegion();

            
            constrainTo.right = constrainTo.left + constrainEl.dom.clientWidth;
        } else {
            
            constrainTo = constrainTo.copy();
        }

        
        if (constraintInsets) {
            constraintInsets = Ext.isObject(constraintInsets) ? constraintInsets : Ext.Element.parseBox(constraintInsets);
            constrainTo.adjust(constraintInsets.top, constraintInsets.right, constraintInsets.bottom, constraintInsets.length);
        }

        
        if (shadowSize) {
            constrainTo.adjust(shadowSize[0], -shadowSize[1], -shadowSize[2], shadowSize[3]);
        }

        
        
        
        if (!me.constrainDelegate) {
            delegateRegion = Ext.fly(me.dragTarget).getRegion();
            elRegion = dragEl.getRegion();

            constrainTo.adjust(
                delegateRegion.top - elRegion.top,
                delegateRegion.right - elRegion.right,
                delegateRegion.bottom - elRegion.bottom,
                delegateRegion.left - elRegion.left
            );
        }
        return constrainTo;
    },

    
    onDrag: function(e) {
        var me = this,
            comp = (me.proxy && !me.comp.liveDrag) ? me.proxy : me.comp,
            offset = me.getOffset(me.constrain || me.constrainDelegate ? 'dragTarget' : null);

        comp.setPagePosition(me.startPosition[0] + offset[0], me.startPosition[1] + offset[1]);
    },

    onEnd: function(e) {
        var comp = this.comp;
        if (comp.isDestroyed || comp.destroying) {
            return;
        }
        
        if (this.proxy && !comp.liveDrag) {
            comp.unghost();
        }
        if (comp.endDrag) {
            comp.endDrag();
        }
    }
});


Ext.define('Ext.window.Window', {
    extend:  Ext.panel.Panel ,

    alternateClassName: 'Ext.Window',

                                                                                   

    alias: 'widget.window',

    

    

    

    

    

    

    

    

    
    
    

    
    baseCls: Ext.baseCSSPrefix + 'window',

    
    resizable: true,

    
    draggable: true,

    
    constrain: false,

    
    constrainHeader: false,

    

    
    plain: false,

    
    minimizable: false,

    
    maximizable: false,

    
    minHeight: 50,

    
    minWidth: 50,

    
    expandOnShow: true,

    
    collapsible: false,

    
    closable: true,

    
    hidden: true,

    
    autoRender: true,

    
    hideMode: 'offsets',

    
    floating: true,

    itemCls: Ext.baseCSSPrefix + 'window-item',
    
    initialAlphaNum: /^[a-z0-9]/,

    overlapHeader: true,

    ignoreHeaderBorderManagement: true,

    
    alwaysFramed: true,
    
    
    isRootCfg: {
        isRoot: true    
    },

    
    isWindow: true,

    
    initComponent: function() {
        var me = this;
        
        
        me.frame = false;
        me.callParent();
        me.addEvents(
            

            

            
            'resize',

            
            'maximize',

            
            'minimize',

            
            'restore'
        );

        if (me.plain) {
            me.addClsWithUI('plain');
        }

        if (me.modal) {
            me.ariaRole = 'dialog';
        }

        me.addStateEvents(['maximize', 'restore', 'resize', 'dragend']);
    },

    getElConfig: function () {
        var me = this,
            elConfig;

        elConfig = me.callParent();
        elConfig.tabIndex = -1;
        return elConfig;
    },

    
    
    
    getState: function() {
        var me = this,
            state = me.callParent() || {},
            maximized = !!me.maximized,
            ghostBox = me.ghostBox,
            pos;

        
        state.maximized = maximized;
        if (maximized) {
            pos = me.restorePos;
        } else if (ghostBox) {
            
            
            pos = [ghostBox.x, ghostBox.y];
        } else {
            pos = me.getPosition();
        }
        Ext.apply(state, {
            size: maximized ? me.restoreSize : me.getSize(),
            pos: pos
        });
        return state;
    },

    applyState: function(state){
        var me = this;

        if (state) {
            me.maximized = state.maximized;
            if (me.maximized) {
                me.hasSavedRestore = true;
                me.restoreSize = state.size;
                me.restorePos = state.pos;
            } else {
                Ext.apply(me, {
                    width: state.size.width,
                    height: state.size.height,
                    x: state.pos[0],
                    y: state.pos[1]
                });
            }
        }
    },

    
    onRender: function(ct, position) {
        var me = this;
        me.callParent(arguments);
        me.focusEl = me.el;

        
        if (me.maximizable) {
            me.header.on({
                scope: me,
                dblclick: me.toggleMaximize
            });
        }
    },

    
    afterRender: function() {
        var me = this,
            header = me.header,
            keyMap;

        me.callParent();

        
        if (me.maximized) {
            me.maximized = false;
            me.maximize();
            if (header) {
                header.removeCls(header.indicateDragCls)
            }
        }

        if (me.closable) {
            keyMap = me.getKeyMap();
            keyMap.on(27, me.onEsc, me);
        } else {
            keyMap = me.keyMap;
        }
        if (keyMap && me.hidden) {
            keyMap.disable();
        }
    },

    
    
    initDraggable: function() {
        
        this.initSimpleDraggable();
    },

    initResizable: function(){
        this.callParent(arguments);
        if (this.maximized) {
            this.resizer.disable();
        }
    },

    
    onEsc: function(k, e) {
        
        if (!Ext.FocusManager || !Ext.FocusManager.enabled || Ext.FocusManager.focusedCmp === this) {
            e.stopEvent();
            this.close();
        }
    },

    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            delete this.animateTarget;
            me.hide();
            Ext.destroy(
                me.keyMap
            );
        }
        me.callParent();
    },

    
    addTools: function() {
        var me = this;

        
        me.callParent();

        if (me.minimizable) {
            me.addTool({
                type: 'minimize',
                handler: Ext.Function.bind(me.minimize, me, [])
            });
        }
        if (me.maximizable) {
            me.addTool({
                type: 'maximize',
                handler: Ext.Function.bind(me.maximize, me, [])
            });
            me.addTool({
                type: 'restore',
                handler: Ext.Function.bind(me.restore, me, []),
                hidden: true
            });
        }
    },

    
    getFocusEl: function() {
        return this.getDefaultFocus();
    },

    
    getDefaultFocus: function() {
        var me = this,
            result,
            defaultComp = me.defaultButton || me.defaultFocus,
            selector;

        if (defaultComp !== undefined) {
            
            if (Ext.isNumber(defaultComp)) {
                result = me.query('button')[defaultComp];
            }
            
            else if (Ext.isString(defaultComp)) {
                selector = defaultComp;
                
                
                if (selector.match(me.initialAlphaNum)) {
                    result = me.down('#' + selector);
                }
                
                if (!result) {
                    result = me.down(selector);
                }
            }
            
            else if (defaultComp.focus) {
                result = defaultComp;
            }
        }
        return result || me.el;
    },

    
    onFocus: function() {
        var me = this,
            focusDescendant;

        
        if ((Ext.FocusManager && Ext.FocusManager.enabled) || ((focusDescendant = me.getDefaultFocus()) === me)) {
            me.callParent(arguments);
        } else {
            focusDescendant.focus();
        }
    },

    onShow: function() {
        var me = this;

        me.callParent(arguments);
        if (me.expandOnShow) {
            me.expand(false);
        }
        me.syncMonitorWindowResize();

        if (me.keyMap) {
            me.keyMap.enable();
        }
   },

    
    doClose: function() {
        var me = this;

        
        if (me.hidden) {
            me.fireEvent('close', me);
            if (me.closeAction == 'destroy') {
                this.destroy();
            }
        } else {
            
            me.hide(me.animateTarget, me.doClose, me);
        }
    },

    
    afterHide: function() {
        var me = this;

        
        me.syncMonitorWindowResize();

        
        if (me.keyMap) {
            me.keyMap.disable();
        }

        
        me.callParent(arguments);
    },

    
    onWindowResize: function() {
        var me = this,
            sizeModel;

        if (me.maximized) {
            me.fitContainer();
        } else {
            sizeModel = me.getSizeModel();
            if (sizeModel.width.natural || sizeModel.height.natural) {
                me.updateLayout();
            }
            me.doConstrain();
        }

    },

    
    minimize: function() {
        this.fireEvent('minimize', this);
        return this;
    },
    
    resumeHeaderLayout: function(changed) {
        this.header.resumeLayouts(changed ? this.isRootCfg : null);    
    },

    afterCollapse: function() {
        var me = this,
            header = me.header,
            tools = me.tools;

        if (header && me.maximizable) {
            header.suspendLayouts();
            tools.maximize.hide();
            tools.restore.hide();
            this.resumeHeaderLayout(true);
        }
        if (me.resizer) {
            me.resizer.disable();
        }
        me.callParent(arguments);
    },

    afterExpand: function() {
        var me = this,
            header = me.header,
            tools = me.tools,
            changed;

        
        if (header) {
            header.suspendLayouts();
            if (me.maximized) {
                tools.restore.show();
                changed = true;
            } else if (me.maximizable) {
                tools.maximize.show();
                changed = true;
            }
            this.resumeHeaderLayout(changed);
        }
        if (me.resizer) {
            me.resizer.enable();
        }
        me.callParent(arguments);
    },

    
    maximize: function(animate) {
        var me = this,
            header = me.header,
            tools = me.tools,
            changed;

        if (!me.maximized) {
            me.expand(false);
            if (!me.hasSavedRestore) {
                me.restoreSize = me.getSize();
                me.restorePos = me.getPosition(true);
            }

            
            if (header) {
                header.suspendLayouts();
                if (tools.maximize) {
                    tools.maximize.hide();
                    changed = true;
                }
                if (tools.restore) {
                    tools.restore.show();
                    changed = true;
                }
                if (me.collapseTool) {
                    me.collapseTool.hide();
                    changed = true;
                }
                me.resumeHeaderLayout(changed);
            }

            me.maximized = true;
            me.el.disableShadow();

            if (me.dd) {
                me.dd.disable();
                if (header) {
                   header.removeCls(header.indicateDragCls)
                }
            }
            if (me.resizer) {
                me.resizer.disable();
            }
            
            me.el.addCls(Ext.baseCSSPrefix + 'window-maximized');
            me.container.addCls(Ext.baseCSSPrefix + 'window-maximized-ct');

            me.syncMonitorWindowResize();
            me.fitContainer(animate = (animate || !!me.animateTarget) ? {
                callback: function() {
                    me.fireEvent('maximize', me);
                }
            } : null);
            if (!animate) {
                me.fireEvent('maximize', me);
            }
        }
        return me;
    },

    
    restore: function(animate) {
        var me = this,
            tools = me.tools,
            header = me.header,
            newBox = me.restoreSize,
            changed;

        if (me.maximized) {
            me.hasSavedRestore = null;
            me.removeCls(Ext.baseCSSPrefix + 'window-maximized');

            
            if (header) {
                header.suspendLayouts();
                if (tools.restore) {
                    tools.restore.hide();
                    changed = true;
                }
                if (tools.maximize) {
                    tools.maximize.show();
                    changed = true;
                }
                if (me.collapseTool) {
                    me.collapseTool.show();
                    changed = true;
                }
                me.resumeHeaderLayout(changed);
            }

            me.maximized = false;

            
            newBox.x = me.restorePos[0];
            newBox.y = me.restorePos[1];
            me.setBox(newBox, animate = (animate || !!me.animateTarget) ? {
                callback: function() {
                    me.el.enableShadow(true);
                    me.fireEvent('restore', me);
                }
            } : null);

            
            me.restorePos = me.restoreSize = null;

            
            if (me.dd) {
                me.dd.enable();
                if (header) {
                    header.addCls(header.indicateDragCls)
                }
            }
            
            if (me.resizer) {
                me.resizer.enable();
            }

            me.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct');

            me.syncMonitorWindowResize();

            if (!animate) {
                me.el.enableShadow(true);
                me.fireEvent('restore', me);
            }
        }
        return me;
    },

    
    syncMonitorWindowResize: function () {
        var me = this,
            currentlyMonitoring = me._monitoringResize,
            
            yes = me.monitorResize || me.constrain || me.constrainHeader || me.maximized,
            
            veto = me.hidden || me.destroying || me.isDestroyed;

        if (yes && !veto) {
            
            if (!currentlyMonitoring) {
                
                
                Ext.EventManager.onWindowResize(me.onWindowResize, me, {delay: 1});
                me._monitoringResize = true;
            }
        } else if (currentlyMonitoring) {
            
            Ext.EventManager.removeResizeListener(me.onWindowResize, me);
            me._monitoringResize = false;
        }
    },

    
    toggleMaximize: function() {
        return this[this.maximized ? 'restore': 'maximize']();
    }

});


Ext.define("Ext.form.Labelable", {
                                

    autoEl: {
        tag: 'table',
        cellpadding: 0
    },

    childEls: [
        
        'labelCell',

        
        'labelEl',

        
        'bodyEl',

        
        'sideErrorCell',

        
        'errorEl',

        'inputRow'
    ],

    
    labelableRenderTpl: [

        
        '<tr role="presentation" id="{id}-inputRow" <tpl if="inFormLayout">id="{id}"</tpl> class="{inputRowCls}">',

            
            '<tpl if="labelOnLeft">',
                '<td role="presentation" id="{id}-labelCell" style="{labelCellStyle}" {labelCellAttrs}>',
                    '{beforeLabelTpl}',
                    '<label id="{id}-labelEl" {labelAttrTpl}<tpl if="inputId"> for="{inputId}"</tpl> class="{labelCls}"',
                        '<tpl if="labelStyle"> style="{labelStyle}"</tpl>',
                        
                        ' unselectable="on"',
                    '>',
                        '{beforeLabelTextTpl}',
                        '<tpl if="fieldLabel">{fieldLabel}{labelSeparator}</tpl>',
                        '{afterLabelTextTpl}',
                    '</label>',
                    '{afterLabelTpl}',
                '</td>',
            '</tpl>',

            
            '<td role="presentation" class="{baseBodyCls} {fieldBodyCls} {extraFieldBodyCls}" id="{id}-bodyEl" colspan="{bodyColspan}" role="presentation">',
                '{beforeBodyEl}',

                
                '<tpl if="labelAlign==\'top\'">',
                    '{beforeLabelTpl}',
                    '<div role="presentation" id="{id}-labelCell" style="{labelCellStyle}">',
                        '<label id="{id}-labelEl" {labelAttrTpl}<tpl if="inputId"> for="{inputId}"</tpl> class="{labelCls}"',
                            '<tpl if="labelStyle"> style="{labelStyle}"</tpl>',
                            
                            ' unselectable="on"',
                        '>',
                            '{beforeLabelTextTpl}',
                            '<tpl if="fieldLabel">{fieldLabel}{labelSeparator}</tpl>',
                            '{afterLabelTextTpl}',
                        '</label>',
                    '</div>',
                    '{afterLabelTpl}',
                '</tpl>',

                '{beforeSubTpl}',
                '{[values.$comp.getSubTplMarkup(values)]}',
                '{afterSubTpl}',

            
            '<tpl if="msgTarget===\'side\'">',
                '{afterBodyEl}',
                '</td>',
                '<td role="presentation" id="{id}-sideErrorCell" vAlign="{[values.labelAlign===\'top\' && !values.hideLabel ? \'bottom\' : \'middle\']}" style="{[values.autoFitErrors ? \'display:none\' : \'\']}" width="{errorIconWidth}">',
                    '<div role="presentation" id="{id}-errorEl" class="{errorMsgCls}" style="display:none"></div>',
                '</td>',
            '<tpl elseif="msgTarget==\'under\'">',
                '<div role="presentation" id="{id}-errorEl" class="{errorMsgClass}" colspan="2" style="display:none"></div>',
                '{afterBodyEl}',
                '</td>',
            '</tpl>',
        '</tr>',
        {
            disableFormats: true
        }
    ],

    
    activeErrorsTpl: undefined,

    htmlActiveErrorsTpl: [
        '<tpl if="errors && errors.length">',
            '<ul class="{listCls}"><tpl for="errors"><li role="alert">{.}</li></tpl></ul>',
        '</tpl>'
    ],
    
    plaintextActiveErrorsTpl: [
        '<tpl if="errors && errors.length">',
            '<tpl for="errors"><tpl if="xindex &gt; 1">\n</tpl>{.}</tpl>',
        '</tpl>'
    ],

    
    isFieldLabelable: true,

    
    formItemCls: Ext.baseCSSPrefix + 'form-item',

    
    labelCls: Ext.baseCSSPrefix + 'form-item-label',

    

    
    errorMsgCls: Ext.baseCSSPrefix + 'form-error-msg',

    
    baseBodyCls: Ext.baseCSSPrefix + 'form-item-body',

    
    inputRowCls: Ext.baseCSSPrefix + 'form-item-input-row',

    
    fieldBodyCls: '',

    
    clearCls: Ext.baseCSSPrefix + 'clear',

    
    invalidCls : Ext.baseCSSPrefix + 'form-invalid',

    
    fieldLabel: undefined,

    
    labelAlign : 'left',

    
    labelWidth: 100,

    
    labelPad : 5,

    
    
    labelSeparator : ':',
    

    

    
    hideLabel: false,

    
    hideEmptyLabel: true,

    
    preventMark: false,

    
    autoFitErrors: true,

    
    msgTarget: 'qtip',

    

    
    noWrap: true,

    labelableInsertions: [

        
        'beforeBodyEl',

        
        'afterBodyEl',

        
        'beforeLabelTpl',

        
        'afterLabelTpl',

        
        'beforeSubTpl',

        
        'afterSubTpl',

        
        'beforeLabelTextTpl',

        
        'afterLabelTextTpl',

        
        'labelAttrTpl'
    ],

    
    labelableRenderProps: ['allowBlank', 'id', 'labelAlign', 'fieldBodyCls', 'extraFieldBodyCls', 
        'baseBodyCls', 'clearCls', 'labelSeparator', 'msgTarget', 'inputRowCls'],

    
    initLabelable: function() {
        var me = this,
            padding = me.padding;

        
        
        
        if (padding) {
            me.padding = undefined;
            me.extraMargins = Ext.Element.parseBox(padding);
        }
        
        if (!me.activeErrorsTpl) {
            if (me.msgTarget == 'title') {
                me.activeErrorsTpl = me.plaintextActiveErrorsTpl;
            } else {
                me.activeErrorsTpl = me.htmlActiveErrorsTpl;
            }
        }

        me.addCls(Ext.plainTableCls);
        me.addCls(me.formItemCls);
        
        
        me.lastActiveError = '';

        me.addEvents(
            
            'errorchange'
        );

        
        me.enableBubble('errorchange');
    },

    
    trimLabelSeparator: function() {
        var me = this,
            separator = me.labelSeparator,
            label = me.fieldLabel || '',
            lastChar = label.substr(label.length - 1);

        
        return lastChar === separator ? label.slice(0, -1) : label;
    },

    
    getFieldLabel: function() {
        return this.trimLabelSeparator();
    },
    
    
    setFieldLabel: function(label){
        label = label || '';
        
        var me = this,
            separator = me.labelSeparator,
            labelEl = me.labelEl;
        
        me.fieldLabel = label;
        if (me.rendered) {
            if (Ext.isEmpty(label) && me.hideEmptyLabel) {
                labelEl.parent().setDisplayed('none');
            } else {
                if (separator) {
                    label = me.trimLabelSeparator() + separator;
                }
                labelEl.update(label);
                labelEl.parent().setDisplayed('');
            }
            me.updateLayout();
        }
    },

    getInsertionRenderData: function (data, names) {
        var i = names.length,
            name, value;

        while (i--) {
            name = names[i];
            value = this[name];

            if (value) {
                if (typeof value != 'string') {
                    if (!value.isTemplate) {
                        value = Ext.XTemplate.getTpl(this, name);
                    }
                    value = value.apply(data);
                }
            }

            data[name] = value || '';
        }

        return data;
    },

    
    getLabelableRenderData: function() {
        var me = this,
            data,
            tempEl,
            topLabel = me.labelAlign === 'top';

        if (!Ext.form.Labelable.errorIconWidth) {
            tempEl = Ext.getBody().createChild({style: 'position:absolute', cls: Ext.baseCSSPrefix + 'form-invalid-icon'});
            Ext.form.Labelable.errorIconWidth = tempEl.getWidth() + tempEl.getMargin('l');
            tempEl.remove();
        }

        data = Ext.copyTo({
            inFormLayout   : me.ownerLayout && me.ownerLayout.type === 'form',
            inputId        : me.getInputId(),
            labelOnLeft    : !topLabel,
            hideLabel      : !me.hasVisibleLabel(),
            fieldLabel     : me.getFieldLabel(),
            labelCellStyle : me.getLabelCellStyle(),
            labelCellAttrs : me.getLabelCellAttrs(),
            labelCls       : me.getLabelCls(),
            labelStyle     : me.getLabelStyle(),
            bodyColspan    : me.getBodyColspan(),
            externalError  : !me.autoFitErrors,
            errorMsgCls    : me.getErrorMsgCls(),
            errorIconWidth : Ext.form.Labelable.errorIconWidth
        },
        me, me.labelableRenderProps, true);

        me.getInsertionRenderData(data, me.labelableInsertions);

        return data;
    },

    xhooks: {
        beforeRender: function() {
            var me = this;
            me.setFieldDefaults(me.getHierarchyState().fieldDefaults);
            if (me.ownerLayout) {
                me.addCls(Ext.baseCSSPrefix + me.ownerLayout.type + '-form-item');
            }
        },

        onRender: function() {
            var me = this,
                margins,
                side,
                style = {};

            if (me.extraMargins) {
                margins = me.el.getMargin();
                for (side in margins) {
                    if (margins.hasOwnProperty(side)) {
                        style['margin-' + side] = (margins[side] + me.extraMargins[side]) + 'px';
                    }
                }
                me.el.setStyle(style);
            }
        }
    },
    
    
    hasVisibleLabel: function(){
        if (this.hideLabel) {
            return false;
        }
        return !(this.hideEmptyLabel && !this.getFieldLabel());
    },
    
    
    getLabelWidth: function(){
        var me = this;
        if (!me.hasVisibleLabel()) {
            return 0;
        }
        return me.labelWidth + me.labelPad;
    },
    
    
    getBodyColspan: function() {
        var me = this,
            result;

        if (me.msgTarget === 'side' && (!me.autoFitErrors || me.hasActiveError())) {
            result = 1;
        } else {
            result = 2;
        }
        if (me.labelAlign !== 'top' && !me.hasVisibleLabel()) {
            result++;
        }
        return result;
    },
    
    getLabelCls: function() {
        var labelCls = this.labelCls + ' ' + Ext.dom.Element.unselectableCls,
            labelClsExtra = this.labelClsExtra;

        return labelClsExtra ? labelCls + ' ' + labelClsExtra : labelCls;
    },

    getLabelCellStyle: function() {
        var me = this,
            hideLabelCell = me.hideLabel || (!me.getFieldLabel() && me.hideEmptyLabel);

        return hideLabelCell ? 'display:none;' : '';
    },
    
    getErrorMsgCls: function() {
        var me = this,
            hideLabelCell = (me.hideLabel || (!me.fieldLabel && me.hideEmptyLabel));
        
        return me.errorMsgCls + (!hideLabelCell && me.labelAlign === 'top' ? ' ' + Ext.baseCSSPrefix + 'lbl-top-err-icon' : '');
    },

    getLabelCellAttrs: function() {
        var me = this,
            labelAlign = me.labelAlign,
            result = '';

        if (labelAlign !== 'top') {
            result = 'valign="top" halign="' + labelAlign + '" width="' + (me.labelWidth + me.labelPad) + '"';
        }
        return result + ' class="' + Ext.baseCSSPrefix + 'field-label-cell"';
    },
    
    
    getLabelStyle: function(){
        var me = this,
            labelPad = me.labelPad,
            labelStyle = '';

        
        
        if (me.labelAlign !== 'top') {
            if (me.labelWidth) {
                labelStyle = 'width:' + me.labelWidth + 'px;';
            }
            if (labelPad) {
                labelStyle += 'margin-right:' + labelPad + 'px;';
            }
        }
        
        return labelStyle + (me.labelStyle || '');
    },

    
    getSubTplMarkup: function() {
        return '';
    },

    
    getInputId: function() {
        return '';
    },

    
    getActiveError : function() {
        return this.activeError || '';
    },

    
    hasActiveError: function() {
        return !!this.getActiveError();
    },

    
    setActiveError: function(msg) {
        this.setActiveErrors(msg);
    },

    
    getActiveErrors: function() {
        return this.activeErrors || [];
    },

    
    setActiveErrors: function(errors) {
        errors = Ext.Array.from(errors);
        this.activeError = errors[0];
        this.activeErrors = errors;
        this.activeError = this.getTpl('activeErrorsTpl').apply({
            errors: errors,
            listCls: Ext.plainListCls 
        });
        this.renderActiveError();
    },

    
    unsetActiveError: function() {
        delete this.activeError;
        delete this.activeErrors;
        this.renderActiveError();
    },

    
    renderActiveError: function() {
        var me = this,
            activeError = me.getActiveError(),
            hasError = !!activeError;

        if (activeError !== me.lastActiveError) {
            me.fireEvent('errorchange', me, activeError);
            me.lastActiveError = activeError;
        }

        if (me.rendered && !me.isDestroyed && !me.preventMark) {
            
            me.el[hasError ? 'addCls' : 'removeCls'](me.invalidCls);

            
            me.getActionEl().dom.setAttribute('aria-invalid', hasError);

            
            if (me.errorEl) {
                me.errorEl.dom.innerHTML = activeError;
            }
        }
    },

    
    setFieldDefaults: function(defaults) {
        var key;

        for (key in defaults) {
            if (!this.hasOwnProperty(key)) {
                this[key] = defaults[key];
            }
        }
    }
});


Ext.define('Ext.form.field.Field', {
    
    isFormField : true,

    

    

    
    disabled : false,

    
    submitValue: true,

    
    validateOnChange: true,

    
    suspendCheckChange: 0,

    
    initField: function() {
        this.addEvents(
            
            'change',
            
            'validitychange',
            
            'dirtychange'
        );

        this.initValue();
        
    },

    
    initValue: function() {
        var me = this;

        me.value = me.transformOriginalValue(me.value);
        
        me.originalValue = me.lastValue = me.value;

        
        me.suspendCheckChange++;
        me.setValue(me.value);
        me.suspendCheckChange--;
    },
    
    
    transformOriginalValue: Ext.identityFn,

    
    getName: function() {
        return this.name;
    },

    
    getValue: function() {
        return this.value;
    },

    
    setValue: function(value) {
        var me = this;
        me.value = value;
        me.checkChange();
        return me;
    },

    
    isEqual: function(value1, value2) {
        return String(value1) === String(value2);
    },

    
    isEqualAsString: function(value1, value2){
        return String(Ext.value(value1, '')) === String(Ext.value(value2, ''));
    },

    
    getSubmitData: function() {
        var me = this,
            data = null;
        if (!me.disabled && me.submitValue && !me.isFileUpload()) {
            data = {};
            data[me.getName()] = '' + me.getValue();
        }
        return data;
    },

    
    getModelData: function() {
        var me = this,
            data = null;
        if (!me.disabled && !me.isFileUpload()) {
            data = {};
            data[me.getName()] = me.getValue();
        }
        return data;
    },

    
    reset : function(){
        var me = this;

        me.beforeReset();
        me.setValue(me.originalValue);
        me.clearInvalid();
        
        delete me.wasValid;
    },
    
    
    beforeReset: Ext.emptyFn,

    
    resetOriginalValue: function() {
        this.originalValue = this.getValue();
        this.checkDirty();
    },

    
    checkChange: function() {
        if (!this.suspendCheckChange) {
            var me = this,
                newVal = me.getValue(),
                oldVal = me.lastValue;
            if (!me.isEqual(newVal, oldVal) && !me.isDestroyed) {
                me.lastValue = newVal;
                me.fireEvent('change', me, newVal, oldVal);
                me.onChange(newVal, oldVal);
            }
        }
    },

    
    onChange: function(newVal, oldVal) {
        if (this.validateOnChange) {
            this.validate();
        }
        this.checkDirty();
    },

    
    isDirty : function() {
        var me = this;
        return !me.disabled && !me.isEqual(me.getValue(), me.originalValue);
    },

    
    checkDirty: function() {
        var me = this,
            isDirty = me.isDirty();
        if (isDirty !== me.wasDirty) {
            me.fireEvent('dirtychange', me, isDirty);
            me.onDirtyChange(isDirty);
            me.wasDirty = isDirty;
        }
    },

    
    onDirtyChange: Ext.emptyFn,

    
    getErrors: function(value) {
        return [];
    },

    
    isValid : function() {
        var me = this;
        return me.disabled || Ext.isEmpty(me.getErrors());
    },

    
    validate : function() {
        var me = this,
            isValid = me.isValid();
        if (isValid !== me.wasValid) {
            me.wasValid = isValid;
            me.fireEvent('validitychange', me, isValid);
        }
        return isValid;
    },

    
    batchChanges: function(fn) {
        try {
            this.suspendCheckChange++;
            fn();
        } catch(e){
            throw e;
        } finally {
            this.suspendCheckChange--;
        }
        this.checkChange();
    },

    
    isFileUpload: function() {
        return false;
    },

    
    extractFileInput: function() {
        return null;
    },

    
    markInvalid: Ext.emptyFn,

    
    clearInvalid: Ext.emptyFn

});


Ext.define('Ext.layout.component.field.Field', {

    

    extend:  Ext.layout.component.Auto ,

    alias: 'layout.field',

                                                                       

    

    type: 'field',
    
    naturalSizingProp: 'size',

    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner;

        me.callParent(arguments);

        ownerContext.labelStrategy = me.getLabelStrategy();
        ownerContext.errorStrategy = me.getErrorStrategy();

        ownerContext.labelContext = ownerContext.getEl('labelEl');
        ownerContext.bodyCellContext = ownerContext.getEl('bodyEl');
        ownerContext.inputContext = ownerContext.getEl('inputEl');
        ownerContext.errorContext = ownerContext.getEl('errorEl');

        
        
        if (Ext.isIE7m && Ext.isStrict && ownerContext.inputContext) {
            me.ieInputWidthAdjustment = ownerContext.inputContext.getPaddingInfo().width + ownerContext.inputContext.getBorderInfo().width;
        }

        
        ownerContext.labelStrategy.prepare(ownerContext, owner);
        ownerContext.errorStrategy.prepare(ownerContext, owner);
    },
    
    beginLayoutCycle: function(ownerContext){
        var me = this,
            owner = me.owner,
            widthModel = ownerContext.widthModel,
            ownerNaturalSize = owner[me.naturalSizingProp],
            width;
            
        me.callParent(arguments);
        
        if (widthModel.shrinkWrap) {
            
            me.beginLayoutShrinkWrap(ownerContext);
        } else if (widthModel.natural) {

            
            if (typeof ownerNaturalSize == 'number' && !owner.inputWidth) {
                me.beginLayoutFixed(ownerContext, (width = ownerNaturalSize * 6.5 + 20), 'px');
            }

            
            else {
                me.beginLayoutShrinkWrap(ownerContext);
            }
            ownerContext.setWidth(width, false);
        } else {
            me.beginLayoutFixed(ownerContext, '100', '%');
        }    
    },

    beginLayoutFixed: function (ownerContext, width, suffix) {
        var owner = ownerContext.target,
            inputEl = owner.inputEl,
            inputWidth = owner.inputWidth;

        owner.el.setStyle('table-layout', 'fixed');
        owner.bodyEl.setStyle('width', width + suffix);
        if (inputEl) {
            if (inputWidth) {
                inputEl.setStyle('width', inputWidth + 'px');
            } else {
                inputEl.setStyle('width', owner.stretchInputElFixed ? '100%' : '');
            }
        }
        ownerContext.isFixed = true;
    },

    beginLayoutShrinkWrap: function (ownerContext) {
        var owner = ownerContext.target,
            inputEl = owner.inputEl,
            inputWidth = owner.inputWidth;

        if (inputEl && inputEl.dom) {
            inputEl.dom.removeAttribute('size');
            if (inputWidth) {
                inputEl.setStyle('width', inputWidth + 'px');
            } else {
                inputEl.setStyle('width', '');
            }
        }
        owner.el.setStyle('table-layout', 'auto');
        owner.bodyEl.setStyle('width', '');
    },

    finishedLayout: function(ownerContext){
        var owner = this.owner;

        this.callParent(arguments);        
        ownerContext.labelStrategy.finishedLayout(ownerContext, owner);
        ownerContext.errorStrategy.finishedLayout(ownerContext, owner);
    },

    calculateOwnerHeightFromContentHeight: function(ownerContext, contentHeight) {
        return contentHeight;
    },

    measureContentHeight: function (ownerContext) {
        return ownerContext.el.getHeight();
    },
    
    measureContentWidth: function (ownerContext) {
        return ownerContext.el.getWidth();
    },

    measureLabelErrorHeight: function (ownerContext) {
        return ownerContext.labelStrategy.getHeight(ownerContext) +
               ownerContext.errorStrategy.getHeight(ownerContext);
    },

    onFocus: function() {
        this.getErrorStrategy().onFocus(this.owner);    
    },

    
    getLabelStrategy: function() {
        var me = this,
            strategies = me.labelStrategies,
            labelAlign = me.owner.labelAlign;
        return strategies[labelAlign] || strategies.base;
    },

    
    getErrorStrategy: function() {
        var me = this,
            owner = me.owner,
            strategies = me.errorStrategies,
            msgTarget = owner.msgTarget;
        return !owner.preventMark && Ext.isString(msgTarget) ?
                (strategies[msgTarget] || strategies.elementId) :
                strategies.none;
    },

    
    labelStrategies: (function() {
        var base = {
                prepare: function(ownerContext, owner) {
                    var cls = owner.labelCls + '-' + owner.labelAlign,
                        labelEl = owner.labelEl;

                    if (labelEl) {
                        labelEl.addCls(cls);
                    }
                },

                getHeight: function () {
                    return 0;
                },
                
                finishedLayout: Ext.emptyFn
            };

        return {
            base: base,

            
            top: Ext.applyIf({        
                        
                getHeight: function (ownerContext) {
                    var labelContext = ownerContext.labelContext,
                        props = labelContext.props,
                        height = props.height;
                        
                    if (height === undefined) {
                        props.height = height = labelContext.el.getHeight();
                    }

                    return height;
                }
            }, base),

            
            left: base,

            
            right: base
        };
    }()),

    
    errorStrategies: (function() {
        function showTip(owner) {
            var tip = Ext.layout.component.field.Field.tip,
                target;
                
            if (tip && tip.isVisible()) {
                target = tip.activeTarget;
                if (target && target.el === owner.getActionEl().dom) {
                    tip.toFront(true);
                }
            }
        }

        var applyIf = Ext.applyIf,
            emptyFn = Ext.emptyFn,
            iconCls = Ext.baseCSSPrefix + 'form-invalid-icon',
            iconWidth,
            base = {
                prepare: function(ownerContext, owner) {
                    var el = owner.errorEl;
                    if (el) {
                        el.setDisplayed(false);
                    }
                },
                getHeight: function () {
                    return 0;
                },
                onFocus: emptyFn,
                finishedLayout: emptyFn
            };

        return {
            none: base,

            
            side: applyIf({
                prepare: function(ownerContext, owner) {
                    var errorEl = owner.errorEl,
                        sideErrorCell = owner.sideErrorCell,
                        displayError = owner.hasActiveError(),
                        tempEl;

                    
                    if (!iconWidth) {
                        iconWidth = (tempEl = Ext.getBody().createChild({style: 'position:absolute', cls: iconCls})).getWidth();
                        tempEl.remove();
                    }

                    errorEl.addCls(iconCls);
                    errorEl.set({'data-errorqtip': owner.getActiveError() || ''});
                    if (owner.autoFitErrors) {
                        errorEl.setDisplayed(displayError);
                    }
                    
                    else {
                        errorEl.setVisible(displayError);
                    }

                    
                    if (sideErrorCell && owner.autoFitErrors) {
                        sideErrorCell.setDisplayed(displayError);
                    }
                    owner.bodyEl.dom.colSpan = owner.getBodyColspan();

                    
                    Ext.layout.component.field.Field.initTip();
                },
                onFocus: showTip
            }, base),

            
            under: applyIf({
                prepare: function(ownerContext, owner) {
                    var errorEl = owner.errorEl,
                        cls = Ext.baseCSSPrefix + 'form-invalid-under';

                    errorEl.addCls(cls);
                    errorEl.setDisplayed(owner.hasActiveError());
                },
                getHeight: function (ownerContext) {
                    var height = 0,
                        errorContext, props;

                    if (ownerContext.target.hasActiveError()) {
                        errorContext = ownerContext.errorContext;
                        props = errorContext.props;
                        height = props.height;

                        if (height === undefined) {
                            props.height = height = errorContext.el.getHeight();
                        }
                    }

                    return height;
                }
            }, base),

            
            qtip: applyIf({
                prepare: function(ownerContext, owner) {
                    Ext.layout.component.field.Field.initTip();
                    owner.getActionEl().dom.setAttribute('data-errorqtip', owner.getActiveError() || '');
                },
                onFocus: showTip
            }, base),

            
            title: applyIf({
                prepare: function(ownerContext, owner) {
                    owner.getActionEl().dom.setAttribute('title', owner.getActiveError() || '');
                }
            }, base),

            
            elementId: applyIf({
                prepare: function(ownerContext, owner) {
                    var targetEl = Ext.fly(owner.msgTarget);
                    if (targetEl) {
                        targetEl.dom.innerHTML = owner.getActiveError() || '';
                        targetEl.setDisplayed(owner.hasActiveError());
                    }
                }
            }, base)
        };
    }()),

    statics: {
        
        initTip: function() {
            var tip = this.tip;
            if (!tip) {
                tip = this.tip = Ext.create('Ext.tip.QuickTip', {
                    ui: 'form-invalid'
                });
                tip.tagConfig = Ext.apply({}, {attribute: 'errorqtip'}, tip.tagConfig);
            }
        },

        
        destroyTip: function() {
            var tip = this.tip;
            if (tip) {
                tip.destroy();
                delete this.tip;
            }
        }
    }
});


Ext.define('Ext.form.field.Base', {
    extend:  Ext.Component ,
    mixins: {
        labelable:  Ext.form.Labelable ,
        field:  Ext.form.field.Field 
    },
    alias: 'widget.field',
    alternateClassName: ['Ext.form.Field', 'Ext.form.BaseField'],
                                                                                            

    
    fieldSubTpl: [ 
        '<input id="{id}" type="{type}" {inputAttrTpl}',
            ' size="1"', 
            '<tpl if="name"> name="{name}"</tpl>',
            '<tpl if="value"> value="{[Ext.util.Format.htmlEncode(values.value)]}"</tpl>',
            '<tpl if="placeholder"> placeholder="{placeholder}"</tpl>',
            '{%if (values.maxLength !== undefined){%} maxlength="{maxLength}"{%}%}',
            '<tpl if="readOnly"> readonly="readonly"</tpl>',
            '<tpl if="disabled"> disabled="disabled"</tpl>',
            '<tpl if="tabIdx"> tabIndex="{tabIdx}"</tpl>',
            '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
        ' class="{fieldCls} {typeCls} {editableCls} {inputCls}" autocomplete="off"/>',
        {
            disableFormats: true
        }
    ],

    subTplInsertions: [
        
        'inputAttrTpl'
    ],

    

    
    inputType: 'text',

    

    
    
    invalidText : 'The value in this field is invalid',
    

    
    fieldCls : Ext.baseCSSPrefix + 'form-field',

    

    
    focusCls : 'form-focus',

    
    dirtyCls : Ext.baseCSSPrefix + 'form-dirty',

    
    checkChangeEvents: Ext.isIE && (!document.documentMode || document.documentMode < 9) ?
                        ['change', 'propertychange', 'keyup'] :
                        ['change', 'input', 'textInput', 'keyup', 'dragdrop'],

    
    checkChangeBuffer: 50,

    componentLayout: 'field',

    
    readOnly : false,

    
    readOnlyCls: Ext.baseCSSPrefix + 'form-readonly',

    

    
    validateOnBlur: true,

    
    hasFocus : false,

    baseCls: Ext.baseCSSPrefix + 'field',

    maskOnDisable: false,
    
    
    
    
    stretchInputElFixed: true,

    
    initComponent : function() {
        var me = this;

        me.callParent();

        me.subTplData = me.subTplData || {};

        me.addEvents(
            
            'specialkey',

            
            'writeablechange'
        );

        
        me.initLabelable();
        me.initField();

        
        if (!me.name) {
            me.name = me.getInputId();
        }
        
        if (me.readOnly) {
            me.addCls(me.readOnlyCls);
        }
        
        me.addCls(Ext.baseCSSPrefix + 'form-type-' + me.inputType);
    },

    
    getInputId: function() {
        return this.inputId || (this.inputId = this.id + '-inputEl');
    },

    
    getSubTplData: function() {
        var me = this,
            type = me.inputType,
            inputId = me.getInputId(),
            data;
        
        data = Ext.apply({
            id         : inputId,
            cmpId      : me.id,
            name       : me.name || inputId,
            disabled   : me.disabled,
            readOnly   : me.readOnly,
            value      : me.getRawValue(),
            type       : type,
            fieldCls   : me.fieldCls,
            fieldStyle : me.getFieldStyle(),
            tabIdx     : me.tabIndex,
            inputCls   : me.inputCls,
            typeCls    : Ext.baseCSSPrefix + 'form-' + (type === 'password' ? 'text' : type)
        }, me.subTplData);

        me.getInsertionRenderData(data, me.subTplInsertions);

        return data;
    },

    applyRenderSelectors: function() {
        var me = this;

        me.callParent();

        
        
        me.addChildEls('inputEl');

        
        me.inputEl = me.el.getById(me.getInputId());
    },

    
    getSubTplMarkup: function() {
        return this.getTpl('fieldSubTpl').apply(this.getSubTplData());
    },

    initRenderTpl: function() {
        var me = this;
        if (!me.hasOwnProperty('renderTpl')) {
            me.renderTpl = me.getTpl('labelableRenderTpl');
        }
        return me.callParent();
    },

    initRenderData: function() {
        return Ext.applyIf(this.callParent(), this.getLabelableRenderData());
    },

    
    setFieldStyle: function(style) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl) {
            inputEl.applyStyles(style);
        }
        me.fieldStyle = style;
    },

    getFieldStyle: function() {
        return Ext.isObject(this.fieldStyle) ? Ext.DomHelper.generateStyles(this.fieldStyle) : this.fieldStyle ||'';
    },

    
    onRender : function() {
        this.callParent(arguments);
        this.renderActiveError();
    },

    getFocusEl: function() {
        return this.inputEl;
    },

    isFileUpload: function() {
        return this.inputType === 'file';
    },

    
    getSubmitData: function() {
        var me = this,
            data = null,
            val;
        if (!me.disabled && me.submitValue && !me.isFileUpload()) {
            val = me.getSubmitValue();
            if (val !== null) {
                data = {};
                data[me.getName()] = val;
            }
        }
        return data;
    },

    
    getSubmitValue: function() {
        return this.processRawValue(this.getRawValue());
    },

    
    getRawValue: function() {
        var me = this,
            v = (me.inputEl ? me.inputEl.getValue() : Ext.value(me.rawValue, ''));
        me.rawValue = v;
        return v;
    },

    
    setRawValue: function(value) {
        var me = this;
        value = Ext.value(me.transformRawValue(value), '');
        me.rawValue = value;

        
        if (me.inputEl) {
            me.inputEl.dom.value = value;
        }
        return value;
    },
    
    
    transformRawValue: Ext.identityFn,

    
    valueToRaw: function(value) {
        return '' + Ext.value(value, '');
    },

    
    rawToValue: Ext.identityFn,

    
    processRawValue: Ext.identityFn,

    
    getValue: function() {
        var me = this,
            val = me.rawToValue(me.processRawValue(me.getRawValue()));
        me.value = val;
        return val;
    },

    
    setValue: function(value) {
        var me = this;
        me.setRawValue(me.valueToRaw(value));
        return me.mixins.field.setValue.call(me, value);
    },

    onBoxReady: function() {
        var me = this;
        me.callParent();
        
        if (me.setReadOnlyOnBoxReady) {
            me.setReadOnly(me.readOnly);
        }
            
    },

    
    onDisable: function() {
        var me = this,
            inputEl = me.inputEl;
            
        me.callParent();
        if (inputEl) {
            inputEl.dom.disabled = true;
            if (me.hasActiveError()) {
                
                me.clearInvalid();
                me.needsValidateOnEnable = true;
            }
        }
    },

    
    onEnable: function() {
        var me = this,
            inputEl = me.inputEl;
            
        me.callParent();
        if (inputEl) {
            inputEl.dom.disabled = false;
            if (me.needsValidateOnEnable) {
                delete me.needsValidateOnEnable;
                
                me.forceValidation = true;
                me.isValid();
                delete me.forceValidation;
            }
        }
    },

    
    setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
        readOnly = !!readOnly;
        me[readOnly ? 'addCls' : 'removeCls'](me.readOnlyCls);
        me.readOnly = readOnly;
        if (inputEl) {
            inputEl.dom.readOnly = readOnly;
        } else if (me.rendering) {
            me.setReadOnlyOnBoxReady = true;
        }
        me.fireEvent('writeablechange', me, readOnly);
    },

    
    fireKey: function(e){
        if(e.isSpecialKey()){
            this.fireEvent('specialkey', this, new Ext.EventObjectImpl(e));
        }
    },

    
    initEvents : function(){
        var me = this,
            inputEl = me.inputEl,
            onChangeTask,
            onChangeEvent,
            events = me.checkChangeEvents,
            e,
            eLen   = events.length,
            event;

        if (inputEl) {
            me.mon(inputEl, Ext.EventManager.getKeyEvent(), me.fireKey,  me);

            
            onChangeTask = new Ext.util.DelayedTask(me.checkChange, me);
            me.onChangeEvent = onChangeEvent = function() {
                onChangeTask.delay(me.checkChangeBuffer);
            };

            for (e = 0; e < eLen; e++) {
                event = events[e];

                if (event === 'propertychange') {
                    me.usesPropertychange = true;
                }

                me.mon(inputEl, event, onChangeEvent);
            }
        }
        me.callParent();
    },

    doComponentLayout: function() {
        var me = this,
            inputEl = me.inputEl,
            usesPropertychange = me.usesPropertychange,
            ename = 'propertychange',
            onChangeEvent = me.onChangeEvent;

        
        
        
        if (usesPropertychange) {
            me.mun(inputEl, ename, onChangeEvent);
        }
        me.callParent(arguments);
        if (usesPropertychange) {
            me.mon(inputEl, ename, onChangeEvent);
        }
    },

    
    onDirtyChange: function(isDirty) {
        this[isDirty ? 'addCls' : 'removeCls'](this.dirtyCls);
    },


    
    isValid : function() {
        var me = this,
            disabled = me.disabled,
            validate = me.forceValidation || !disabled;
            
        
        return validate ? me.validateValue(me.processRawValue(me.getRawValue())) : disabled;
    },


    
    validateValue: function(value) {
        var me = this,
            errors = me.getErrors(value),
            isValid = Ext.isEmpty(errors);
        if (!me.preventMark) {
            if (isValid) {
                me.clearInvalid();
            } else {
                me.markInvalid(errors);
            }
        }

        return isValid;
    },

    
    markInvalid : function(errors) {
        
        var me = this,
            oldMsg = me.getActiveError(),
            active;
            
        me.setActiveErrors(Ext.Array.from(errors));
        active = me.getActiveError();
        if (oldMsg !== active) {
            me.setError(active);
        }
    },

    
    clearInvalid : function() {
        
        var me = this,
            hadError = me.hasActiveError();
            
        delete me.needsValidateOnEnable;
        me.unsetActiveError();
        if (hadError) {
            me.setError('');
        }
    },
    
    
    setError: function(active){
        var me = this,
            msgTarget = me.msgTarget,
            prop;
            
        if (me.rendered) {
            if (msgTarget == 'title' || msgTarget == 'qtip') {
                if (me.rendered) {
                    prop = msgTarget == 'qtip' ? 'data-errorqtip' : 'title';
                }
                me.getActionEl().dom.setAttribute(prop, active || '');
            } else {
                me.updateLayout();
            }
        }
    },

    
    renderActiveError: function() {
        var me = this,
            hasError = me.hasActiveError();
        if (me.inputEl) {
            
            me.inputEl[hasError ? 'addCls' : 'removeCls'](me.invalidCls + '-field');
        }
        me.mixins.labelable.renderActiveError.call(me);
    },


    getActionEl: function() {
        return this.inputEl || this.el;
    }

});


Ext.define('Ext.form.field.VTypes', (function(){
    
    var alpha = /^[a-zA-Z_]+$/,
        alphanum = /^[a-zA-Z0-9_]+$/,

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        email = /^(")?(?:[^\."])(?:(?:[\.])?(?:[\w\-!#$%&'*+/=?^_`{|}~]))*\1@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
        url = /(((^https?)|(^ftp)):\/\/((([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*)|(localhost|LOCALHOST))\/?)/i;

    
    return {
        singleton: true,
        alternateClassName: 'Ext.form.VTypes',

        
        'email' : function(v){
            return email.test(v);
        },
        
        
        'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
        
        
        'emailMask' : /[\w.\-@'"!#$%&'*+/=?^_`{|}~]/i,

        
        'url' : function(v){
            return url.test(v);
        },
        
        
        'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
        

        
        'alpha' : function(v){
            return alpha.test(v);
        },
        
        
        'alphaText' : 'This field should only contain letters and _',
        
        
        'alphaMask' : /[a-z_]/i,

        
        'alphanum' : function(v){
            return alphanum.test(v);
        },
        
        
        'alphanumText' : 'This field should only contain letters, numbers and _',
        
        
        'alphanumMask' : /[a-z0-9_]/i
    };
}()));


Ext.define('Ext.layout.component.field.Text', {
    extend:  Ext.layout.component.field.Field ,
    alias: 'layout.textfield',
                                       

    type: 'textfield',
    
    canGrowWidth: true,

    beginLayoutCycle: function(ownerContext) {
        this.callParent(arguments);
        
        
        if (ownerContext.heightModel.shrinkWrap) {
            ownerContext.inputContext.el.setStyle('height', '');
        }
    },

    measureContentWidth: function (ownerContext) {
        var me = this,
            owner = me.owner,
            width = me.callParent(arguments),
            inputContext = ownerContext.inputContext,
            inputEl, value, calcWidth, max, min;

        if (owner.grow && me.canGrowWidth && !ownerContext.state.growHandled) {
            inputEl = owner.inputEl;

            
            value = Ext.util.Format.htmlEncode(inputEl.dom.value || (owner.hasFocus ? '' : owner.emptyText) || '');
            value += owner.growAppend;
            calcWidth = inputEl.getTextWidth(value) + inputContext.getFrameInfo().width;

            max = owner.growMax;
            min = Math.min(max, width);
            max = Math.max(owner.growMin, max, min);

            
            calcWidth = Ext.Number.constrain(calcWidth, owner.growMin, max);
            inputContext.setWidth(calcWidth);
            ownerContext.state.growHandled = true;
            
            
            inputContext.domBlock(me, 'width');
            width = NaN;
        }
        return width;
    },
    
    publishInnerHeight: function(ownerContext, height) {
        ownerContext.inputContext.setHeight(height - this.measureLabelErrorHeight(ownerContext));
    },

    beginLayoutFixed: function(ownerContext, width, suffix) {
        var me = this,
            ieInputWidthAdjustment = me.ieInputWidthAdjustment;

        if (ieInputWidthAdjustment) {
            me.adjustIEInputPadding(ownerContext);
            if(suffix === 'px') {
                width -= ieInputWidthAdjustment;
            }
        }

        me.callParent(arguments);
    },

    adjustIEInputPadding: function(ownerContext) {
        
        this.owner.bodyEl.setStyle('padding-right', this.ieInputWidthAdjustment + 'px');
    }
});


Ext.define('Ext.form.field.Text', {
    extend: Ext.form.field.Base ,
    alias: 'widget.textfield',
                                                                           
    alternateClassName: ['Ext.form.TextField', 'Ext.form.Text'],

    

    

    
    size: 20,

    

    
    growMin : 30,

    
    growMax : 800,

    
    
    growAppend: 'W',
    

    

    

    

    
    allowBlank : true,

    
    validateBlank: false,

    
    allowOnlyWhitespace: true,

    
    minLength : 0,

    
    maxLength : Number.MAX_VALUE,

    

    
    
    minLengthText : 'The minimum length for this field is {0}',
    

    
    
    maxLengthText : 'The maximum length for this field is {0}',
    

    

    
    
    blankText : 'This field is required',
    

    

    

    
    regexText : '',

    

    
    emptyCls : Ext.baseCSSPrefix + 'form-empty-field',

    
    requiredCls : Ext.baseCSSPrefix + 'form-required-field',

    

    componentLayout: 'textfield',

    
    valueContainsPlaceholder : false,


    initComponent: function () {
        var me = this;
        
        if (me.allowOnlyWhitespace === false) {
            me.allowBlank = false;
        }

        me.callParent();

        me.addEvents(
            
            'autosize',

            
            'keydown',
            
            'keyup',
            
            'keypress'
        );
        me.addStateEvents('change');
        me.setGrowSizePolicy();
    },

    
    setGrowSizePolicy: function(){
        if (this.grow) {
            this.shrinkWrap |= 1; 
        }    
    },

    
    initEvents : function(){
        var me = this,
            el = me.inputEl;

        me.callParent();
        if(me.selectOnFocus || me.emptyText){
            me.mon(el, 'mousedown', me.onMouseDown, me);
        }
        if(me.maskRe || (me.vtype && me.disableKeyFilter !== true && (me.maskRe = Ext.form.field.VTypes[me.vtype+'Mask']))){
            me.mon(el, 'keypress', me.filterKeys, me);
        }

        if (me.enableKeyEvents) {
            me.mon(el, {
                scope: me,
                keyup: me.onKeyUp,
                keydown: me.onKeyDown,
                keypress: me.onKeyPress
            });
        }
    },

    
    isEqual: function(value1, value2) {
        return this.isEqualAsString(value1, value2);
    },

    
    onChange: function(newVal, oldVal) {
        this.callParent(arguments);
        this.autoSize();
    },

    getSubTplData: function() {
        var me = this,
            value = me.getRawValue(),
            isEmpty = me.emptyText && value.length < 1,
            maxLength = me.maxLength,
            placeholder;
            
        
        
        
        if (me.enforceMaxLength) {
            if (maxLength === Number.MAX_VALUE) {
                maxLength = undefined;
            }
        } else {
            maxLength = undefined;
        }

        if (isEmpty) {
            if (Ext.supports.Placeholder) {
                placeholder = me.emptyText;
            } else {
                value = me.emptyText;
                me.valueContainsPlaceholder = true;
            }
        }

        return Ext.apply(me.callParent(), {
            maxLength   : maxLength,
            readOnly    : me.readOnly,
            placeholder : placeholder,
            value       : value,
            fieldCls    : me.fieldCls + ((isEmpty && (placeholder || value)) ? ' ' + me.emptyCls : '') + (me.allowBlank ? '' :  ' ' + me.requiredCls)
        });
    },

    afterRender: function(){
        this.autoSize();
        this.callParent();
    },

    onMouseDown: function(e){
        var me = this;
        if(!me.hasFocus){
            me.mon(me.inputEl, 'mouseup', Ext.emptyFn, me, { single: true, preventDefault: true });
        }
    },

    
    processRawValue: function(value) {
        var me = this,
            stripRe = me.stripCharsRe,
            newValue;

        if (stripRe) {
            newValue = value.replace(stripRe, '');
            if (newValue !== value) {
                me.setRawValue(newValue);
                value = newValue;
            }
        }
        return value;
    },

    
    onDisable: function(){
        this.callParent();
        if (Ext.isIE) {
            this.inputEl.dom.unselectable = 'on';
        }
    },

    
    onEnable: function(){
        this.callParent();
        if (Ext.isIE) {
            this.inputEl.dom.unselectable = '';
        }
    },

    onKeyDown: function(e) {
        this.fireEvent('keydown', this, e);
    },

    onKeyUp: function(e) {
        this.fireEvent('keyup', this, e);
    },

    onKeyPress: function(e) {
        this.fireEvent('keypress', this, e);
    },

    
    reset : function(){
        this.callParent();
        this.applyEmptyText();
    },

    applyEmptyText : function(){
        var me = this,
            emptyText = me.emptyText,
            isEmpty;

        if (me.rendered && emptyText) {
            isEmpty = me.getRawValue().length < 1 && !me.hasFocus;

            if (Ext.supports.Placeholder) {
                me.inputEl.dom.placeholder = emptyText;
            } else if (isEmpty) {
                me.setRawValue(emptyText);
                me.valueContainsPlaceholder = true;
            }

            
            
            if (isEmpty) {
                me.inputEl.addCls(me.emptyCls);
            }

            me.autoSize();
        }
    },
    
    afterFirstLayout: function() {
        this.callParent();
        if (Ext.isIE && this.disabled) {
            var el = this.inputEl;
            if (el) {
                el.dom.unselectable = 'on';
            }
        }
    },
    
    
    beforeFocus : function(){
        var me = this,
            inputEl = me.inputEl,
            emptyText = me.emptyText,
            isEmpty;

        me.callParent(arguments);
        if ((emptyText && !Ext.supports.Placeholder) && (inputEl.dom.value === me.emptyText && me.valueContainsPlaceholder)) {
            me.setRawValue('');
            isEmpty = true;
            inputEl.removeCls(me.emptyCls);
            me.valueContainsPlaceholder = false;
        } else if (Ext.supports.Placeholder) {
            me.inputEl.removeCls(me.emptyCls);
        }
        if (me.selectOnFocus || isEmpty) {
            
            if (Ext.isWebKit) {
                if (!me.inputFocusTask) {
                    me.inputFocusTask = new Ext.util.DelayedTask(me.focusInput, me);
                }
                me.inputFocusTask.delay(1);
            } else {
                inputEl.dom.select();
            }
        }
    },
    
    focusInput: function(){
        var input = this.inputEl;
        if (input) {
            input = input.dom;
            if (input) {
                input.select();
            }
        }    
    },

    onFocus: function() {
        var me = this;
        me.callParent(arguments);
        if (me.emptyText) {
            me.autoSize();
        }
    },

    
    postBlur : function(){
        this.callParent(arguments);
        this.applyEmptyText();
    },

    
    filterKeys : function(e){
        
        if (e.ctrlKey && !e.altKey) {
            return;
        }
        var key = e.getKey(),
            charCode = String.fromCharCode(e.getCharCode());

        if((Ext.isGecko || Ext.isOpera) && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){
            return;
        }

        if((!Ext.isGecko && !Ext.isOpera) && e.isSpecialKey() && !charCode){
            return;
        }
        if(!this.maskRe.test(charCode)){
            e.stopEvent();
        }
    },

    getState: function() {
        return this.addPropertyToState(this.callParent(), 'value');
    },

    applyState: function(state) {
        this.callParent(arguments);
        if(state.hasOwnProperty('value')) {
            this.setValue(state.value);
        }
    },

    
    getRawValue: function() {
        var me = this,
            v = me.callParent();
        if (v === me.emptyText && me.valueContainsPlaceholder) {
            v = '';
        }
        return v;
    },

    
    setValue: function(value) {
        var me = this,
            inputEl = me.inputEl;

        if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
            inputEl.removeCls(me.emptyCls);
            me.valueContainsPlaceholder = false;
        }

        me.callParent(arguments);

        me.applyEmptyText();
        return me;
    },

    
    getErrors: function(value) {
        var me = this,
            errors = me.callParent(arguments),
            validator = me.validator,
            vtype = me.vtype,
            vtypes = Ext.form.field.VTypes,
            regex = me.regex,
            format = Ext.String.format,
            msg, trimmed, isBlank;

        value = value || me.processRawValue(me.getRawValue());

        if (Ext.isFunction(validator)) {
            msg = validator.call(me, value);
            if (msg !== true) {
                errors.push(msg);
            }
        }
        
        trimmed = me.allowOnlyWhitespace ? value : Ext.String.trim(value);

        if (trimmed.length < 1 || (value === me.emptyText && me.valueContainsPlaceholder)) {
            if (!me.allowBlank) {
                errors.push(me.blankText);
            }
            
            if (!me.validateBlank) {
                return errors;
            }
            isBlank = true;
        }

        
        
        if (!isBlank && value.length < me.minLength) {
            errors.push(format(me.minLengthText, me.minLength));
        }

        if (value.length > me.maxLength) {
            errors.push(format(me.maxLengthText, me.maxLength));
        }

        if (vtype) {
            if (!vtypes[vtype](value, me)) {
                errors.push(me.vtypeText || vtypes[vtype +'Text']);
            }
        }

        if (regex && !regex.test(value)) {
            errors.push(me.regexText || me.invalidText);
        }

        return errors;
    },

    
    selectText : function(start, end){
        var me = this,
            v = me.getRawValue(),
            doFocus = true,
            el = me.inputEl.dom,
            undef,
            range;

        if (v.length > 0) {
            start = start === undef ? 0 : start;
            end = end === undef ? v.length : end;
            if (el.setSelectionRange) {
                el.setSelectionRange(start, end);
            }
            else if(el.createTextRange) {
                range = el.createTextRange();
                range.moveStart('character', start);
                range.moveEnd('character', end - v.length);
                range.select();
            }
            doFocus = Ext.isGecko || Ext.isOpera;
        }
        if (doFocus) {
            me.focus();
        }
    },

    
    autoSize: function() {
        var me = this;
        if (me.grow && me.rendered) {
            me.autoSizing = true;
            me.updateLayout();
        }
    },

    afterComponentLayout: function() {
        var me = this,
            width;

        me.callParent(arguments);
        if (me.autoSizing) {
            width = me.inputEl.getWidth();
            if (width !== me.lastInputWidth) {
                me.fireEvent('autosize', me, width);
                me.lastInputWidth = width;
                delete me.autoSizing;
            }
        }
    },
    
    onDestroy: function(){
        var me = this;
        me.callParent();
        
        if (me.inputFocusTask) {
            me.inputFocusTask.cancel();
            me.inputFocusTask = null;
        }
    }
});


Ext.define('Ext.layout.component.field.TextArea', {
    extend:  Ext.layout.component.field.Text ,
    alias: 'layout.textareafield',

    type: 'textareafield',
    
    canGrowWidth: false,
    
    naturalSizingProp: 'cols',
    
    beginLayout: function(ownerContext){
        this.callParent(arguments);
        ownerContext.target.inputEl.setStyle('height', '');
    },

    measureContentHeight: function (ownerContext) {
        var me = this,
            owner = me.owner,
            height = me.callParent(arguments),
            inputContext, inputEl, value, max, curWidth, calcHeight;

        if (owner.grow && !ownerContext.state.growHandled) {
            inputContext = ownerContext.inputContext;
            inputEl = owner.inputEl;
            curWidth = inputEl.getWidth(true); 

            
            value = Ext.util.Format.htmlEncode(inputEl.dom.value) || '&#160;';
            value += owner.growAppend;
            
            
            value = value.replace(/\n/g, '<br/>');

            
            calcHeight = Ext.util.TextMetrics.measure(inputEl, value, curWidth).height +
                         inputContext.getBorderInfo().height + inputContext.getPaddingInfo().height;

            
            calcHeight = Ext.Number.constrain(calcHeight, owner.growMin, owner.growMax);
            inputContext.setHeight(calcHeight);
            ownerContext.state.growHandled = true;
            
            
            inputContext.domBlock(me, 'height');
            height = NaN;
        }
        return height;
    }
});


Ext.define('Ext.form.field.TextArea', {
    extend: Ext.form.field.Text ,
    alias: ['widget.textareafield', 'widget.textarea'],
    alternateClassName: 'Ext.form.TextArea',
               
                         
                                              
                              
      

    
    
    
    
    
    
    
    
    
    
    fieldSubTpl: [
        '<textarea id="{id}" {inputAttrTpl}',
            '<tpl if="name"> name="{name}"</tpl>',
            '<tpl if="rows"> rows="{rows}" </tpl>',
            '<tpl if="cols"> cols="{cols}" </tpl>',
            '<tpl if="placeholder"> placeholder="{placeholder}"</tpl>',
            '<tpl if="size"> size="{size}"</tpl>',
            '<tpl if="maxLength !== undefined"> maxlength="{maxLength}"</tpl>',
            '<tpl if="readOnly"> readonly="readonly"</tpl>',
            '<tpl if="disabled"> disabled="disabled"</tpl>',
            '<tpl if="tabIdx"> tabIndex="{tabIdx}"</tpl>',
            ' class="{fieldCls} {typeCls} {inputCls}" ',
            '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
            ' autocomplete="off">\n',
            '<tpl if="value">{[Ext.util.Format.htmlEncode(values.value)]}</tpl>',
        '</textarea>',
        {
            disableFormats: true
        }
    ],

    
    growMin: 60,

    
    growMax: 1000,

    
    growAppend: '\n-',

    
    cols: 20,

    
    rows: 4,

    
    enterIsSpecial: false,

    
    preventScrollbars: false,

    
    componentLayout: 'textareafield',
    
    setGrowSizePolicy: Ext.emptyFn,
    
    returnRe: /\r/g,

    inputCls: Ext.baseCSSPrefix + 'form-textarea',

    
    getSubTplData: function() {
        var me = this,
            fieldStyle = me.getFieldStyle(),
            ret = me.callParent();

        if (me.grow) {
            if (me.preventScrollbars) {
                ret.fieldStyle = (fieldStyle||'') + ';overflow:hidden;height:' + me.growMin + 'px';
            }
        }

        Ext.applyIf(ret, {
            cols: me.cols,
            rows: me.rows
        });

        return ret;
    },

    afterRender: function () {
        var me = this;

        me.callParent(arguments);

        me.needsMaxCheck = me.enforceMaxLength && me.maxLength !== Number.MAX_VALUE && !Ext.supports.TextAreaMaxLength;
        if (me.needsMaxCheck) {
            me.inputEl.on('paste', me.onPaste, me);
        }
    },
    
    
    
    
    
    
    
    transformRawValue: function(value){
        return this.stripReturns(value);
    },
    
    transformOriginalValue: function(value){
        return this.stripReturns(value); 
    },
    
    getValue: function(){
        return this.stripReturns(this.callParent());    
    },
    
    valueToRaw: function(value){
        value = this.stripReturns(value);
        return this.callParent([value]);
    },
    
    stripReturns: function(value){
        if (value && typeof value === 'string') {
            value = value.replace(this.returnRe, '');
        }
        return value;
    },

    onPaste: function(e){
        var me = this;
        if (!me.pasteTask) {
            me.pasteTask = new Ext.util.DelayedTask(me.pasteCheck, me);
        }
        
        me.pasteTask.delay(1);
    },
    
    pasteCheck: function(){
        var me = this,
            value = me.getValue(),
            max = me.maxLength;
            
        if (value.length > max) {
            value = value.substr(0, max);
            me.setValue(value);
        }
    },

    
    fireKey: function(e) {
        var me = this,
            key = e.getKey(),
            value;
            
        if (e.isSpecialKey() && (me.enterIsSpecial || (key !== e.ENTER || e.hasModifier()))) {
            me.fireEvent('specialkey', me, e);
        }
        
        if (me.needsMaxCheck && key !== e.BACKSPACE && key !== e.DELETE && !e.isNavKeyPress() && !me.isCutCopyPasteSelectAll(e, key)) {
            value = me.getValue();
            if (value.length >= me.maxLength) {
                e.stopEvent();
            }
        }
    },
    
    isCutCopyPasteSelectAll: function(e, key) {
        if (e.ctrlKey) {
            return key === e.A || key === e.C || key === e.V || key === e.X;
        }
        return false;
    },

    
    autoSize: function() {
        var me = this,
            height;

        if (me.grow && me.rendered) {
            me.updateLayout();
            height = me.inputEl.getHeight();
            if (height !== me.lastInputHeight) {
                
                me.fireEvent('autosize', me, height);
                me.lastInputHeight = height;
            }
        }
    },

    
    initAria: function() {
        this.callParent(arguments);
        this.getActionEl().dom.setAttribute('aria-multiline', true);
    },
    
    beforeDestroy: function(){
        var task = this.pasteTask;
        if (task) {
            task.cancel();
            this.pasteTask = null;
        }    
        this.callParent();
    }
});


Ext.define('Ext.form.field.Display', {
    extend: Ext.form.field.Base ,
    alias: 'widget.displayfield',
                                                   
    alternateClassName: ['Ext.form.DisplayField', 'Ext.form.Display'],
    fieldSubTpl: [
        '<div id="{id}" role="input" ',
        '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>', 
        ' class="{fieldCls}">{value}</div>',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    
    readOnly: true,

    
    fieldCls: Ext.baseCSSPrefix + 'form-display-field',

    fieldBodyCls: Ext.baseCSSPrefix + 'form-display-field-body',

    
    htmlEncode: false,
    
    
    
    

    noWrap: false,
    
    
    validateOnChange: false,

    initEvents: Ext.emptyFn,

    submitValue: false,
    
    isDirty: function(){
        return false;
    },

    isValid: function() {
        return true;
    },

    validate: function() {
        return true;
    },

    getRawValue: function() {
        return this.rawValue;
    },

    setRawValue: function(value) {
        var me = this;
            
        value = Ext.value(value, '');
        me.rawValue = value;
        if (me.rendered) {
            me.inputEl.dom.innerHTML = me.getDisplayValue();
            me.updateLayout();
        }
        return value;
    },

    
    getDisplayValue: function() {
        var me = this,
            value = this.getRawValue(),
            display;
        if (me.renderer) {
             display = me.renderer.call(me.scope || me, value, me);
        } else {
             display = me.htmlEncode ? Ext.util.Format.htmlEncode(value) : value;
        }
        return display;
    },
        
    getSubTplData: function() {
        var ret = this.callParent(arguments);

        ret.value = this.getDisplayValue();

        return ret;
    }

    
    
    
    
});


Ext.define('Ext.layout.container.Anchor', {

    

    alias: 'layout.anchor',
    extend:  Ext.layout.container.Auto ,
    alternateClassName: 'Ext.layout.AnchorLayout',

    

    type: 'anchor',

    

    
    defaultAnchor: '100%',

    parseAnchorRE: /^(r|right|b|bottom)$/i,

    manageOverflow: true,

    beginLayoutCycle: function (ownerContext) {
        var me = this,
            dimensions = 0,
            anchorSpec, childContext, childItems, i, length, target;

        me.callParent(arguments);

        childItems = ownerContext.childItems; 
        length = childItems.length;

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            anchorSpec = childContext.target.anchorSpec;

            if (anchorSpec) {
                if (childContext.widthModel.calculated && anchorSpec.right) {
                    dimensions |= 1;
                }
                if (childContext.heightModel.calculated && anchorSpec.bottom) {
                    dimensions |= 2;
                }

                if (dimensions == 3) { 
                    break;
                }
            }
        }

        ownerContext.anchorDimensions = dimensions;

    },

    calculateItems: function (ownerContext, containerSize) {
        var me = this,
            childItems = ownerContext.childItems,
            length = childItems.length,
            gotHeight = containerSize.gotHeight,
            gotWidth = containerSize.gotWidth,
            ownerHeight = containerSize.height,
            ownerWidth = containerSize.width,
            knownDimensions = (gotWidth ? 1 : 0) | (gotHeight ? 2 : 0),
            anchorDimensions = ownerContext.anchorDimensions,
            anchorSpec, childContext, childMargins, height, i, width;

        if (!anchorDimensions) {
            return true;
        }

        for (i = 0; i < length; i++) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();
            anchorSpec = childContext.target.anchorSpec;

            
            
            
            
            if (gotWidth && childContext.widthModel.calculated) {
                width = anchorSpec.right(ownerWidth) - childMargins.width;
                width = me.adjustWidthAnchor(width, childContext);

                childContext.setWidth(width);
            }

            
            if (gotHeight && childContext.heightModel.calculated) {
                height = anchorSpec.bottom(ownerHeight) - childMargins.height;
                height = me.adjustHeightAnchor(height, childContext);

                childContext.setHeight(height);
            }
        }

        
        return (knownDimensions & anchorDimensions) === anchorDimensions;
    },


    
    anchorFactory: {
        offset: function (delta) {
            return function(v) {
                return v + delta;
            };
        },
        ratio: function (ratio) {
            return function(v) {
                return Math.floor(v * ratio);
            };
        },
        standard: function (diff) {
            return function(v) {
                return v - diff;
            };
        }
    },

    parseAnchor: function(a, start, cstart) {
        if (a && a != 'none') {
            var factory = this.anchorFactory,
                delta;

            if (this.parseAnchorRE.test(a)) {
                return factory.standard(cstart - start);
            }    
            if (a.indexOf('%') != -1) {
                return factory.ratio(parseFloat(a.replace('%', '')) * 0.01);
            }    
            delta = parseInt(a, 10);
            if (!isNaN(delta)) {
                return factory.offset(delta);
            }
        }
        return null;
    },

    
    adjustWidthAnchor: function(value, childContext) {
        return value;
    },

    
    adjustHeightAnchor: function(value, childContext) {
        return value;
    },

    configureItem: function(item) {
        var me = this,
            owner = me.owner,
            anchor= item.anchor,
            anchorsArray,
            anchorWidth,
            anchorHeight;

        me.callParent(arguments);

        if (!item.anchor && item.items && !Ext.isNumber(item.width) && !(Ext.isIE6 && Ext.isStrict)) {
            item.anchor = anchor = me.defaultAnchor;
        }

         
        if (owner.anchorSize) {
            if (typeof owner.anchorSize == 'number') {
                anchorWidth = owner.anchorSize;
            } else {
                anchorWidth = owner.anchorSize.width;
                anchorHeight = owner.anchorSize.height;
            }
        } else {
            anchorWidth = owner.initialConfig.width;
            anchorHeight = owner.initialConfig.height;
        }

        if (anchor) {
            
            anchorsArray = anchor.split(' ');
            item.anchorSpec = {
                right: me.parseAnchor(anchorsArray[0], item.initialConfig.width, anchorWidth),
                bottom: me.parseAnchor(anchorsArray[1], item.initialConfig.height, anchorHeight)
            };
        }
    },

    sizePolicy: {
        $: {
            readsWidth: 1,
            readsHeight: 1,
            setsWidth: 0,
            setsHeight: 0
        },
        b: {
            readsWidth: 1,
            readsHeight: 0,
            setsWidth: 0,
            setsHeight: 1
        },
        r: {
            $: {
                readsWidth: 0,
                readsHeight: 1,
                setsWidth: 1,
                setsHeight: 0
            },
            b: {
                readsWidth: 0,
                readsHeight: 0,
                setsWidth: 1,
                setsHeight: 1
            }
        }
    },

    getItemSizePolicy: function (item) {
        var anchorSpec = item.anchorSpec,
            key = '$',
            policy = this.sizePolicy,
            sizeModel;

        if (anchorSpec) {
            sizeModel = this.owner.getSizeModel();
            if (anchorSpec.right && !sizeModel.width.shrinkWrap) {
                policy = policy.r;
            }
            if (anchorSpec.bottom && !sizeModel.height.shrinkWrap) {
                key = 'b';
            }
        }

        return policy[key];
    }
});




Ext.define('Ext.window.MessageBox', {
    extend:  Ext.window.Window ,

               
                              
                              
                                  
                                 
                            
                                      
                                    
                         
      

    alias: 'widget.messagebox',

    
    OK : 1,
    
    YES : 2,
    
    NO : 4,
    
    CANCEL : 8,
    
    OKCANCEL : 9,
    
    YESNO : 6,
    
    YESNOCANCEL : 14,
    
    INFO : Ext.baseCSSPrefix + 'message-box-info',
    
    WARNING : Ext.baseCSSPrefix + 'message-box-warning',
    
    QUESTION : Ext.baseCSSPrefix + 'message-box-question',
    
    ERROR : Ext.baseCSSPrefix + 'message-box-error',

    
    hideMode: 'offsets',
    closeAction: 'hide',
    resizable: false,
    title: '&#160;',

    defaultMinWidth: 250,
    defaultMaxWidth: 600,
    defaultMinHeight: 110,
    defaultMaxHeight: 500,
    
    
    
    minWidth: null,
    maxWidth: null,
    minHeight: null,
    maxHeight: null,
    constrain: true,

    cls: [Ext.baseCSSPrefix + 'message-box', Ext.baseCSSPrefix + 'hide-offsets'],

    layout: {
        type: 'vbox',
        align: 'stretch'
    },

    
    shrinkWrapDock: true,

    
    defaultTextHeight : 75,
    
    minProgressWidth : 250,
    
    minPromptWidth: 250,
    
    
    buttonText: {
        ok: 'OK',
        yes: 'Yes',
        no: 'No',
        cancel: 'Cancel'
    },
    

    buttonIds: [
        'ok', 'yes', 'no', 'cancel'
    ],

    
    titleText: {
        confirm: 'Confirm',
        prompt: 'Prompt',
        wait: 'Loading...',
        alert: 'Attention'
    },
    

    iconHeight: 35,
    iconWidth: 50,

    makeButton: function(btnIdx) {
        var btnId = this.buttonIds[btnIdx];
        return new Ext.button.Button({
            handler: this.btnCallback,
            itemId: btnId,
            scope: this,
            text: this.buttonText[btnId],
            minWidth: 75
        });
    },

    btnCallback: function(btn) {
        var me = this,
            value,
            field;

        if (me.cfg.prompt || me.cfg.multiline) {
            if (me.cfg.multiline) {
                field = me.textArea;
            } else {
                field = me.textField;
            }
            value = field.getValue();
            field.reset();
        }

        
        me.hide();
        me.userCallback(btn.itemId, value, me.cfg);
    },

    hide: function() {
        var me = this,
            cls = me.cfg.cls;

        me.dd.endDrag();
        me.progressBar.reset();
        if (cls) {
            me.removeCls(cls);
        }
        me.callParent(arguments);
    },

    constructor: function(cfg) {
        var me = this;

        me.callParent(arguments);

        
        
        me.minWidth = me.defaultMinWidth = (me.minWidth || me.defaultMinWidth);
        me.maxWidth = me.defaultMaxWidth = (me.maxWidth || me.defaultMaxWidth);
        me.minHeight = me.defaultMinHeight = (me.minHeight || me.defaultMinHeight);
        me.maxHeight = me.defaultMaxHeight = (me.maxHeight || me.defaultMaxHeight);
    },

    initComponent: function(cfg) {
        var me = this,
            baseId = me.id,
            i, button;

        me.title = '&#160;';

        me.topContainer = new Ext.container.Container({
            layout: 'hbox',
            padding: 10,
            style: {
                overflow: 'hidden'
            },
            items: [
                me.iconComponent = new Ext.Component({
                    width: me.iconWidth,
                    height: me.iconHeight
                }),
                me.promptContainer = new Ext.container.Container({
                    flex: 1,
                    layout: 'anchor',
                    items: [
                        me.msg = new Ext.form.field.Display({
                            id: baseId + '-displayfield',
                            cls: me.baseCls + '-text'
                        }),
                        me.textField = new Ext.form.field.Text({
                            id: baseId + '-textfield',
                            anchor: '100%',
                            enableKeyEvents: true,
                            listeners: {
                                keydown: me.onPromptKey,
                                scope: me
                            }
                        }),
                        me.textArea = new Ext.form.field.TextArea({
                            id: baseId + '-textarea',
                            anchor: '100%',
                            height: 75
                        })
                    ]
                })
            ]
        });
        me.progressBar = new Ext.ProgressBar({
            id: baseId + '-progressbar',
            margins: '0 10 10 10'
        });

        me.items = [me.topContainer, me.progressBar];

        
        me.msgButtons = [];
        for (i = 0; i < 4; i++) {
            button = me.makeButton(i);
            me.msgButtons[button.itemId] = button;
            me.msgButtons.push(button);
        }
        me.bottomTb = new Ext.toolbar.Toolbar({
            id: baseId + '-toolbar',
            ui: 'footer',
            dock: 'bottom',
            layout: {
                pack: 'center'
            },
            items: [
                me.msgButtons[0],
                me.msgButtons[1],
                me.msgButtons[2],
                me.msgButtons[3]
            ]
        });
        me.dockedItems = [me.bottomTb];
        me.on('close', me.onClose, me);
        me.callParent();
    },

    onClose: function(){
        var btn = this.header.child('[type=close]');
        
        btn.itemId = 'cancel';
        this.btnCallback(btn);
        delete btn.itemId;
    },

    onPromptKey: function(textField, e) {
        var me = this;

        if (e.keyCode === e.RETURN || e.keyCode === 10) {
            if (me.msgButtons.ok.isVisible()) {
                me.msgButtons.ok.handler.call(me, me.msgButtons.ok);
            } else if (me.msgButtons.yes.isVisible()) {
                me.msgButtons.yes.handler.call(me, me.msgButtons.yes);
            }
        }
    },

    reconfigure: function(cfg) {
        var me = this,
            buttons = 0,
            hideToolbar = true,
            oldButtonText = me.buttonText,
            resizer = me.resizer,
            resizeTracker, width, height, i, textArea, textField,
            msg, progressBar, msgButtons;

        
        me.updateButtonText();

        cfg = cfg || {};
        me.cfg = cfg;
        if (cfg.width) {
            width = cfg.width;
        }

        if (cfg.height) {
            height = cfg.height;
        }

        me.minWidth = cfg.minWidth || me.defaultMinWidth;
        me.maxWidth = cfg.maxWidth || me.defaultMaxWidth;
        me.minHeight = cfg.minHeight || me.defaultMinHeight;
        me.maxHeight = cfg.maxHeight || me.defaultMaxHeight;

        if (resizer) {
            resizeTracker = resizer.resizeTracker;
            resizer.minWidth = resizeTracker.minWidth = me.minWidth;
            resizer.maxWidth = resizeTracker.maxWidth = me.maxWidth;
            resizer.minHeight = resizeTracker.minHeight = me.minHeight;
            resizer.maxHeight = resizeTracker.maxHeight = me.maxHeight;
        }

        
        delete me.defaultFocus;
        if (cfg.defaultFocus) {
            me.defaultFocus = cfg.defaultFocus;
        }

        
        me.animateTarget = cfg.animateTarget || undefined;

        
        me.modal = cfg.modal !== false;

        
        me.setTitle(cfg.title || '');
        me.setIconCls(cfg.iconCls || '');

        
        if (Ext.isObject(cfg.buttons)) {
            me.buttonText = cfg.buttons;
            buttons = 0;
        } else {
            me.buttonText = cfg.buttonText || me.buttonText;
            buttons = Ext.isNumber(cfg.buttons) ? cfg.buttons : 0;
        }

        
        
        buttons = buttons | me.updateButtonText();

        
        me.buttonText = oldButtonText;

        
        
        Ext.suspendLayouts();
        delete me.width;
        delete me.height;
        if (width || height) {
            if (width) {
                me.setWidth(width);
            }

            if (height) {
                me.setHeight(height);
            }
        }
        me.hidden = false;
        if (!me.rendered) {
            me.render(Ext.getBody());
        }

        
        me.closable = cfg.closable !== false && !cfg.wait;
        me.header.child('[type=close]').setVisible(me.closable);

        
        if (!cfg.title && !me.closable && !cfg.iconCls) {
            me.header.hide();
        } else {
            me.header.show();
        }

        
        me.liveDrag = !cfg.proxyDrag;

        
        me.userCallback = Ext.Function.bind(cfg.callback ||cfg.fn || Ext.emptyFn, cfg.scope || Ext.global);

        
        me.setIcon(cfg.icon, cfg.iconWidth, cfg.iconHeight);

        
        msg = me.msg;
        if (cfg.msg) {
            msg.setValue(cfg.msg);
            msg.show();
        } else {
            msg.hide();
        }

        
        textArea = me.textArea;
        textField = me.textField;
        if (cfg.prompt || cfg.multiline) {
            me.multiline = cfg.multiline;
            if (cfg.multiline) {
                textArea.setValue(cfg.value);
                textArea.setHeight(cfg.defaultTextHeight || me.defaultTextHeight);
                textArea.show();
                textField.hide();
                me.defaultFocus = textArea;
            } else {
                textField.setValue(cfg.value);
                textArea.hide();
                textField.show();
                me.defaultFocus = textField;
            }
        } else {
            textArea.hide();
            textField.hide();
        }

        
        progressBar = me.progressBar;
        if (cfg.progress || cfg.wait) {
            progressBar.show();
            me.updateProgress(0, cfg.progressText);
            if(cfg.wait === true){
                progressBar.wait(cfg.waitConfig);
            }
        } else {
            progressBar.hide();
        }

        
        msgButtons = me.msgButtons;
        for (i = 0; i < 4; i++) {
            if (buttons & Math.pow(2, i)) {

                
                if (!me.defaultFocus) {
                    me.defaultFocus = msgButtons[i];
                }
                msgButtons[i].show();
                hideToolbar = false;
            } else {
                msgButtons[i].hide();
            }
        }

        
        if (hideToolbar) {
            me.bottomTb.hide();
        } else {
            me.bottomTb.show();
        }
        Ext.resumeLayouts(true);
    },

    
    updateButtonText: function() {
        var me = this,
            buttonText = me.buttonText,
            buttons = 0,
            btnId,
            btn;

        for (btnId in buttonText) {
            if (buttonText.hasOwnProperty(btnId)) {
                btn = me.msgButtons[btnId];
                if (btn) {
                    if (me.cfg && me.cfg.buttonText) {
                        buttons = buttons | Math.pow(2, Ext.Array.indexOf(me.buttonIds, btnId));
                    }
                    if (btn.text != buttonText[btnId]) {
                        btn.setText(buttonText[btnId]);
                    }
                }
            }
        }
        return buttons;
    },

    
    show: function(cfg) {
        var me = this,
            visibleFocusables;

        
        if (Ext.AbstractComponent.layoutSuspendCount) {
            Ext.on({
                resumelayouts: function() {
                    me.show(cfg);
                },
                single: true
            });
            return me;
        }

        me.reconfigure(cfg);
        if (cfg.cls) {
            me.addCls(cfg.cls);
        }

        
        
        visibleFocusables = me.query('textfield:not([hidden]),textarea:not([hidden]),button:not([hidden])');
        me.preventFocusOnActivate = !visibleFocusables.length;

        
        
        me.hidden = true;
        me.callParent();
        return me;
    },

    onShow: function() {
        this.callParent(arguments);
        this.center();
    },

    updateText: function(text) {
        this.msg.setValue(text);
    },

    
    setIcon : function(icon, width, height) {
        var me = this,
            iconCmp = me.iconComponent,
            cls = me.messageIconCls;

        if (cls) {
            iconCmp.removeCls(cls);
        }

        if (icon) {
            iconCmp.show();
            iconCmp.setSize(width || me.iconWidth, height || me.iconHeight);
            iconCmp.addCls(Ext.baseCSSPrefix + 'dlg-icon');
            iconCmp.addCls(me.messageIconCls = icon);
        } else {
            iconCmp.removeCls(Ext.baseCSSPrefix + 'dlg-icon');
            iconCmp.hide();
        }
        return me;
    },

    
    updateProgress : function(value, progressText, msg){
        this.progressBar.updateProgress(value, progressText);
        if (msg){
            this.updateText(msg);
        }
        return this;
    },

    onEsc: function() {
        if (this.closable !== false) {
            this.callParent(arguments);
        }
    },

    
    confirm: function(cfg, msg, fn, scope) {
        if (Ext.isString(cfg)) {
            cfg = {
                title: cfg,
                icon: this.QUESTION,
                msg: msg,
                buttons: this.YESNO,
                callback: fn,
                scope: scope
            };
        }
        return this.show(cfg);
    },

    
    prompt : function(cfg, msg, fn, scope, multiline, value){
        if (Ext.isString(cfg)) {
            cfg = {
                prompt: true,
                title: cfg,
                minWidth: this.minPromptWidth,
                msg: msg,
                buttons: this.OKCANCEL,
                callback: fn,
                scope: scope,
                multiline: multiline,
                value: value
            };
        }
        return this.show(cfg);
    },

    
    wait : function(cfg, title, config){
        if (Ext.isString(cfg)) {
            cfg = {
                title : title,
                msg : cfg,
                closable: false,
                wait: true,
                modal: true,
                minWidth: this.minProgressWidth,
                waitConfig: config
            };
        }
        return this.show(cfg);
    },

    
    alert: function(cfg, msg, fn, scope) {
        if (Ext.isString(cfg)) {
            cfg = {
                title : cfg,
                msg : msg,
                buttons: this.OK,
                fn: fn,
                scope : scope,
                minWidth: this.minWidth
            };
        }
        return this.show(cfg);
    },

    
    progress : function(cfg, msg, progressText){
        if (Ext.isString(cfg)) {
            cfg = {
                title: cfg,
                msg: msg,
                progress: true,
                progressText: progressText
            };
        }
        return this.show(cfg);
    }
}, function() {
    
    Ext.MessageBox = Ext.Msg = new this();
});


Ext.define('Ext.form.Basic', {
    extend:  Ext.util.Observable ,
    alternateClassName: 'Ext.form.BasicForm',
                                                                                            
                                                                                   

    
    constructor: function(owner, config) {
        var me = this,
            reader;

        
        me.owner = owner;
        
        me.checkValidityTask = new Ext.util.DelayedTask(me.checkValidity, me);
        me.checkDirtyTask = new Ext.util.DelayedTask(me.checkDirty, me);
        
        
        
        
        me.monitor = new Ext.container.Monitor({
            selector: '[isFormField]',
            scope: me,
            addHandler: me.onFieldAdd,
            removeHandler: me.onFieldRemove
        });
        me.monitor.bind(owner);

        Ext.apply(me, config);

        
        if (Ext.isString(me.paramOrder)) {
            me.paramOrder = me.paramOrder.split(/[\s,|]/);
        }
        
        reader = me.reader;
        if (reader && !reader.isReader) {
            if (typeof reader === 'string') {
                reader = {
                    type: reader
                };
            }
            me.reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        
        reader = me.errorReader;
        if (reader && !reader.isReader) {
            if (typeof reader === 'string') {
                reader = {
                    type: reader
                };
            }
            me.errorReader = Ext.createByAlias('reader.' + reader.type, reader);
        }

        me.addEvents(
            
            'beforeaction',
            
            'actionfailed',
            
            'actioncomplete',
            
            'validitychange',
            
            'dirtychange'
        );
        me.callParent();
    },

    
    initialize : function() {
        this.initialized = true;
        this.onValidityChange(!this.hasInvalidField());
    },


    

    

    

    

    

    
    timeout: 30,

    

    

    
    paramsAsHash: false,

    
    
    waitTitle: 'Please Wait...',
    

    
    trackResetOnLoad: false,

    

    

    


    
    wasDirty: false,


    
    destroy: function() {
        var me = this,
            mon = me.monitor;
        
        if (mon) {
            mon.unbind();
            me.monitor = null;
        }
        me.clearListeners();
        me.checkValidityTask.cancel();
        me.checkDirtyTask.cancel();
    },
    
    onFieldAdd: function(field){
        var me = this;
        
        me.mon(field, 'validitychange', me.checkValidityDelay, me);
        me.mon(field, 'dirtychange', me.checkDirtyDelay, me);
        if (me.initialized) {
            me.checkValidityDelay();
        }
    },
    
    onFieldRemove: function(field){
        var me = this;
        
        me.mun(field, 'validitychange', me.checkValidityDelay, me);
        me.mun(field, 'dirtychange', me.checkDirtyDelay, me);
        if (me.initialized) {
            me.checkValidityDelay();
        }
    },
    
    
    getFields: function() {
        return this.monitor.getItems();
    },

    
    getBoundItems: function() {
        var boundItems = this._boundItems;
        
        if (!boundItems || boundItems.getCount() === 0) {
            boundItems = this._boundItems = new Ext.util.MixedCollection();
            boundItems.addAll(this.owner.query('[formBind]'));
        }
        
        return boundItems;
    },

    
    hasInvalidField: function() {
        return !!this.getFields().findBy(function(field) {
            var preventMark = field.preventMark,
                isValid;
            field.preventMark = true;
            isValid = field.isValid();
            field.preventMark = preventMark;
            return !isValid;
        });
    },

    
    isValid: function() {
        var me = this,
            invalid;
        Ext.suspendLayouts();
        invalid = me.getFields().filterBy(function(field) {
            return !field.validate();
        });
        Ext.resumeLayouts(true);
        return invalid.length < 1;
    },

    
    checkValidity: function() {
        var me = this,
            valid = !me.hasInvalidField();
        if (valid !== me.wasValid) {
            me.onValidityChange(valid);
            me.fireEvent('validitychange', me, valid);
            me.wasValid = valid;
        }
    },
    
    checkValidityDelay: function(){
        this.checkValidityTask.delay(10);
    },

    
    onValidityChange: function(valid) {
        var boundItems = this.getBoundItems(),
            items, i, iLen, cmp;

        if (boundItems) {
            items = boundItems.items;
            iLen  = items.length;

            for (i = 0; i < iLen; i++) {
                cmp = items[i];

                if (cmp.disabled === valid) {
                    cmp.setDisabled(!valid);
                }
            }
        }
    },

    
    isDirty: function() {
        return !!this.getFields().findBy(function(f) {
            return f.isDirty();
        });
    },
    
    checkDirtyDelay: function(){
        this.checkDirtyTask.delay(10);
    },

    
    checkDirty: function() {
        var dirty = this.isDirty();
        if (dirty !== this.wasDirty) {
            this.fireEvent('dirtychange', this, dirty);
            this.wasDirty = dirty;
        }
    },

    
    hasUpload: function() {
        return !!this.getFields().findBy(function(f) {
            return f.isFileUpload();
        });
    },

    
    doAction: function(action, options) {
        if (Ext.isString(action)) {
            action = Ext.ClassManager.instantiateByAlias('formaction.' + action, Ext.apply({}, options, {form: this}));
        }
        if (this.fireEvent('beforeaction', this, action) !== false) {
            this.beforeAction(action);
            Ext.defer(action.run, 100, action);
        }
        return this;
    },

    
    submit: function(options) {
        options = options || {};
        var me = this,
            action;
            
        if (options.standardSubmit || me.standardSubmit) {
            action = 'standardsubmit';
        } else {
            action = me.api ? 'directsubmit' : 'submit';
        }
            
        return me.doAction(action, options);
    },

    
    load: function(options) {
        return this.doAction(this.api ? 'directload' : 'load', options);
    },

    
    updateRecord: function(record) {
        record = record || this._record;
        if (!record) {
            return this;
        }
        
        var fields = record.fields.items,
            values = this.getFieldValues(),
            obj = {},
            i = 0,
            len = fields.length,
            name;

        for (; i < len; ++i) {
            name  = fields[i].name;

            if (values.hasOwnProperty(name)) {
                obj[name] = values[name];
            }
        }

        record.beginEdit();
        record.set(obj);
        record.endEdit();

        return this;
    },

    
    loadRecord: function(record) {
        this._record = record;
        return this.setValues(record.getData());
    },

    
    getRecord: function() {
        return this._record;
    },

    
    beforeAction: function(action) {
        var me = this,
            waitMsg = action.waitMsg,
            maskCls = Ext.baseCSSPrefix + 'mask-loading',
            fields  = me.getFields().items,
            f,
            fLen    = fields.length,
            field, waitMsgTarget;

        
        for (f = 0; f < fLen; f++) {
            field = fields[f];

            if (field.isFormField && field.syncValue) {
                field.syncValue();
            }
        }

        if (waitMsg) {
            waitMsgTarget = me.waitMsgTarget;
            if (waitMsgTarget === true) {
                me.owner.el.mask(waitMsg, maskCls);
            } else if (waitMsgTarget) {
                waitMsgTarget = me.waitMsgTarget = Ext.get(waitMsgTarget);
                waitMsgTarget.mask(waitMsg, maskCls);
            } else {
                me.floatingAncestor = me.owner.up('[floating]');

                
                
                
                
                
                
                
                if (me.floatingAncestor) {
                    me.savePreventFocusOnActivate = me.floatingAncestor.preventFocusOnActivate;
                    me.floatingAncestor.preventFocusOnActivate = true;
                }
                Ext.MessageBox.wait(waitMsg, action.waitTitle || me.waitTitle);
            }
        }
    },

    
    afterAction: function(action, success) {
        var me = this;
        if (action.waitMsg) {
            var messageBox = Ext.MessageBox,
                waitMsgTarget = me.waitMsgTarget;
            if (waitMsgTarget === true) {
                me.owner.el.unmask();
            } else if (waitMsgTarget) {
                waitMsgTarget.unmask();
            } else {
                messageBox.hide();
            }
        }
        
        if (me.floatingAncestor) {
            me.floatingAncestor.preventFocusOnActivate = me.savePreventFocusOnActivate;
        }
        if (success) {
            if (action.reset) {
                me.reset();
            }
            Ext.callback(action.success, action.scope || action, [me, action]);
            me.fireEvent('actioncomplete', me, action);
        } else {
            Ext.callback(action.failure, action.scope || action, [me, action]);
            me.fireEvent('actionfailed', me, action);
        }
    },


    
    findField: function(id) {
        return this.getFields().findBy(function(f) {
            return f.id === id || f.getName() === id;
        });
    },


    
    markInvalid: function(errors) {
        var me = this,
            e, eLen, error, value,
            key;

        function mark(fieldId, msg) {
            var field = me.findField(fieldId);
            if (field) {
                field.markInvalid(msg);
            }
        }

        if (Ext.isArray(errors)) {
            eLen = errors.length;

            for (e = 0; e < eLen; e++) {
                error = errors[e];
                mark(error.id, error.msg);
            }
        } else if (errors instanceof Ext.data.Errors) {
            eLen  = errors.items.length;
            for (e = 0; e < eLen; e++) {
                error = errors.items[e];

                mark(error.field, error.message);
            }
        } else {
            for (key in errors) {
                if (errors.hasOwnProperty(key)) {
                    value = errors[key];
                    mark(key, value, errors);
                }
            }
        }
        return this;
    },

    
    setValues: function(values) {
        var me = this,
            v, vLen, val, field;

        function setVal(fieldId, val) {
            var field = me.findField(fieldId);
            if (field) {
                field.setValue(val);
                if (me.trackResetOnLoad) {
                    field.resetOriginalValue();
                }
            }
        }

        
        
        Ext.suspendLayouts();
        if (Ext.isArray(values)) {
            
            vLen = values.length;

            for (v = 0; v < vLen; v++) {
                val = values[v];

                setVal(val.id, val.value);
            }
        } else {
            
            Ext.iterate(values, setVal);
        }
        Ext.resumeLayouts(true);
        return this;
    },

    
    getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
        var values  = {},
            fields  = this.getFields().items,
            f,
            fLen    = fields.length,
            isArray = Ext.isArray,
            field, data, val, bucket, name;

        for (f = 0; f < fLen; f++) {
            field = fields[f];

            if (!dirtyOnly || field.isDirty()) {
                data = field[useDataValues ? 'getModelData' : 'getSubmitData'](includeEmptyText);

                if (Ext.isObject(data)) {
                    for (name in data) {
                        if (data.hasOwnProperty(name)) {
                            val = data[name];

                            if (includeEmptyText && val === '') {
                                val = field.emptyText || '';
                            }

                            if (values.hasOwnProperty(name)) {
                                bucket = values[name];

                                if (!isArray(bucket)) {
                                    bucket = values[name] = [bucket];
                                }

                                if (isArray(val)) {
                                    values[name] = bucket.concat(val);
                                } else {
                                    bucket.push(val);
                                }
                            } else {
                                values[name] = val;
                            }
                        }
                    }
                }
            }
        }

        if (asString) {
            values = Ext.Object.toQueryString(values);
        }
        return values;
    },

    
    getFieldValues: function(dirtyOnly) {
        return this.getValues(false, dirtyOnly, false, true);
    },

    
    clearInvalid: function() {
        Ext.suspendLayouts();

        var me     = this,
            fields = me.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            fields[f].clearInvalid();
        }

        Ext.resumeLayouts(true);
        return me;
    },

    
    reset: function(resetRecord) {
        Ext.suspendLayouts();

        var me     = this,
            fields = me.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            fields[f].reset();
        }

        Ext.resumeLayouts(true);
        
        if (resetRecord === true) {
            delete me._record;
        }
        return me;
    },

    
    applyToFields: function(obj) {
        var fields = this.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            Ext.apply(fields[f], obj);
        }

        return this;
    },

    
    applyIfToFields: function(obj) {
        var fields = this.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            Ext.applyIf(fields[f], obj);
        }

        return this;
    }
});


Ext.define('Ext.form.FieldAncestor', {
    
               
                               
      

    


    xhooks: {
        initHierarchyState: function(hierarchyState) {
            if (this.fieldDefaults) {
                if (hierarchyState.fieldDefaults) {
                    hierarchyState.fieldDefaults = Ext.apply(Ext.Object.chain(hierarchyState.fieldDefaults), this.fieldDefaults);
                } else {
                    hierarchyState.fieldDefaults = this.fieldDefaults;
                }
            }
        }
    },

    
    initFieldAncestor: function() {
        var me = this;

        me.addEvents(
            
            'fieldvaliditychange',

            
            'fielderrorchange'
        );

        
        
        
        me.monitor = new Ext.container.Monitor({
            scope: me,
            selector: '[isFormField]',
            addHandler: me.onChildFieldAdd,
            removeHandler: me.onChildFieldRemove
        });
        me.initFieldDefaults();
    },
    
    initMonitor: function() {
        this.monitor.bind(this);    
    },
    
    onChildFieldAdd: function(field) {
        var me = this;
        me.mon(field, 'errorchange', me.handleFieldErrorChange, me);
        me.mon(field, 'validitychange', me.handleFieldValidityChange, me);
    },
    
    onChildFieldRemove: function(field) {
        var me = this;
        me.mun(field, 'errorchange', me.handleFieldErrorChange, me);
        me.mun(field, 'validitychange', me.handleFieldValidityChange, me);
    },

    
    initFieldDefaults: function() {
        if (!this.fieldDefaults) {
            this.fieldDefaults = {};
        }
    },

    
    handleFieldValidityChange: function(field, isValid) {
        var me = this;
        if (field !== me) {
            me.fireEvent('fieldvaliditychange', me, field, isValid);
            me.onFieldValidityChange(field, isValid);
        }
    },

    
    handleFieldErrorChange: function(labelable, activeError) {
        var me = this;
        if (labelable !== me) {
            me.fireEvent('fielderrorchange', me, labelable, activeError);
            me.onFieldErrorChange(labelable, activeError);
        }
    },

    
    onFieldValidityChange: Ext.emptyFn,

    
    onFieldErrorChange: Ext.emptyFn,
    
    beforeDestroy: function(){
        this.monitor.unbind();
        this.callParent();
    }

});


Ext.define('Ext.layout.component.field.FieldContainer', {

    

    extend:  Ext.layout.component.field.Field ,

    alias: 'layout.fieldcontainer',

    

    type: 'fieldcontainer',

    waitForOuterHeightInDom: true,
    waitForOuterWidthInDom: true,

    beginLayout: function(ownerContext) {
        var owner = this.owner;
        this.callParent(arguments);

        
        ownerContext.hasRawContent = true;
        owner.bodyEl.setStyle('height', '');
        owner.containerEl.setStyle('height', '');
        ownerContext.containerElContext = ownerContext.getEl('containerEl');
    },

    measureContentHeight: function (ownerContext) {
        
        
        
        return ownerContext.hasDomProp('containerLayoutDone') ? this.callParent(arguments) : NaN;
    },

    measureContentWidth: function (ownerContext) {
        
        return ownerContext.hasDomProp('containerLayoutDone') ? this.callParent(arguments) : NaN;
    },

    publishInnerWidth: function (ownerContext, width) {
        var bodyContext = ownerContext.bodyCellContext,
            innerWidth = bodyContext.el.getWidth();

        bodyContext.setWidth(innerWidth, false);
        ownerContext.containerElContext.setWidth(innerWidth, false);
    },
    
    publishInnerHeight: function (ownerContext, height) {
        var bodyContext = ownerContext.bodyCellContext,
            containerElContext = ownerContext.containerElContext;
            
        height -= this.measureLabelErrorHeight(ownerContext);
        bodyContext.setHeight(height);
        containerElContext.setHeight(height);
    }
});


Ext.define('Ext.form.FieldContainer', {
    extend:  Ext.container.Container ,
    mixins: {
        labelable:  Ext.form.Labelable ,
        fieldAncestor:  Ext.form.FieldAncestor 
    },
                                                          

    alias: 'widget.fieldcontainer',

    componentLayout: 'fieldcontainer',

    componentCls: Ext.baseCSSPrefix + 'form-fieldcontainer',
    
    
    
    customOverflowEl: 'containerEl',
    
    childEls: [
        'containerEl'
    ],

    

    
    combineLabels: false,

    
    
    labelConnector: ', ',
    

    
    combineErrors: false,

    maskOnDisable: false,
    
    
    invalidCls: '',

    fieldSubTpl: '<div id="{id}-containerEl" class="{containerElCls}">{%this.renderContainer(out,values)%}</div>',

    initComponent: function() {
        var me = this;

        
        me.initLabelable();
        me.initFieldAncestor();
        
        me.callParent();
        me.initMonitor();
    },
    
    getOverflowEl: function(){
        return this.containerEl;    
    },

    
    onAdd: function(labelable) {
        var me = this;
        
        
        
        
        if (Ext.isGecko && me.layout.type === 'absolute' && !me.hideLabel && me.labelAlign !== 'top') {
            labelable.x += (me.labelWidth + me.labelPad);
        }
        me.callParent(arguments);
        if (me.combineLabels) {
            labelable.oldHideLabel = labelable.hideLabel;
            labelable.hideLabel = true;
        }
        me.updateLabel();
    },

    
    onRemove: function(labelable, isDestroying) {
        var me = this;
        me.callParent(arguments);
        if (!isDestroying) {
            if (me.combineLabels) {
                labelable.hideLabel = labelable.oldHideLabel;
            }
            me.updateLabel();
        }   
    },

    initRenderTpl: function() {
        var me = this;
        if (!me.hasOwnProperty('renderTpl')) {
            me.renderTpl = me.getTpl('labelableRenderTpl');
        }
        return me.callParent();
    },

    initRenderData: function() {
        var me = this,
            data = me.callParent();

        data.containerElCls = me.containerElCls;
        return Ext.applyIf(data, me.getLabelableRenderData());
    },

    
    getFieldLabel: function() {
        var label = this.fieldLabel || '';
        if (!label && this.combineLabels) {
            label = Ext.Array.map(this.query('[isFieldLabelable]'), function(field) {
                return field.getFieldLabel();
            }).join(this.labelConnector);
        }
        return label;
    },

    getSubTplData: function() {
        var ret = this.initRenderData();

        Ext.apply(ret, this.subTplData);
        return ret;
    },

    getSubTplMarkup: function() {
        var me = this,
            tpl = me.getTpl('fieldSubTpl'),
            html;

        if (!tpl.renderContent) {
            me.setupRenderTpl(tpl);
        }

        html = tpl.apply(me.getSubTplData());
        return html;
    },

    
    updateLabel: function() {
        var me = this,
            label = me.labelEl;
            
        if (label) {
            me.setFieldLabel(me.getFieldLabel());
        }
    },


    
    onFieldErrorChange: function(field, activeError) {
        if (this.combineErrors) {
            var me = this,
                oldError = me.getActiveError(),
                invalidFields = Ext.Array.filter(me.query('[isFormField]'), function(field) {
                    return field.hasActiveError();
                }),
                newErrors = me.getCombinedErrors(invalidFields);

            if (newErrors) {
                me.setActiveErrors(newErrors);
            } else {
                me.unsetActiveError();
            }

            if (oldError !== me.getActiveError()) {
                me.doComponentLayout();
            }
        }
    },

    
    getCombinedErrors: function(invalidFields) {
        var errors = [],
            f,
            fLen   = invalidFields.length,
            field,
            activeErrors, a, aLen,
            error, label;

        for (f = 0; f < fLen; f++) {
            field = invalidFields[f];
            activeErrors = field.getActiveErrors();
            aLen         = activeErrors.length;

            for (a = 0; a < aLen; a++) {
                error = activeErrors[a];
                label = field.getFieldLabel();

                errors.push((label ? label + ': ' : '') + error);
            }
        }

        return errors;
    },

    getTargetEl: function() {
        return this.containerEl;
    },

    applyTargetCls: function(targetCls) {
        var containerElCls = this.containerElCls;

        this.containerElCls = containerElCls ? containerElCls + ' ' + targetCls : targetCls;
    }
});


Ext.define('Ext.layout.container.CheckboxGroup', {
    extend:  Ext.layout.container.Container ,
    alias: ['layout.checkboxgroup'],

    
    autoFlex: true,

    type: 'checkboxgroup',
    
    createsInnerCt: true,

    childEls: [
        'innerCt'
    ],

    renderTpl: [
        '<table id="{ownerId}-innerCt" class="' + Ext.plainTableCls + '" cellpadding="0"',
            'role="presentation" style="{tableStyle}"><tbody><tr>',
            '<tpl for="columns">',
                '<td class="{parent.colCls}" valign="top" style="{style}">',
                    '{% this.renderColumn(out,parent,xindex-1) %}',
                '</td>',
            '</tpl>',
        '</tr></tbody></table>'
    ],

    lastOwnerItemsGeneration : null,

    beginLayout: function(ownerContext) {
        var me = this,
            columns,
            numCols,
            i, width, cwidth,
            totalFlex = 0, flexedCols = 0,
            autoFlex = me.autoFlex,
            innerCtStyle = me.innerCt.dom.style;

        me.callParent(arguments);

        columns = me.columnNodes;
        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);

        
        if (!ownerContext.widthModel.shrinkWrap) {
            numCols = columns.length;

            
            if (me.columnsArray) {

                
                for (i = 0; i < numCols; i++) {
                    width = me.owner.columns[i];
                    if (width < 1) {
                        totalFlex += width;
                        flexedCols++;
                    }
                }

                
                for (i = 0; i < numCols; i++) {
                    width = me.owner.columns[i];
                    if (width < 1) {
                        cwidth = ((width / totalFlex) * 100) + '%';
                    } else {
                        cwidth = width + 'px';
                    }
                    columns[i].style.width = cwidth;
                }
            }

            
            else {
                for (i = 0; i < numCols; i++) {
                    
                    
                    
                    cwidth = autoFlex
                        ? (1 / numCols * 100) + '%'
                        : '';
                    columns[i].style.width = cwidth;
                    flexedCols++;
                }
            }

            
            if (!flexedCols) {
                innerCtStyle.tableLayout = 'fixed';
                innerCtStyle.width = '';
            
            } else if (flexedCols < numCols) {
                innerCtStyle.tableLayout = 'fixed';
                innerCtStyle.width = '100%';
            
            } else {
                innerCtStyle.tableLayout = 'auto';
                
                if (autoFlex) {
                    innerCtStyle.width = '100%';
                } else {
                    innerCtStyle.width = '';
                }
            }

        } else {
            innerCtStyle.tableLayout = 'auto';
            innerCtStyle.width = '';
        }
    },

    cacheElements: function () {
        var me = this;

        
        me.callParent();

        me.rowEl = me.innerCt.down('tr');

        
        me.columnNodes = me.rowEl.dom.childNodes;
    },

    
    calculate: function(ownerContext) {
        var me = this,
            targetContext, widthShrinkWrap, heightShrinkWrap, shrinkWrap, table, targetPadding;

        
        
        if (!ownerContext.getDomProp('containerChildrenSizeDone')) {
            me.done = false;
        } else {
            targetContext = ownerContext.innerCtContext;
            widthShrinkWrap = ownerContext.widthModel.shrinkWrap;
            heightShrinkWrap = ownerContext.heightModel.shrinkWrap;
            shrinkWrap = heightShrinkWrap || widthShrinkWrap;
            table = targetContext.el.dom;
            targetPadding = shrinkWrap && targetContext.getPaddingInfo();

            if (widthShrinkWrap) {
                ownerContext.setContentWidth(table.offsetWidth + targetPadding.width, true);
            }

            if (heightShrinkWrap) {
                ownerContext.setContentHeight(table.offsetHeight + targetPadding.height, true);
            }
        }
    },

    doRenderColumn: function (out, renderData, columnIndex) {
        
        

        var me = renderData.$layout,
            owner = me.owner,
            columnCount = renderData.columnCount,
            items = owner.items.items,
            itemCount = items.length,
            item, itemIndex, rowCount, increment, tree;

        
        
        

        if (owner.vertical) {
            
            
            
            
            
            
            
            
            
            

            rowCount = Math.ceil(itemCount / columnCount); 
            itemIndex = columnIndex * rowCount;
            itemCount = Math.min(itemCount, itemIndex + rowCount);
            increment = 1;
        } else {
            
            
            
            
            
            
            
            
            
            

            itemIndex = columnIndex;
            increment = columnCount;
        }

        for ( ; itemIndex < itemCount; itemIndex += increment) {
            item = items[itemIndex];
            me.configureItem(item);
            tree = item.getRenderTree();
            Ext.DomHelper.generateMarkup(tree, out);
        }
    },

    
    getColumnCount: function() {
        var me = this,
            owner = me.owner,
            ownerColumns = owner.columns;

        
        
        if (me.columnsArray) {
            return ownerColumns.length;
        }

        if (Ext.isNumber(ownerColumns)) {
            return ownerColumns;
        }
        return owner.items.length;
    },

    getItemSizePolicy: function (item) {
        return this.autoSizePolicy;
    },

    getRenderData: function () {
        var me = this,
            data = me.callParent(),
            owner = me.owner,
            i, columns = me.getColumnCount(),
            width, column, cwidth,
            autoFlex = me.autoFlex,
            totalFlex = 0, flexedCols = 0;

        
        if (me.columnsArray) {
            for (i=0; i < columns; i++) {
                width = me.owner.columns[i];
                if (width < 1) {
                    totalFlex += width;
                    flexedCols++;
                }
            }
        }

        data.colCls = owner.groupCls;
        data.columnCount = columns;

        data.columns = [];
        for (i = 0; i < columns; i++) {
            column = (data.columns[i] = {});

            if (me.columnsArray) {
                width = me.owner.columns[i];
                if (width < 1) {
                    cwidth = ((width / totalFlex) * 100) + '%';
                } else {
                    cwidth = width + 'px';
                }
                column.style = 'width:' + cwidth;
            } else {
                column.style = 'width:' + (1 / columns * 100) + '%';
                flexedCols++;
            }
        }

        
        data.tableStyle =
            !flexedCols ? 'table-layout:fixed;' :
            (flexedCols < columns) ? 'table-layout:fixed;width:100%' :
            (autoFlex) ? 'table-layout:auto;width:100%' : 'table-layout:auto;';

        return data;
    },

    initLayout: function () {
        var me = this,
            owner = me.owner;

        me.columnsArray = Ext.isArray(owner.columns);
        me.autoColumns = !owner.columns || owner.columns === 'auto';
        me.vertical = owner.vertical;

        me.callParent();
    },

    
    isValidParent: function() {
        return true;
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);

        renderTpl.renderColumn = this.doRenderColumn;
    },

    renderChildren: function () {
        var me = this,
            generation = me.owner.items.generation;

        if (me.lastOwnerItemsGeneration !== generation) {
            me.lastOwnerItemsGeneration = generation;
            me.renderItems(me.getLayoutItems());
        }
    },

    
    renderItems : function(items) {
        var me = this,
            itemCount = items.length,
            i,
            item,
            rowCount,
            columnCount,
            rowIndex,
            columnIndex;

        if (itemCount) {
            Ext.suspendLayouts();

            if (me.autoColumns) {
                me.addMissingColumns(itemCount);
            }

            columnCount = me.columnNodes.length;
            rowCount = Math.ceil(itemCount / columnCount);

            for (i = 0; i < itemCount; i++) {
                item = items[i];
                rowIndex = me.getRenderRowIndex(i, rowCount, columnCount);
                columnIndex = me.getRenderColumnIndex(i, rowCount, columnCount);

                if (!item.rendered) {
                    me.renderItem(item, rowIndex, columnIndex);
                } else if (!me.isItemAtPosition(item, rowIndex, columnIndex)) {
                    me.moveItem(item, rowIndex, columnIndex);
                }
            }

            if (me.autoColumns) {
                me.removeExceedingColumns(itemCount);
            }

            Ext.resumeLayouts(true);
        }
    },

    isItemAtPosition : function(item, rowIndex, columnIndex) {
        return item.el.dom === this.getNodeAt(rowIndex, columnIndex);
    },

    getRenderColumnIndex : function(itemIndex, rowCount, columnCount) {
        if (this.vertical) {
            return Math.floor(itemIndex / rowCount);
        } else {
            return itemIndex % columnCount;
        }
    },

    getRenderRowIndex : function(itemIndex, rowCount, columnCount) {
        var me = this;
        if (me.vertical) {
            return itemIndex % rowCount;
        } else {
            return Math.floor(itemIndex / columnCount);
        }
    },

    getNodeAt : function(rowIndex, columnIndex) {
        return this.columnNodes[columnIndex].childNodes[rowIndex];
    },

    addMissingColumns : function(itemsCount) {
        var me = this,
            existingColumnsCount = me.columnNodes.length,
            missingColumnsCount,
            row,
            cls,
            i;
        if (existingColumnsCount < itemsCount) {
            missingColumnsCount = itemsCount - existingColumnsCount;
            row = me.rowEl;
            cls = me.owner.groupCls;
            for (i = 0; i < missingColumnsCount; i++) {
                row.createChild({
                    cls: cls,
                    tag: 'td',
                    vAlign: 'top'
                });
            }
        }
    },

    removeExceedingColumns : function(itemsCount) {
        var me = this,
            existingColumnsCount = me.columnNodes.length,
            exceedingColumnsCount,
            row,
            i;
        if (existingColumnsCount > itemsCount) {
            exceedingColumnsCount = existingColumnsCount - itemsCount;
            row = me.rowEl;
            for (i = 0; i < exceedingColumnsCount; i++) {
                row.last().remove();
            }
        }
    },

    
    renderItem : function(item, rowIndex, columnIndex) {
        var me = this;

        me.configureItem(item);
        item.render(Ext.get(me.columnNodes[columnIndex]), rowIndex);
        me.afterRenderItem(item);
    },

    
    moveItem : function(item, rowIndex, columnIndex) {
        var me = this,
            column = me.columnNodes[columnIndex],
            targetNode = column.childNodes[rowIndex];
        column.insertBefore(item.el.dom, targetNode || null);
    }

});


Ext.define('Ext.form.CheckboxManager', {
    extend:  Ext.util.MixedCollection ,
    singleton: true,

    getByName: function(name, formId) {
        return this.filterBy(function(item) {
            return item.name == name && item.getFormId() == formId;
        });
    }
});


Ext.define('Ext.form.field.Checkbox', {
    extend:  Ext.form.field.Base ,
    alias: ['widget.checkboxfield', 'widget.checkbox'],
    alternateClassName: 'Ext.form.Checkbox',
                                                             

    componentLayout: 'field',
    
    
    stretchInputElFixed: false,

    childEls: [
        
        'boxLabelEl'
    ],

    
    fieldSubTpl: [
        '<tpl if="boxLabel && boxLabelAlign == \'before\'">',
            '{beforeBoxLabelTpl}',
            '<label id="{cmpId}-boxLabelEl" {boxLabelAttrTpl} class="{boxLabelCls} {boxLabelCls}-{boxLabelAlign}" for="{id}">',
                '{beforeBoxLabelTextTpl}',
                '{boxLabel}',
                '{afterBoxLabelTextTpl}',
            '</label>',
            '{afterBoxLabelTpl}',
        '</tpl>',
        
        
        '<input type="{inputTypeAttr}" id="{id}" {inputAttrTpl}',
            '<tpl if="tabIdx"> tabIndex="{tabIdx}"</tpl>',
            '<tpl if="disabled"> disabled="disabled"</tpl>',
            '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
            '<tpl if="ariaAttrs"> {ariaAttrs}</tpl>',
            ' class="{fieldCls} {typeCls} {inputCls} {childElCls}" autocomplete="off" hidefocus="true" />',
        '<tpl if="boxLabel && boxLabelAlign == \'after\'">',
            '{beforeBoxLabelTpl}',
            '<label id="{cmpId}-boxLabelEl" {boxLabelAttrTpl} class="{boxLabelCls} {boxLabelCls}-{boxLabelAlign}" for="{id}">',
                '{beforeBoxLabelTextTpl}',
                '{boxLabel}',
                '{afterBoxLabelTextTpl}',
            '</label>',
            '{afterBoxLabelTpl}',
        '</tpl>',
        {
            disableFormats: true,
            compiled: true
        }
    ],

    subTplInsertions: [
        
        'beforeBoxLabelTpl',

        
        'afterBoxLabelTpl',

        
        'beforeBoxLabelTextTpl',

        
        'afterBoxLabelTextTpl',

        
        'boxLabelAttrTpl',

        
        'inputAttrTpl'
    ],

    
    isCheckbox: true,

    
    focusCls: 'form-checkbox-focus',

    
    
    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'form-cb-wrap',

    
    checked: false,

    
    checkedCls: Ext.baseCSSPrefix + 'form-cb-checked',

    

    
    boxLabelCls: Ext.baseCSSPrefix + 'form-cb-label',

    
    boxLabelAlign: 'after',

    
    inputValue: 'on',

    

    

    

    
    checkChangeEvents: [],
    inputType: 'checkbox',
    
    
    inputTypeAttr: 'button',


    
    onRe: /^on$/i,

    
    inputCls: Ext.baseCSSPrefix + 'form-cb',

    initComponent: function() {
        this.callParent(arguments);
        this.getManager().add(this);
    },

    initValue: function() {
        var me = this,
            checked = !!me.checked;

        
        me.originalValue = me.lastValue = checked;

        
        me.setValue(checked);
    },

    getElConfig: function() {
        var me = this;

        
        if (me.isChecked(me.rawValue, me.inputValue)) {
            me.addCls(me.checkedCls);
        }
        return me.callParent();
    },

    getFieldStyle: function() {
        return Ext.isObject(this.fieldStyle) ? Ext.DomHelper.generateStyles(this.fieldStyle) : this.fieldStyle ||'';
    },

    getSubTplData: function() {
        var me = this;
        return Ext.apply(me.callParent(), {
            disabled      : me.readOnly || me.disabled,
            boxLabel      : me.boxLabel,
            boxLabelCls   : me.boxLabelCls,
            boxLabelAlign : me.boxLabelAlign,
            inputTypeAttr : me.inputTypeAttr
        });
    },

    initEvents: function() {
        var me = this;
        me.callParent();
        me.mon(me.inputEl, 'click', me.onBoxClick, me);
    },
    
    
    setBoxLabel: function(boxLabel){
        var me = this;
        
        me.boxLabel = boxLabel;
        if (me.rendered) {
            me.boxLabelEl.update(boxLabel);
        }
    },

    
    onBoxClick: function(e) {
        var me = this;
        if (!me.disabled && !me.readOnly) {
            this.setValue(!this.checked);
        }
    },

    
    getRawValue: function() {
        return this.checked;
    },

    
    getValue: function() {
        return this.checked;
    },

    
    getSubmitValue: function() {
        var unchecked = this.uncheckedValue,
            uncheckedVal = Ext.isDefined(unchecked) ? unchecked : null;
        return this.checked ? this.inputValue : uncheckedVal;
    },

    isChecked: function(rawValue, inputValue) {
        return (rawValue === true || rawValue === 'true' || rawValue === '1' || rawValue === 1 ||
                      (((Ext.isString(rawValue) || Ext.isNumber(rawValue)) && inputValue) ? rawValue == inputValue : this.onRe.test(rawValue)));
    },

    
    setRawValue: function(value) {
        var me = this,
            inputEl = me.inputEl,
            checked = me.isChecked(value, me.inputValue);

        if (inputEl) {
            me[checked ? 'addCls' : 'removeCls'](me.checkedCls);
        }

        me.checked = me.rawValue = checked;
        return checked;
    },

    
    setValue: function(checked) {
        var me = this,
            boxes, i, len, box;

        
        
        
        
        if (Ext.isArray(checked)) {
            boxes = me.getManager().getByName(me.name, me.getFormId()).items;
            len = boxes.length;

            for (i = 0; i < len; ++i) {
                box = boxes[i];
                box.setValue(Ext.Array.contains(checked, box.inputValue));
            }
        } else {
            me.callParent(arguments);
        }

        return me;
    },

    
    valueToRaw: function(value) {
        
        return value;
    },

    
    onChange: function(newVal, oldVal) {
        var me = this,
            handler = me.handler;
        if (handler) {
            handler.call(me.scope || me, me, newVal);
        }
        me.callParent(arguments);
    },
    
    resetOriginalValue: function( fromBoxInGroup){
        var me = this,
            boxes,
            box,
            len,
            i;
            
        
        if (!fromBoxInGroup) {
            boxes = me.getManager().getByName(me.name, me.getFormId()).items;
            len  = boxes.length;
            
            for (i = 0; i < len; ++i) {
                box = boxes[i];
                if (box !== me) {
                    boxes[i].resetOriginalValue(true);
                }
            }
        }
        me.callParent();
    },

    
    beforeDestroy: function(){
        this.callParent();
        this.getManager().removeAtKey(this.id);
    },

    
    getManager: function() {
        return Ext.form.CheckboxManager;
    },

    onEnable: function() {
        var me = this,
            inputEl = me.inputEl;
        me.callParent();
        if (inputEl) {
            
            inputEl.dom.disabled = me.readOnly;
        }
    },

    setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl) {
            
            inputEl.dom.disabled = !!readOnly || me.disabled;
        }
        me.callParent(arguments);
    },

    getFormId: function(){
        var me = this,
            form;

        if (!me.formId) {
            form = me.up('form');
            if (form) {
                me.formId = form.id;
            }
        }
        return me.formId;
    }
});


Ext.define('Ext.form.CheckboxGroup', {
    extend: Ext.form.FieldContainer ,
    mixins: {
        field:  Ext.form.field.Field 
    },
    alias: 'widget.checkboxgroup',
               
                                             
                                  
                             
      

    

    

    
    columns : 'auto',

    
    vertical : false,

    
    allowBlank : true,

    
    
    blankText : "You must select at least one item in this group",
    

    
    defaultType : 'checkboxfield',

    
    groupCls : Ext.baseCSSPrefix + 'form-check-group',

    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'form-checkboxgroup-body',

    
    layout: 'checkboxgroup',

    componentCls: Ext.baseCSSPrefix + 'form-checkboxgroup',

    initComponent: function() {
        var me = this;
        me.callParent();
        me.initField();
    },

    
    initValue: function() {
        var me = this,
            valueCfg = me.value;
        me.originalValue = me.lastValue = valueCfg || me.getValue();
        if (valueCfg) {
            me.setValue(valueCfg);
        }
    },

    
    onAdd: function(item) {
        var me = this,
            items,
            len, i;

        if (item.isCheckbox) {
            me.mon(item, 'change', me.checkChange, me);
        } else if (item.isContainer) {
            items = item.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                me.onAdd(items[i]);
            }
        }
        me.callParent(arguments);
    },

    onRemove: function(item) {
        var me = this,
            items,
            len, i;

        if (item.isCheckbox) {
            me.mun(item, 'change', me.checkChange, me);
        } else if (item.isContainer) {
            items = item.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                me.onRemove(items[i]);
            }
        }
        me.callParent(arguments);
    },

    
    isEqual: function(value1, value2) {
        var toQueryString = Ext.Object.toQueryString;
        return toQueryString(value1) === toQueryString(value2);
    },

    
    getErrors: function() {
        var errors = [];
        if (!this.allowBlank && Ext.isEmpty(this.getChecked())) {
            errors.push(this.blankText);
        }
        return errors;
    },

    
    getBoxes: function(query) {
        return this.query('[isCheckbox]' + (query||''));
    },

    
    eachBox: function(fn, scope) {
        Ext.Array.forEach(this.getBoxes(), fn, scope || this);
    },

    
    getChecked: function() {
        return this.getBoxes('[checked]');
    },

    
    isDirty: function(){
        var boxes = this.getBoxes(),
            b ,
            bLen  = boxes.length;

        for (b = 0; b < bLen; b++) {
            if (boxes[b].isDirty()) {
                return true;
            }
        }
    },

    
    setReadOnly: function(readOnly) {
        var boxes = this.getBoxes(),
            b,
            bLen  = boxes.length;

        for (b = 0; b < bLen; b++) {
            boxes[b].setReadOnly(readOnly);
        }

        this.readOnly = readOnly;
    },

    
    reset: function() {
        var me = this,
            hadError = me.hasActiveError(),
            preventMark = me.preventMark;
        me.preventMark = true;
        me.batchChanges(function() {
            var boxes = me.getBoxes(),
                b,
                bLen  = boxes.length;

            for (b = 0; b < bLen; b++) {
                boxes[b].reset();
            }
        });
        me.preventMark = preventMark;
        me.unsetActiveError();
        if (hadError) {
            me.updateLayout();
        }
    },

    resetOriginalValue: function(){
        var me    = this,
            boxes = me.getBoxes(),
            b,
            bLen  = boxes.length;

        for (b = 0; b < bLen; b++) {
            boxes[b].resetOriginalValue();
        }

        me.originalValue = me.getValue();
        me.checkDirty();
    },


    
    setValue: function(value) {
        var me    = this,
            boxes = me.getBoxes(),
            b,
            bLen  = boxes.length,
            box, name,
            cbValue;

        me.batchChanges(function() {
            for (b = 0; b < bLen; b++) {
                box = boxes[b];
                name = box.getName();
                cbValue = false;

                if (value && value.hasOwnProperty(name)) {
                    if (Ext.isArray(value[name])) {
                        cbValue = Ext.Array.contains(value[name], box.inputValue);
                    } else {
                        
                        cbValue = value[name];
                    }
                }

                box.setValue(cbValue);
            }
        });
        return me;
    },


    
    getValue: function() {
        var values = {},
            boxes  = this.getBoxes(),
            b,
            bLen   = boxes.length,
            box, name, inputValue, bucket;

        for (b = 0; b < bLen; b++) {
            box        = boxes[b];
            name       = box.getName();
            inputValue = box.inputValue;

            if (box.getValue()) {
                if (values.hasOwnProperty(name)) {
                    bucket = values[name];
                    if (!Ext.isArray(bucket)) {
                        bucket = values[name] = [bucket];
                    }
                    bucket.push(inputValue);
                } else {
                    values[name] = inputValue;
                }
            }
        }

        return values;
    },

    
    getSubmitData: function() {
        return null;
    },

    
    getModelData: function() {
        return null;
    },

    validate: function() {
        var me = this,
            errors,
            isValid,
            wasValid;

        if (me.disabled) {
            isValid = true;
        } else {
            errors = me.getErrors();
            isValid = Ext.isEmpty(errors);
            wasValid = me.wasValid;
            if (isValid) {
                me.unsetActiveError();
            } else {
                me.setActiveError(errors);
            }
        }
        if (isValid !== wasValid) {
            me.wasValid = isValid;
            me.fireEvent('validitychange', me, isValid);
            me.updateLayout();
        }

        return isValid;
    }

}, function() {

    this.borrow(Ext.form.field.Base, ['markInvalid', 'clearInvalid', 'setError']);

});



Ext.define('Ext.form.FieldSet', {
    extend:  Ext.container.Container ,
    mixins: {
        fieldAncestor:  Ext.form.FieldAncestor 
    },
    alias: 'widget.fieldset',
                                                                                                                        

    

    

    

    

    
    collapsed: false,

    
    toggleOnTitleClick : true,

    

    
    baseCls: Ext.baseCSSPrefix + 'fieldset',

    
    layout: 'anchor',

    componentLayout: 'fieldset',

    autoEl: 'fieldset',

    childEls: [
        'body'
    ],

    renderTpl: [
        '{%this.renderLegend(out,values);%}',
        '<div id="{id}-body" class="{baseCls}-body {bodyTargetCls}"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
            '{%this.renderContainer(out,values);%}',
        '</div>'
    ],

    stateEvents : [ 'collapse', 'expand' ],

    maskOnDisable: false,

    beforeDestroy: function(){
        var me = this,
            legend = me.legend;

        if (legend) {
            
            delete legend.ownerCt;
            legend.destroy();
            me.legend = null;
        }
        me.callParent();
    },

    initComponent: function() {
        var me = this,
            baseCls = me.baseCls;
                
        me.initFieldAncestor();

        me.callParent();

        
        
        
        
        
        
        
        
        
        
        me.layout.managePadding = me.layout.manageOverflow = false;

        me.addEvents(

            
            "beforeexpand",

            
            "beforecollapse",

            
            "expand",

            
            "collapse"
        );

        if (me.collapsed) {
            me.addCls(baseCls + '-collapsed');
            me.collapse();
        }
        if (me.title || me.checkboxToggle || me.collapsible) {
            me.addTitleClasses();
            me.legend = Ext.widget(me.createLegendCt());
        }
        me.initMonitor();
    },

    initPadding: function(targetEl) {
        var me = this,
            body = me.getProtoBody(),
            padding = me.padding,
            bodyPadding;

        if (padding !== undefined) {
            if (Ext.isIEQuirks || Ext.isIE8m) {
                
                
                padding = me.parseBox(padding);
                bodyPadding = Ext.Element.parseBox(0);
                bodyPadding.top = padding.top;
                padding.top = 0;
                body.setStyle('padding', me.unitizeBox(bodyPadding));
            }

            targetEl.setStyle('padding', me.unitizeBox(padding));
        }
    },

    getProtoBody: function () {
        var me = this,
            body = me.protoBody;

        if (!body) {
            me.protoBody = body = new Ext.util.ProtoElement({
                styleProp: 'bodyStyle',
                styleIsText: true
            });
        }

        return body;
    },

    
    initRenderData: function() {
        var me = this,
            data = me.callParent();

        data.bodyTargetCls = me.bodyTargetCls;
        me.protoBody.writeTo(data);
        delete me.protoBody;

        return data;
    },

    getState: function () {
        var state = this.callParent();

        state = this.addPropertyToState(state, 'collapsed');

        return state;
    },

    afterCollapse: Ext.emptyFn,
    afterExpand: Ext.emptyFn,

    collapsedHorizontal: function () {
        return true;
    },

    collapsedVertical: function () {
        return true;
    },

    createLegendCt: function () {
        var me = this,
            items = [],
            legend = {
                xtype: 'container',
                baseCls: me.baseCls + '-header',
                id: me.id + '-legend',
                autoEl: 'legend',
                items: items,
                ownerCt: me,
                shrinkWrap: true,
                ownerLayout: me.componentLayout
            };

        
        if (me.checkboxToggle) {
            items.push(me.createCheckboxCmp());
        } else if (me.collapsible) {
            
            items.push(me.createToggleCmp());
        }

        
        items.push(me.createTitleCmp());

        return legend;
    },

    
    createTitleCmp: function() {
        var me  = this,
            cfg = {
                xtype : 'component',
                html  : me.title,
                cls   : me.baseCls + '-header-text',
                id    : me.id + '-legendTitle'
            };

        if (me.collapsible && me.toggleOnTitleClick) {
            cfg.listeners = {
                click : {
                    element: 'el',
                    scope : me,
                    fn : me.toggle
                }
            };
            cfg.cls += ' ' + me.baseCls + '-header-text-collapsible';
        }

        return (me.titleCmp = Ext.widget(cfg));
    },

    

    
    createCheckboxCmp: function() {
        var me = this,
            suffix = '-checkbox';

        me.checkboxCmp = Ext.widget({
            xtype: 'checkbox',
            hideEmptyLabel: true,
            name: me.checkboxName || me.id + suffix,
            cls: me.baseCls + '-header' + suffix,
            id: me.id + '-legendChk',
            checked: !me.collapsed,
            listeners: {
                change: me.onCheckChange,
                scope: me
            }
        });
        return me.checkboxCmp;
    },

    

    
    createToggleCmp: function() {
        var me = this;
        me.toggleCmp = Ext.widget({
            xtype: 'tool',
            height: 15,
            width: 15,
            type: 'toggle',
            handler: me.toggle,
            id: me.id + '-legendToggle',
            scope: me
        });
        return me.toggleCmp;
    },

    doRenderLegend: function (out, renderData) {
        
        

        var me = renderData.$comp,
            legend = me.legend,
            tree;
            
        
        if (legend) {
            legend.ownerLayout.configureItem(legend);
            tree = legend.getRenderTree();
            Ext.DomHelper.generateMarkup(tree, out);
        }
    },

    finishRender: function () {
        var legend = this.legend;

        this.callParent();

        if (legend) {
            legend.finishRender();
        }
    },

    getCollapsed: function () {
        return this.collapsed ? 'top' : false;
    },

    getCollapsedDockedItems: function () {
        var legend = this.legend;

        return legend ? [ legend ] : [];
    },

    
    setTitle: function(title) {
        var me = this,
            legend = me.legend,
            baseCls = me.baseCls;
            
        me.title = title;
        if (me.rendered) {
            if (!legend) {
                me.legend = legend = Ext.widget(me.createLegendCt());
                me.addTitleClasses();
                legend.ownerLayout.configureItem(legend);
                legend.render(me.el, 0);
            }
            me.titleCmp.update(title);
        } else if (legend) {
            me.titleCmp.update(title);
        } else {
            me.addTitleClasses();
            me.legend = Ext.widget(me.createLegendCt());
        }
        return me;
    },
    
    addTitleClasses: function(){
        var me = this,
            title = me.title,
            baseCls = me.baseCls;
            
        if (title) {
            me.addCls(baseCls + '-with-title');
        }
        
        if (title || me.checkboxToggle || me.collapsible) {
            me.addCls(baseCls + '-with-header');
        }
    },

    applyTargetCls: function(targetCls) {
        this.bodyTargetCls = targetCls;
    },

    getTargetEl : function() {
        return this.body || this.frameBody || this.el;
    },

    getDefaultContentTarget: function() {
        return this.body;
    },

    
    expand : function(){
        return this.setExpanded(true);
    },

    
    collapse : function() {
        return this.setExpanded(false);
    },

    
    setExpanded: function(expanded) {
        var me = this,
            checkboxCmp = me.checkboxCmp,
            operation = expanded ? 'expand' : 'collapse';

        if (!me.rendered || me.fireEvent('before' + operation, me) !== false) {
            expanded = !!expanded;

            if (checkboxCmp) {
                checkboxCmp.setValue(expanded);
            }

            if (expanded) {
                me.removeCls(me.baseCls + '-collapsed');
            } else {
                me.addCls(me.baseCls + '-collapsed');
            }
            me.collapsed = !expanded;
            if (expanded) {
                delete me.getHierarchyState().collapsed;
            } else {
                me.getHierarchyState().collapsed = true;
            }
            if (me.rendered) {
                
                
                
                me.updateLayout({ isRoot: false });
                me.fireEvent(operation, me);
            }
        }
        return me;
    },
    
    getRefItems: function(deep) {
        var refItems = this.callParent(arguments),
            legend = this.legend;

        
        if (legend) {
            refItems.unshift(legend);
            if (deep) {
                refItems.unshift.apply(refItems, legend.getRefItems(true));
            }
        }
        return refItems;
    },

    
    toggle: function() {
        this.setExpanded(!!this.collapsed);
    },

    
    onCheckChange: function(cmp, checked) {
        this.setExpanded(checked);
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);

        renderTpl.renderLegend = this.doRenderLegend;
    }
});


Ext.define('Ext.form.Label', {
    extend: Ext.Component ,
    alias: 'widget.label',
                                  

    autoEl: 'label',

    
    
    
    
    maskOnDisable: false,

    getElConfig: function(){
        var me = this;

        me.html = me.text ? Ext.util.Format.htmlEncode(me.text) : (me.html || '');
        return Ext.apply(me.callParent(), {
            htmlFor: me.forId || ''
        });
    },

    
    setText : function(text, encode){
        var me = this;
        
        encode = encode !== false;
        if(encode) {
            me.text = text;
            delete me.html;
        } else {
            me.html = text;
            delete me.text;
        }
        
        if(me.rendered){
            me.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(text) : text;
            me.updateLayout();
        }
        return me;
    }
});



Ext.define('Ext.form.Panel', {
    extend: Ext.panel.Panel ,
    mixins: {
        fieldAncestor:  Ext.form.FieldAncestor 
    },
    alias: 'widget.form',
    alternateClassName: ['Ext.FormPanel', 'Ext.form.FormPanel'],
                                                        

    

    

    
    layout: 'anchor',

    ariaRole: 'form',
    
    basicFormConfigs: [
        'api', 
        'baseParams', 
        'errorReader', 
        'jsonSubmit',
        'method', 
        'paramOrder',
        'paramsAsHash',
        'reader',
        'standardSubmit',
        'timeout',
        'trackResetOnLoad',
        'url',
        'waitMsgTarget',
        'waitTitle'
    ],

    initComponent: function() {
        var me = this;

        if (me.frame) {
            me.border = false;
        }

        me.initFieldAncestor();
        me.callParent();

        me.relayEvents(me.form, [
            
            'beforeaction',
            
            'actionfailed',
            
            'actioncomplete',
            
            'validitychange',
            
            'dirtychange'
        ]);

        
        if (me.pollForChanges) {
            me.startPolling(me.pollInterval || 500);
        }
    },

    initItems: function() {
        
        this.callParent();
        this.initMonitor();
        this.form = this.createForm();
    },

    
    afterFirstLayout: function() {
        this.callParent(arguments);
        this.form.initialize();
    },

    
    createForm: function() {
        var cfg = {},
            props = this.basicFormConfigs,
            len = props.length,
            i = 0,
            prop;
            
        for (; i < len; ++i) {
            prop = props[i];
            cfg[prop] = this[prop];
        }
        return new Ext.form.Basic(this, cfg);
    },

    
    getForm: function() {
        return this.form;
    },

    
    loadRecord: function(record) {
        return this.getForm().loadRecord(record);
    },

    
    getRecord: function() {
        return this.getForm().getRecord();
    },
    
    
    updateRecord: function(record) {
        return this.getForm().updateRecord(record);
    },

    
    getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
        return this.getForm().getValues(asString, dirtyOnly, includeEmptyText, useDataValues);
    },
    
    
    isDirty: function () {
        return this.form.isDirty();
    },
    
    
    isValid: function () {
       return this.form.isValid();
    },
    
    
    hasInvalidField: function () {
        return this.form.hasInvalidField();
    },

    beforeDestroy: function() {
        this.stopPolling();
        this.form.destroy();
        this.callParent();
    },

    
    load: function(options) {
        this.form.load(options);
    },

    
    submit: function(options) {
        this.form.submit(options);
    },

    
    startPolling: function(interval) {
        this.stopPolling();
        var task = new Ext.util.TaskRunner(interval);
        task.start({
            interval: 0,
            run: this.checkChange,
            scope: this
        });
        this.pollTask = task;
    },

    
    stopPolling: function() {
        var task = this.pollTask;
        if (task) {
            task.stopAll();
            delete this.pollTask;
        }
    },

    
    checkChange: function() {
        var fields = this.form.getFields().items,
            f,
            fLen   = fields.length;

        for (f = 0; f < fLen; f++) {
            fields[f].checkChange();
        }
    }
});


Ext.define('Ext.form.RadioManager', {
    extend:  Ext.util.MixedCollection ,
    singleton: true,

    getByName: function(name, formId) {
        return this.filterBy(function(item) {
            return item.name == name && item.getFormId() == formId;
        });
    },

    getWithValue: function(name, value, formId) {
        return this.filterBy(function(item) {
            return item.name == name && item.inputValue == value && item.getFormId() == formId;
        });
    },

    getChecked: function(name, formId) {
        return this.findBy(function(item) {
            return item.name == name && item.checked && item.getFormId() == formId;
        });
    }
});


Ext.define('Ext.form.field.Radio', {
    extend: Ext.form.field.Checkbox ,
    alias: ['widget.radiofield', 'widget.radio'],
    alternateClassName: 'Ext.form.Radio',
                                        

    
    isRadio: true,

    
    focusCls: 'form-radio-focus',

    

    
    inputType: 'radio',
    ariaRole: 'radio',
    
    formId: null,

    
    getGroupValue: function() {
        var selected = this.getManager().getChecked(this.name, this.getFormId());
        return selected ? selected.inputValue : null;
    },

    
    onBoxClick: function(e) {
        var me = this;
        if (!me.disabled && !me.readOnly) {
            this.setValue(true);
        }
    },
    
    onRemoved: function(){
        this.callParent(arguments);
        this.formId = null;
    },

    
    setValue: function(v) {
        var me = this,
            active;

        if (Ext.isBoolean(v)) {
            me.callParent(arguments);
        } else {
            active = me.getManager().getWithValue(me.name, v, me.getFormId()).getAt(0);
            if (active) {
                active.setValue(true);
            }
        }
        return me;
    },

    
    getSubmitValue: function() {
        return this.checked ? this.inputValue : null;
    },

    getModelData: function() {
        return this.getSubmitData();
    },

    
    onChange: function(newVal, oldVal) {
        var me = this,
            r, rLen, radio, radios;

        me.callParent(arguments);

        if (newVal) {
            radios = me.getManager().getByName(me.name, me.getFormId()).items;
            rLen   = radios.length;

            for (r = 0; r < rLen; r++) {
                radio = radios[r];

                if (radio !== me) {
                    radio.setValue(false);
                }
            }
        }
    },

    
    getManager: function() {
        return Ext.form.RadioManager;
    }
});


Ext.define('Ext.form.RadioGroup', {
    extend:  Ext.form.CheckboxGroup ,
    alias: 'widget.radiogroup',

               
                              
      

    
    
    allowBlank : true,
    
    
    blankText : 'You must select one item in this group',
    

    
    defaultType : 'radiofield',

    
    groupCls : Ext.baseCSSPrefix + 'form-radio-group',

    getBoxes: function(query) {
        return this.query('[isRadio]' + (query||''));
    },
    
    checkChange: function() {
        var value = this.getValue(),
            key = Ext.Object.getKeys(value)[0];
            
        
        
        if (Ext.isArray(value[key])) {
            return;
        }
        this.callParent(arguments);    
    },

    
    setValue: function(value) {
        var cbValue, first, formId, radios,
            i, len, name;

        if (Ext.isObject(value)) {
            for (name in value) {
                if (value.hasOwnProperty(name)) {
                    cbValue = value[name];
                    first = this.items.first();
                    formId = first ? first.getFormId() : null;
                    radios = Ext.form.RadioManager.getWithValue(name, cbValue, formId).items;
                    len = radios.length;

                    for (i = 0; i < len; ++i) {
                        radios[i].setValue(true);
                    }
                }
            }
        }
        return this;
    }
});


Ext.define('Ext.form.action.DirectLoad', {
    extend: Ext.form.action.Load ,
                                     
    alternateClassName: 'Ext.form.Action.DirectLoad',
    alias: 'formaction.directload',

    type: 'directload',

    run: function() {
        var me = this,
            form = me.form,
            api = form.api,
            fn = api.load,
            method, args;

        if (typeof fn !== 'function') {
            
            api.load = fn = Ext.direct.Manager.parseMethod(fn);

        }
        
        method = fn.directCfg.method;
        args = method.getArgs(me.getParams(), form.paramOrder, form.paramsAsHash);
            
        args.push(me.onComplete, me);
        fn.apply(window, args);
    },

    
    
    
    processResponse: function(result) {
        return (this.result = result);
    },

    onComplete: function(data, response) {
        if (data) {
            this.onSuccess(data);
        } else {
            this.onFailure(null);
        }
    }
});




Ext.define('Ext.form.action.DirectSubmit', {
    extend: Ext.form.action.Submit ,
                                     
    alternateClassName: 'Ext.form.Action.DirectSubmit',
    alias: 'formaction.directsubmit',

    type: 'directsubmit',

    doSubmit: function() {
        var me = this,
            form = me.form,
            api = form.api,
            fn = api.submit,
            callback = Ext.Function.bind(me.onComplete, me),
            formInfo = me.buildForm(),
            options;
        
        if (typeof fn !== 'function') {
            
            api.submit = fn = Ext.direct.Manager.parseMethod(fn);
            
        }
        
        if (me.timeout || form.timeout) {
            options = {
                timeout: me.timeout * 1000 || form.timeout * 1000
            };
        }
        
        fn.call(window, formInfo.formEl, callback, me, options);
        me.cleanup(formInfo);
    },

    
    
    
    processResponse: function(result) {
        return (this.result = result);
    },
    
    onComplete: function(data, response){
        if (data) {
            this.onSuccess(data);
        } else {
            this.onFailure(null);
        }
    }
});


Ext.define('Ext.form.action.StandardSubmit', {
    extend: Ext.form.action.Submit ,
    alias: 'formaction.standardsubmit',

    

    
    doSubmit: function() {
        var formInfo = this.buildForm();
        formInfo.formEl.submit();
        this.cleanup(formInfo);
    }

});


Ext.define('Ext.layout.component.field.Trigger', {

    

    alias: 'layout.triggerfield',

    extend:  Ext.layout.component.field.Field ,

    

    type: 'triggerfield',

    
    borderWidths: {},

    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            flags;

        ownerContext.triggerWrap = ownerContext.getEl('triggerWrap');

        me.callParent(arguments);

        
        flags = owner.getTriggerStateFlags();
        if (flags != owner.lastTriggerStateFlags) {
            owner.lastTriggerStateFlags = flags;
            me.updateEditState();
        }
    },
    
    beginLayoutCycle: function(ownerContext){
        this.callParent(arguments);
        
        
        if (ownerContext.widthModel.shrinkWrap && !this.owner.inputWidth) {
            ownerContext.inputContext.el.setStyle('width', '');
        }    
    },

    beginLayoutFixed: function (ownerContext, width, suffix) {
        var me = this,
            owner = ownerContext.target,
            ieInputWidthAdjustment = me.ieInputWidthAdjustment || 0,
            inputWidth = '100%',
            triggerWrap = owner.triggerWrap;

        me.callParent(arguments);

        owner.inputCell.setStyle('width', '100%');
        if(ieInputWidthAdjustment) {
            me.adjustIEInputPadding(ownerContext);
            if(suffix === 'px') {
                if (owner.inputWidth) {
                    inputWidth = owner.inputWidth - me.getExtraWidth(ownerContext);
                } else {
                    inputWidth = width - ieInputWidthAdjustment - me.getExtraWidth(ownerContext);
                }
                inputWidth += 'px';
            }
        }
        owner.inputEl.setStyle('width', inputWidth);
        inputWidth = owner.inputWidth;
        if (inputWidth) {
            triggerWrap.setStyle('width', inputWidth + (ieInputWidthAdjustment) + 'px');
        } else {
            triggerWrap.setStyle('width', width + suffix);
        }
        triggerWrap.setStyle('table-layout', 'fixed');
    },

    adjustIEInputPadding: function(ownerContext) {
        
        this.owner.inputCell.setStyle('padding-right', this.ieInputWidthAdjustment + 'px');
    },

    
    getExtraWidth: function(ownerContext) {
        var me = this,
            owner = me.owner,
            borderWidths = me.borderWidths,
            ui = owner.ui + owner.triggerEl.getCount();

        if (!(ui in borderWidths)) {
            borderWidths[ui] = ownerContext.triggerWrap.getBorderInfo().width
        }
        return borderWidths[ui] + owner.getTriggerWidth();
    },

    beginLayoutShrinkWrap: function (ownerContext) {
        var owner = ownerContext.target,
            emptyString = '',
            inputWidth = owner.inputWidth,
            triggerWrap = owner.triggerWrap;

        this.callParent(arguments);

        if (inputWidth) {
            triggerWrap.setStyle('width', inputWidth + 'px');
            inputWidth = (inputWidth - this.getExtraWidth(ownerContext)) + 'px';
            owner.inputEl.setStyle('width', inputWidth);
            owner.inputCell.setStyle('width', inputWidth);
        } else {
            owner.inputCell.setStyle('width', emptyString);
            owner.inputEl.setStyle('width', emptyString);
            triggerWrap.setStyle('width', emptyString);
            triggerWrap.setStyle('table-layout', 'auto');
        }
    },

    getTextWidth: function () {
        var me = this,
            owner = me.owner,
            inputEl = owner.inputEl,
            value;

        
        value = (inputEl.dom.value || (owner.hasFocus ? '' : owner.emptyText) || '') + owner.growAppend;
        return inputEl.getTextWidth(value);
    },
    
    publishOwnerWidth: function(ownerContext, width) {
        var owner = this.owner;
        this.callParent(arguments);
        if (!owner.grow && !owner.inputWidth) {
            width -= this.getExtraWidth(ownerContext);
            if (owner.labelAlign != 'top') {
                width -= owner.getLabelWidth();
            }
            ownerContext.inputContext.setWidth(width);
        }    
    },

    publishInnerHeight: function(ownerContext, height) {
        ownerContext.inputContext.setHeight(height - this.measureLabelErrorHeight(ownerContext));
    },

    measureContentWidth: function (ownerContext) {
        var me = this,
            owner = me.owner,
            width = me.callParent(arguments),
            inputContext = ownerContext.inputContext,
            calcWidth, max, min;

        if (owner.grow && !ownerContext.state.growHandled) {
            calcWidth = me.getTextWidth() + ownerContext.inputContext.getFrameInfo().width;

            max = owner.growMax;
            min = Math.min(max, width);
            max = Math.max(owner.growMin, max, min);

            
            calcWidth = Ext.Number.constrain(calcWidth, owner.growMin, max);
            inputContext.setWidth(calcWidth);
            ownerContext.state.growHandled = true;
            
            
            inputContext.domBlock(me, 'width');
            width = NaN;
        } else if (!owner.inputWidth) {
            width -= me.getExtraWidth(ownerContext);
        }
        return width;
    },

    updateEditState: function() {
        var me = this,
            owner = me.owner,
            inputEl = owner.inputEl,
            noeditCls = Ext.baseCSSPrefix + 'trigger-noedit',
            displayed,
            readOnly;

        if (me.owner.readOnly) {
            inputEl.addCls(noeditCls);
            readOnly = true;
            displayed = false;
        } else {
            if (me.owner.editable) {
                inputEl.removeCls(noeditCls);
                readOnly = false;
            } else {
                inputEl.addCls(noeditCls);
                readOnly = true;
            }
            displayed = !me.owner.hideTrigger;
        }

        owner.triggerCell.setDisplayed(displayed);
        inputEl.dom.readOnly = readOnly;
    }
});


Ext.define('Ext.form.field.Trigger', {
    extend: Ext.form.field.Text ,
    alias: ['widget.triggerfield', 'widget.trigger'],
                                                                                                 
    alternateClassName: ['Ext.form.TriggerField', 'Ext.form.TwinTriggerField', 'Ext.form.Trigger'],

    childEls: [
        
        { name: 'triggerCell', select: '.' + Ext.baseCSSPrefix + 'trigger-cell' },
        { name: 'triggerEl', select: '.' + Ext.baseCSSPrefix + 'form-trigger' },

        
        'triggerWrap',

        
        'inputCell'
    ],

    

    
    triggerBaseCls: Ext.baseCSSPrefix + 'form-trigger',

    
    triggerWrapCls: Ext.baseCSSPrefix + 'form-trigger-wrap',

    
    triggerNoEditCls: Ext.baseCSSPrefix + 'trigger-noedit',

    
    hideTrigger: false,

    
    editable: true,

    
    readOnly: false,

    

    
    repeatTriggerClick: false,


    
    autoSize: Ext.emptyFn,
    
    monitorTab: true,
    
    mimicing: false,
    
    triggerIndexRe: /trigger-index-(\d+)/,
    
    extraTriggerCls: '',

    componentLayout: 'triggerfield',

    initComponent: function() {
        this.wrapFocusCls = this.triggerWrapCls + '-focus';
        this.callParent(arguments);
    },

    getSubTplMarkup: function(values) {
        var me = this,
            childElCls = values.childElCls, 
            field = me.callParent(arguments);

        return '<table id="' + me.id + '-triggerWrap" class="' + Ext.baseCSSPrefix + 'form-trigger-wrap' + childElCls + '" cellpadding="0" cellspacing="0"><tbody><tr>' +
            '<td id="' + me.id + '-inputCell" class="' + Ext.baseCSSPrefix + 'form-trigger-input-cell' + childElCls + '">' + field + '</td>' +
            me.getTriggerMarkup() +
            '</tr></tbody></table>';
    },
    
    getSubTplData: function(){
        var me = this,
            data = me.callParent(),
            readOnly = me.readOnly === true,
            editable = me.editable !== false;
        
        return Ext.apply(data, {
            editableCls: (readOnly || !editable) ? ' ' + me.triggerNoEditCls : '',
            readOnly: !editable || readOnly
        });  
    },

    getLabelableRenderData: function() {
        var me = this,
            triggerWrapCls = me.triggerWrapCls,
            result = me.callParent(arguments);

        return Ext.applyIf(result, {
            triggerWrapCls: triggerWrapCls,
            triggerMarkup: me.getTriggerMarkup()
        });
    },

    getTriggerMarkup: function() {
        var me = this,
            i = 0,
            hideTrigger = (me.readOnly || me.hideTrigger),
            triggerCls,
            triggerBaseCls = me.triggerBaseCls,
            triggerConfigs = [],
            unselectableCls = Ext.dom.Element.unselectableCls,
            style = 'width:' + me.triggerWidth + 'px;' + (hideTrigger ? 'display:none;' : ''),
            cls = me.extraTriggerCls + ' ' + Ext.baseCSSPrefix + 'trigger-cell ' + unselectableCls;

        
        
        
        
        if (!me.trigger1Cls) {
            me.trigger1Cls = me.triggerCls;
        }

        
        for (i = 0; (triggerCls = me['trigger' + (i + 1) + 'Cls']) || i < 1; i++) {
            triggerConfigs.push({
                tag: 'td',
                valign: 'top',
                cls: cls,
                style: style,
                cn: {
                    cls: [Ext.baseCSSPrefix + 'trigger-index-' + i, triggerBaseCls, triggerCls].join(' '),
                    role: 'button'
                }
            });
        }
        triggerConfigs[0].cn.cls += ' ' + triggerBaseCls + '-first';

        return Ext.DomHelper.markup(triggerConfigs);
    },
    
    disableCheck: function() {
        return !this.disabled;    
    },

    
    beforeRender: function() {
        var me = this,
            triggerBaseCls = me.triggerBaseCls,
            tempEl;
            
        
        if (!me.triggerWidth) {
            tempEl = Ext.getBody().createChild({
                style: 'position: absolute;', 
                cls: Ext.baseCSSPrefix + 'form-trigger'
            });
            Ext.form.field.Trigger.prototype.triggerWidth = tempEl.getWidth();
            tempEl.remove();
        }

        me.callParent();

        if (triggerBaseCls != Ext.baseCSSPrefix + 'form-trigger') {
            
            
            me.addChildEls({ name: 'triggerEl', select: '.' + triggerBaseCls });
        }

        
        me.lastTriggerStateFlags = me.getTriggerStateFlags();
    },

    onRender: function() {
        var me = this;

        me.callParent(arguments);

        me.doc = Ext.getDoc();
        me.initTrigger();
    },

    
    getTriggerWidth: function() {
        var me = this,
            totalTriggerWidth = 0;

        if (me.triggerWrap && !me.hideTrigger && !me.readOnly) {
            totalTriggerWidth = me.triggerEl.getCount() * me.triggerWidth;
        }
        return totalTriggerWidth;
    },

    setHideTrigger: function(hideTrigger) {
        if (hideTrigger != this.hideTrigger) {
            this.hideTrigger = hideTrigger;
            this.updateLayout();
        }
    },

    
    setEditable: function(editable) {
        if (editable != this.editable) {
            this.editable = editable;
            this.updateLayout();
        }
    },

    
    setReadOnly: function(readOnly) {
        var me = this,
            old = me.readOnly;
            
        me.callParent(arguments);
        if (readOnly != old) {
            me.updateLayout();
        }
    },

    
    initTrigger: function() {
        var me = this,
            triggerWrap = me.triggerWrap,
            triggerEl = me.triggerEl,
            disableCheck = me.disableCheck,
            els, eLen, el, e, idx;

        if (me.repeatTriggerClick) {
            me.triggerRepeater = new Ext.util.ClickRepeater(triggerWrap, {
                preventDefault: true,
                handler: me.onTriggerWrapClick,
                listeners: {
                    mouseup: me.onTriggerWrapMouseup,
                    scope: me
                },
                scope: me
            });
        } else {
            me.mon(triggerWrap, {
                click: me.onTriggerWrapClick,
                mouseup: me.onTriggerWrapMouseup,
                scope: me
            });
        }

        triggerEl.setVisibilityMode(Ext.Element.DISPLAY);
        triggerEl.addClsOnOver(me.triggerBaseCls + '-over', disableCheck, me);

        els  = triggerEl.elements;
        eLen = els.length;

        for (e = 0; e < eLen; e++) {
            el = els[e];
            idx = e+1;
            el.addClsOnOver(me['trigger' + (idx) + 'Cls'] + '-over', disableCheck, me);
            el.addClsOnClick(me['trigger' + (idx) + 'Cls'] + '-click', disableCheck, me);
        }

        triggerEl.addClsOnClick(me.triggerBaseCls + '-click', disableCheck, me);

    },

    
    onDestroy: function() {
        var me = this;
        Ext.destroyMembers(me, 'triggerRepeater', 'triggerWrap', 'triggerEl');
        delete me.doc;
        me.callParent();
    },

    
    onFocus: function() {
        var me = this;
        me.callParent(arguments);
        if (!me.mimicing) {
            me.bodyEl.addCls(me.wrapFocusCls);
            me.mimicing = true;
            me.mon(me.doc, 'mousedown', me.mimicBlur, me, {
                delay: 10
            });
            if (me.monitorTab) {
                me.on('specialkey', me.checkTab, me);
            }
        }
    },

    
    checkTab: function(me, e) {
        if (!this.ignoreMonitorTab && e.getKey() == e.TAB) {
            this.triggerBlur();
        }
    },

    
    getTriggerStateFlags: function () {
        var me = this,
            state = 0;

        if (me.readOnly) {
            state += 1;
        }
        if (me.editable) {
            state += 2;
        }
        if (me.hideTrigger) {
            state += 4;
        }
        return state;
    },

    
    onBlur: Ext.emptyFn,

    
    mimicBlur: function(e) {
        if (!this.isDestroyed && !this.bodyEl.contains(e.target) && this.validateBlur(e)) {
            this.triggerBlur(e);
        }
    },

    
    triggerBlur: function(e) {
        var me = this;
        me.mimicing = false;
        me.mun(me.doc, 'mousedown', me.mimicBlur, me);
        if (me.monitorTab && me.inputEl) {
            me.un('specialkey', me.checkTab, me);
        }
        Ext.form.field.Trigger.superclass.onBlur.call(me, e);
        if (me.bodyEl) {
            me.bodyEl.removeCls(me.wrapFocusCls);
        }
    },

    
    
    validateBlur: function(e) {
        return true;
    },

    
    
    
    onTriggerWrapClick: function() {
        var me = this,
            targetEl, match,
            triggerClickMethod,
            event;

        event = arguments[me.triggerRepeater ? 1 : 0];
        if (event && !me.readOnly && !me.disabled) {
                targetEl = event.getTarget('.' + me.triggerBaseCls, null);
                match = targetEl && targetEl.className.match(me.triggerIndexRe);

            if (match) {
                triggerClickMethod = me['onTrigger' + (parseInt(match[1], 10) + 1) + 'Click'] || me.onTriggerClick;
                if (triggerClickMethod) {
                    triggerClickMethod.call(me, event);
                }
            }
        }
    },

    
    
    
    onTriggerWrapMouseup: Ext.emptyFn,

    
    onTriggerClick: Ext.emptyFn

    
    
    
});


Ext.define('Ext.form.field.Picker', {
    extend:  Ext.form.field.Trigger ,
    alias: 'widget.pickerfield',
    alternateClassName: 'Ext.form.Picker',
                                  

    
    matchFieldWidth: true,

    
    pickerAlign: 'tl-bl?',

    

    
    openCls: Ext.baseCSSPrefix + 'pickerfield-open',

    

    
    editable: true,


    initComponent: function() {
        this.callParent();

        
        this.addEvents(
            
            'expand',
            
            'collapse',
            
            'select'
        );
    },


    initEvents: function() {
        var me = this;
        me.callParent();

        
        me.keyNav = new Ext.util.KeyNav(me.inputEl, {
            down: me.onDownArrow,
            esc: {
                handler: me.onEsc,
                scope: me,
                defaultEventAction: false
            },
            scope: me,
            forceKeyDown: true
        });

        
        if (!me.editable) {
            me.mon(me.inputEl, 'click', me.onTriggerClick, me);
        }

        
        if (Ext.isGecko) {
            me.inputEl.dom.setAttribute('autocomplete', 'off');
        }
    },

    
    onEsc: function(e) {
        if (Ext.isIE) {
            
            
            
            
            e.preventDefault();
        }
        
        if (this.isExpanded) {
            this.collapse();
            e.stopEvent();
        }
    },

    onDownArrow: function(e) {
        if (!this.isExpanded) {
            
            
            this.onTriggerClick();
        }
    },

    
    expand: function() {
        var me = this,
            bodyEl, picker, collapseIf;

        if (me.rendered && !me.isExpanded && !me.isDestroyed) {
            me.expanding = true;
            bodyEl = me.bodyEl;
            picker = me.getPicker();
            collapseIf = me.collapseIf;

            
            picker.show();
            me.isExpanded = true;
            me.alignPicker();
            bodyEl.addCls(me.openCls);

            
            me.mon(Ext.getDoc(), {
                mousewheel: collapseIf,
                mousedown: collapseIf,
                scope: me
            });
            Ext.EventManager.onWindowResize(me.alignPicker, me);
            me.fireEvent('expand', me);
            me.onExpand();
            delete me.expanding;
        }
    },

    onExpand: Ext.emptyFn,

    
    alignPicker: function() {
        var me = this,
            picker = me.getPicker();

        if (me.isExpanded) {
            if (me.matchFieldWidth) {
                
                picker.setWidth(me.bodyEl.getWidth());
            }
            if (picker.isFloating()) {
                me.doAlign();
            }
        }
    },

    
    doAlign: function(){
        var me = this,
            picker = me.picker,
            aboveSfx = '-above',
            isAbove;

        
        
        me.picker.alignTo(me.triggerWrap, me.pickerAlign, me.pickerOffset);
        
        
        isAbove = picker.el.getY() < me.inputEl.getY();
        me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx);
        picker[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx);
    },

    
    collapse: function() {
        if (this.isExpanded && !this.isDestroyed) {
            var me = this,
                openCls = me.openCls,
                picker = me.picker,
                doc = Ext.getDoc(),
                collapseIf = me.collapseIf,
                aboveSfx = '-above';

            
            picker.hide();
            me.isExpanded = false;

            
            me.bodyEl.removeCls([openCls, openCls + aboveSfx]);
            picker.el.removeCls(picker.baseCls + aboveSfx);

            
            doc.un('mousewheel', collapseIf, me);
            doc.un('mousedown', collapseIf, me);
            Ext.EventManager.removeResizeListener(me.alignPicker, me);
            me.fireEvent('collapse', me);
            me.onCollapse();
        }
    },

    onCollapse: Ext.emptyFn,


    
    collapseIf: function(e) {
        var me = this;

        if (!me.isDestroyed && !e.within(me.bodyEl, false, true) && !e.within(me.picker.el, false, true) && !me.isEventWithinPickerLoadMask(e)) {
            me.collapse();
        }
    },

    
    getPicker: function() {
        var me = this;
        return me.picker || (me.picker = me.createPicker());
    },

    
    createPicker: Ext.emptyFn,

    
    onTriggerClick: function() {
        var me = this;
        if (!me.readOnly && !me.disabled) {
            if (me.isExpanded) {
                me.collapse();
            } else {
                me.expand();
            }
            me.inputEl.focus();
        }
    },
    
    triggerBlur: function() {
        var picker = this.picker;
            
        this.callParent(arguments);
        if (picker && picker.isVisible()) {
            picker.hide();
        }
    },

    mimicBlur: function(e) {
        var me = this,
            picker = me.picker;
        
        if (!picker || !e.within(picker.el, false, true) && !me.isEventWithinPickerLoadMask(e)) {
            me.callParent(arguments);
        }
    },

    onDestroy : function(){
        var me = this,
            picker = me.picker;

        Ext.EventManager.removeResizeListener(me.alignPicker, me);
        Ext.destroy(me.keyNav);
        if (picker) {
            delete picker.pickerField;
            picker.destroy();
        }
        me.callParent();
    },

    
    isEventWithinPickerLoadMask: function(e) {
        var loadMask = this.picker.loadMask;

        return loadMask ? e.within(loadMask.maskEl, false, true) || e.within(loadMask.el, false, true) : false;
    }

});



Ext.define('Ext.selection.Model', {
    extend:  Ext.util.Observable ,
    alternateClassName: 'Ext.AbstractSelectionModel',
                                        
    mixins: {
        bindable:  Ext.util.Bindable     
    },
    

    

    
    allowDeselect: undefined,
    
    
    toggleOnClick: true,

    
    selected: null,

    
    pruneRemoved: true,
    
    suspendChange: 0,

    constructor: function(cfg) {
        var me = this;

        cfg = cfg || {};
        Ext.apply(me, cfg);

        me.addEvents(
            
            'selectionchange',
            
            'focuschange'
        );

        me.modes = {
            SINGLE: true,
            SIMPLE: true,
            MULTI: true
        };

        
        me.setSelectionMode(cfg.mode || me.mode);

        
        me.selected = new Ext.util.MixedCollection(null, me.getSelectionId);

        me.callParent(arguments);
    },

    
    bindStore: function(store, initial){
        var me = this;
        me.mixins.bindable.bindStore.apply(me, arguments);
        if(me.store && !initial) {
            me.refresh();
        }
    },

    getStoreListeners: function() {
        var me = this;
        return {
            add: me.onStoreAdd,
            clear: me.onStoreClear,
            bulkremove: me.onStoreRemove,
            update: me.onStoreUpdate,
            load: me.onStoreLoad,
            idchanged: me.onModelIdChanged,
            refresh: me.onStoreRefresh
        };
    },
    
    suspendChanges: function(){
        ++this.suspendChange;
    },
    
    resumeChanges: function(){
        if (this.suspendChange) {
            --this.suspendChange;
        }
    },

    
    selectAll: function(suppressEvent) {
        var me = this,
            selections = me.store.getRange(),
            i = 0,
            len = selections.length,
            start = me.getSelection().length;

        me.suspendChanges();
        for (; i < len; i++) {
            me.doSelect(selections[i], true, suppressEvent);
        }
        me.resumeChanges();
        
        if (!suppressEvent) {
            me.maybeFireSelectionChange(me.getSelection().length !== start);
        }
    },

    
    deselectAll: function(suppressEvent) {
        var me = this,
            selections = me.getSelection(),
            selIndexes = {},
            store = me.store,
            start = selections.length,
            i, l, rec;

        
        
        
        
        
        
        for (i = 0, l = selections.length; i < l; i++) {
            rec = selections[i];
            
            selIndexes[rec.internalId] = store.indexOf(rec);
        }
        
        
        
        selections = Ext.Array.sort(selections, function(r1, r2){
            var idx1 = selIndexes[r1.internalId],
                idx2 = selIndexes[r2.internalId];
            
            
            return idx1 < idx2 ? -1 : 1;
        });
        
        me.suspendChanges();
        me.doDeselect(selections, suppressEvent);
        me.resumeChanges();
        
        if (!suppressEvent) {
            me.maybeFireSelectionChange(me.getSelection().length !== start);
        }
    },

    
    
    
    selectWithEvent: function(record, e) {
        var me = this,
            isSelected = me.isSelected(record),
            shift = e.shiftKey,
            ctrl = e.ctrlKey,
            start = me.selectionStart,
            selected = me.getSelection(),
            len = selected.length,
            allowDeselect = me.allowDeselect,
            toDeselect, i, item;

        switch (me.selectionMode) {
            case 'MULTI':
                if (shift && start) {
                    me.selectRange(start, record, ctrl);
                } else if (ctrl && isSelected) {
                    me.doDeselect(record, false);
                } else if (ctrl) {
                    me.doSelect(record, true, false);
                } else if (isSelected && !shift && !ctrl && len > 1) {
                    toDeselect = [];
                    
                    for (i = 0; i < len; ++i) {
                        item = selected[i];
                        if (item !== record) {
                            toDeselect.push(item);    
                        }
                    }
                    
                    me.doDeselect(toDeselect);
                } else if (!isSelected) {
                    me.doSelect(record, false);
                }
                break;
            case 'SIMPLE':
                if (isSelected) {
                    me.doDeselect(record);
                } else {
                    me.doSelect(record, true);
                }
                break;
            case 'SINGLE':
                if (allowDeselect && !ctrl) {
                    allowDeselect = me.toggleOnClick;
                }
                if (allowDeselect && isSelected) {
                    me.doDeselect(record);
                } else {
                    me.doSelect(record, false);
                }
                break;
        }

        
        
        
        
        if (!shift) {
            if (me.isSelected(record)) {
                me.selectionStart = record;
            } else {
                me.selectionStart = null;
            }
        }
    },

    
    
    
    afterKeyNavigate: function(e, record) {
        var me = this,
            recIdx,
            fromIdx,
            isSelected = me.isSelected(record),
            from = (me.selectionStart && me.isSelected(me.lastFocused)) ? me.selectionStart : (me.selectionStart = me.lastFocused),
            key = e.getCharCode(),
            isSpace = key === e.SPACE,
            direction = key === e.UP || key === e.PAGE_UP ? 'up' : (key === e.DOWN || key === e.DOWN ? 'down' : null);

        switch (me.selectionMode) {
            case 'MULTI':

                if (isSpace) {
                    
                    if (e.shiftKey) {
                        me.selectRange(from, record, e.ctrlKey);
                    } else {
                        
                        
                        if (isSelected) {
                            me.doDeselect(record, e.ctrlKey);

                            
                            
                            me.setLastFocused(null);
                            me.setLastFocused(record);
                        }
                        
                        else {
                            me.doSelect(record, e.ctrlKey);
                        }
                    }
                }

                
                else if (e.shiftKey && from) {

                    
                    fromIdx = me.store.indexOf(from);
                    recIdx = me.store.indexOf(record);

                    
                    if (direction === 'up' && fromIdx <= recIdx) {
                        me.deselectRange(me.lastFocused, recIdx + 1);
                    }
                    else if (direction === 'down' && fromIdx >= recIdx) {
                        me.deselectRange(me.lastFocused, recIdx - 1);
                    }

                    
                    else if (from !== record) {
                        me.selectRange(from, record, e.ctrlKey);
                    }
                    me.lastSelected = record;
                    me.setLastFocused(record);
                }

                
                else if (e.ctrlKey && isSelected) {
                    me.setLastFocused(record);
                }

                
                else if (e.ctrlKey) {
                    me.setLastFocused(record);
                }

                
                else {
                    me.doSelect(record, false);
                }
                break;
            case 'SIMPLE':
                if (isSelected) {
                    me.doDeselect(record);
                } else {
                    me.doSelect(record, true);
                }
                break;
            case 'SINGLE':
                
                if (isSpace) {
                    if (isSelected) {
                        me.doDeselect(record);
                        me.setLastFocused(record);
                    } else {
                        me.doSelect(record);
                    }
                }

                
                else if (e.ctrlKey) {
                    me.setLastFocused(record);
                }

                
                else if (me.allowDeselect && isSelected) {
                    me.doDeselect(record);
                }

                
                else {
                    me.doSelect(record, false);
                }
                break;
        }

        
        
        
        
        if (!e.shiftKey) {
            if (me.isSelected(record)) {
                me.selectionStart = record;
            }
        }
    },

    
    selectRange : function(startRow, endRow, keepExisting) {
        var me = this,
            store = me.store,
            selected = me.selected.items,
            result, i, len, toSelect, toDeselect, idx, rec;

        if (me.isLocked()){
            return;
        }

        result = me.normalizeRowRange(startRow, endRow);
        startRow = result[0];
        endRow = result[1];

        toSelect = [];
        for (i = startRow; i <= endRow; i++){
            if (!me.isSelected(store.getAt(i))) {
                toSelect.push(store.getAt(i));
            }
        }
        
        if (!keepExisting) {
            
            toDeselect = [];
            me.suspendChanges();
            
            for (i = 0, len = selected.length; i < len; ++i) {
                rec = selected[i];
                idx = store.indexOf(rec);
                if (idx < startRow || idx > endRow) {
                    toDeselect.push(rec)
                }
            }
            
            for (i = 0, len = toDeselect.length; i < len; ++i) {
                me.doDeselect(toDeselect[i]);
            }
            me.resumeChanges();
        }
        
        me.doMultiSelect(toSelect, true);
    },

    
    deselectRange : function(startRow, endRow) {
        var me = this,
            store = me.store,
            result, i, toDeselect, record;

        if (me.isLocked()){
            return;
        }

        result = me.normalizeRowRange(startRow, endRow);
        startRow = result[0];
        endRow = result[1];

        toDeselect = [];
        for (i = startRow; i <= endRow; i++) {
            record = store.getAt(i);
            if (me.isSelected(record)) {
                toDeselect.push(record);
            }
        }
        me.doDeselect(toDeselect);
    },
    
    normalizeRowRange: function(startRow, endRow) {
        var store = this.store,
            tmp;
        
        if (!Ext.isNumber(startRow)) {
            startRow = store.indexOf(startRow);
        }
        startRow = Math.max(0, startRow);
        
        if (!Ext.isNumber(endRow)) {
            endRow = store.indexOf(endRow);
        }
        endRow = Math.min(endRow, store.getCount() - 1);
        
        
        if (startRow > endRow){
            tmp = endRow;
            endRow = startRow;
            startRow = tmp;
        }    
        
        return [startRow, endRow];
    },

    onModelIdChanged: function(store, model, oldId, newId, oldInternalId) {
        this.selected.updateKey(oldInternalId, newId);
    },

    
    select: function(records, keepExisting, suppressEvent) {
        
        if (Ext.isDefined(records)) {
            this.doSelect(records, keepExisting, suppressEvent);
        }
    },

    
    deselect: function(records, suppressEvent) {
        this.doDeselect(records, suppressEvent);
    },

    doSelect: function(records, keepExisting, suppressEvent) {
        var me = this,
            record;

        if (me.locked || !me.store) {
            return;
        }
        if (typeof records === "number") {
            record = me.store.getAt(records);
            
            if (!record) {
                return;
            }
            records = [record];
        }
        if (me.selectionMode == "SINGLE" && records) {
            record = records.length ? records[0] : records;
            me.doSingleSelect(record, suppressEvent);
        } else {
            me.doMultiSelect(records, keepExisting, suppressEvent);
        }
    },

    doMultiSelect: function(records, keepExisting, suppressEvent) {
        var me = this,
            selected = me.selected,
            change = false,
            result, i, len, record, commit;

        if (me.locked) {
            return;
        }

        records = !Ext.isArray(records) ? [records] : records;
        len = records.length;
        if (!keepExisting && selected.getCount() > 0) {
            result = me.deselectDuringSelect(records, selected.getRange(), suppressEvent);
            if (result[0]) {
                
                
                me.maybeFireSelectionChange(result[1] > 0 && !suppressEvent);
                return;
            }
        }

        commit = function() {
            selected.add(record);
            change = true;
        };

        for (i = 0; i < len; i++) {
            record = records[i];
            if (me.isSelected(record)) {
                continue;
            }
            me.lastSelected = record;

            me.onSelectChange(record, true, suppressEvent, commit);
        }
        if (!me.preventFocus) {
            me.setLastFocused(record, suppressEvent);
        }
        
        me.maybeFireSelectionChange(change && !suppressEvent);
    },
    
    deselectDuringSelect: function(toSelect, selected, suppressEvent) {
        var me = this,
            len = selected.length,
            changed = 0,
            failed = false,
            item, i;
            
        
        me.suspendChanges();
        for (i = 0; i < len; ++i) {
            item = selected[i];
            if (!Ext.Array.contains(toSelect, item)) {
                if (me.doDeselect(item, suppressEvent)) {
                    ++changed;
                } else {
                    failed = true;
                }
            }
        }
        me.resumeChanges();
        
        return [failed, changed];
    },

    
    doDeselect: function(records, suppressEvent) {
        var me = this,
            selected = me.selected,
            i = 0,
            len, record,
            attempted = 0,
            accepted = 0,
            commit;

        if (me.locked || !me.store) {
            return false;
        }

        if (typeof records === "number") {
            
            record = me.store.getAt(records);
            if (!record) {
                return false;
            }
            records = [record];
        } else if (!Ext.isArray(records)) {
            records = [records];
        }

        commit = function() {
            ++accepted;
            selected.remove(record);
        };

        len = records.length;

        me.suspendChanges();
        for (; i < len; i++) {
            record = records[i];
            if (me.isSelected(record)) {
                if (me.lastSelected === record) {
                    me.lastSelected = selected.last();
                    if (me.lastFocused === record) {
                        me.setLastFocused(null);
                    }
                }
                ++attempted;
                me.onSelectChange(record, false, suppressEvent, commit);
            }
        }
        me.resumeChanges();

        
        me.maybeFireSelectionChange(accepted > 0 && !suppressEvent);
        return accepted === attempted;
    },

    doSingleSelect: function(record, suppressEvent) {
        var me = this,
            changed = false,
            selected = me.selected,
            commit;

        if (me.locked) {
            return;
        }
        
        
        if (me.isSelected(record)) {
            return;
        }
        
        if (selected.getCount()) {
            me.suspendChanges();
            if (!me.doDeselect(me.lastSelected, suppressEvent)) {
                me.resumeChanges();
                return;
            }
            me.resumeChanges();
        }

        commit = function() {
            selected.add(record);
            me.lastSelected = record;
            changed = true;
        };

        me.onSelectChange(record, true, suppressEvent, commit);

        if (changed) {
            if (!suppressEvent && !me.preventFocus) {
                me.setLastFocused(record);
            }
            me.maybeFireSelectionChange(!suppressEvent);
        }
    },

    
    setLastFocused: function(record, supressFocus) {
        var me = this,
            recordBeforeLast = me.lastFocused;

        
        if (record !== recordBeforeLast) {
            me.lastFocused = record;
            me.onLastFocusChanged(recordBeforeLast, record, supressFocus);
        }
    },

    
    isFocused: function(record) {
        return record === this.getLastFocused();
    },

    
    
    maybeFireSelectionChange: function(fireEvent) {
        var me = this;
        if (fireEvent && !me.suspendChange) {
            me.fireEvent('selectionchange', me, me.getSelection());
        }
    },

    
    getLastSelected: function() {
        return this.lastSelected;
    },

    getLastFocused: function() {
        return this.lastFocused;
    },

    
    getSelection: function() {
        return this.selected.getRange();
    },

    
    getSelectionMode: function() {
        return this.selectionMode;
    },

    
    setSelectionMode: function(selMode) {
        selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
        
        
        this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
    },

    
    isLocked: function() {
        return this.locked;
    },

    
    setLocked: function(locked) {
        this.locked = !!locked;
    },

    
    isRangeSelected: function(startRow, endRow) {
        var me = this,
            store = me.store,
            i, result;

        result = me.normalizeRowRange(startRow, endRow);
        startRow = result[0];
        endRow = result[1];

        
        for (i = startRow; i <= endRow; i++) {
            if (!me.isSelected(store.getAt(i))) {
                return false;
            }
        }
        return true;
    },

    
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.store.getAt(record) : record;
        return this.selected.contains(record);
    },

    
    hasSelection: function() {
        return this.selected.getCount() > 0;
    },

    getSelectionId: function(record){
        return record.internalId;
    },

    pruneIf: function() {
        var me = this,
            selected = me.selected,
            toRemove = [],
            len = selected.length,
            i, item;

        if (me.pruneRemoved) {
            for (i = 0; i < len; i++) {
                item = selected.getAt(i);
                if (!this.storeHasSelected(item)) {
                    toRemove.push(item);
                }
            }
            if (toRemove.length) {
                for (i = 0, len = toRemove.length; i < len; i++) {
                    selected.remove(toRemove[i]);
                }
                me.maybeFireSelectionChange(true);
            }
        }
    },

    
    
    
    storeHasSelected: function(record) {
        var store = this.store,
            records,
            len, id, i;

        if (record.hasId() && store.getById(record)) {
            return true;
        } else {
            records = store.data.items;
            len = records.length;
            id = record.internalId;

            for (i = 0; i < len; ++i) {
                if (id === records[i].internalId) {
                    return true;
                }
            }
        }
        return false;
    },

    refresh: function() {
        var me = this,
            store = me.store,
            rec,
            toBeSelected = [],
            toBeReAdded = [],
            oldSelections = me.getSelection(),
            len = oldSelections.length,
            selection,
            change,
            i = 0,
            lastFocused = me.getLastFocused();

        
        if (!store) {
            return;
        }

        
        
        for (; i < len; i++) {
            selection = oldSelections[i];
            if (store.indexOf(selection) !== -1) {
                toBeSelected.push(selection);
            }

            
            else if (!me.pruneRemoved) {
                
                rec = store.getById(selection.getId());
                if (rec) {
                    toBeSelected.push(rec);
                }
                
                else {
                    toBeReAdded.push(selection)
                }
            }

            
            if (me.mode === 'SINGLE' && toBeReAdded.length) {
                break;
            }
        }

        
        
        if (me.selected.getCount() != (toBeSelected.length + toBeReAdded.length)) {
            change = true;
        }

        me.clearSelections();

        if (store.indexOf(lastFocused) !== -1) {
            
            me.setLastFocused(lastFocused, true);
        }

        if (toBeSelected.length) {
            
            me.doSelect(toBeSelected, false, true);
        }

        
        if (toBeReAdded.length) {
            me.selected.addAll(toBeReAdded);

            
            if (!me.lastSelected) {
                me.lastSelected = toBeReAdded[toBeReAdded.length - 1];
            }
        }

        me.maybeFireSelectionChange(change);
    },

    
    clearSelections: function() {
        
        this.selected.clear();
        this.lastSelected = null;
        this.setLastFocused(null);
    },

    
    onStoreAdd: Ext.emptyFn,

    
    
    onStoreClear: function() {
        if (this.selected.getCount() > 0) {
            this.clearSelections();
            this.maybeFireSelectionChange(true);
        }
    },

    
    
    
    onStoreRemove: function(store, records, indexes, isMove) {
        var me = this;

        
        if (me.selectionStart && Ext.Array.contains(records, me.selectionStart)) {
            me.selectionStart = null;
        }

        if (isMove || me.locked || !me.pruneRemoved) {
            return;
        }
        me.deselectDeletedRecords(records);
    },

    
    
    deselectDeletedRecords: function(records) {
        var me = this,
            selected = me.selected,
            i, length = records.length,
            removed = 0,
            record;

        
        for (i = 0; i < length; i++) {
            record = records[i];
            if (selected.remove(record)) {
                if (me.lastSelected == record) {
                    me.lastSelected = null;
                }
                if (me.getLastFocused() == record) {
                    me.setLastFocused(null);
                }
                ++removed;
            }
        }
        if (removed) {
            me.maybeFireSelectionChange(true);
        }
    },

    
    getCount: function() {
        return this.selected.getCount();
    },

    
    onUpdate: Ext.emptyFn,

    
    destroy: function(){
        this.clearListeners();    
    },

    
    onStoreUpdate: Ext.emptyFn,

    onStoreRefresh: Ext.emptyFn,

    
    onStoreLoad: Ext.emptyFn,

    
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            eventName = isSelected ? 'select' : 'deselect';

        if ((suppressEvent || me.fireEvent('before' + eventName, me, record)) !== false &&
           commitFn() !== false) {

            if (!suppressEvent) {
                me.fireEvent(eventName, me, record);
            }
        }   
    },

    
    onLastFocusChanged: function(oldFocused, newFocused) {
        this.fireEvent('focuschange', this, oldFocused, newFocused);
    },

    
    onEditorKey: Ext.emptyFn,

    
    beforeViewRender: function(view) {
        this.views = this.views || [];
        this.views.push(view);
        this.bindStore(view.getStore(), true);
    },

    
    bindComponent: Ext.emptyFn
});


Ext.define('Ext.selection.DataViewModel', {
    extend:  Ext.selection.Model ,

                                  

    deselectOnContainerClick: true,

    
    enableKeyNav: true,

    constructor: function(cfg){
        this.addEvents(
            
            'beforedeselect',

            
            'beforeselect',

            
            'deselect',

            
            'select'
        );
        this.callParent(arguments);
    },

    bindComponent: function(view) {
        var me = this,
            eventListeners = {
                refresh: me.refresh,
                scope: me
            };

        me.view = view;
        me.bindStore(view.getStore());

        eventListeners[view.triggerEvent] = me.onItemClick;
        eventListeners[view.triggerCtEvent] = me.onContainerClick;

        view.on(eventListeners);

        if (me.enableKeyNav) {
            me.initKeyNav(view);
        }
    },
    
    onUpdate: function(record){
        var view = this.view;
        if (view && this.isSelected(record)) {
            view.onItemSelect(record);
        }
    },

    onItemClick: function(view, record, item, index, e) {
        this.selectWithEvent(record, e);
    },

    onContainerClick: function() {
        if (this.deselectOnContainerClick) {
            this.deselectAll();
        }
    },

    initKeyNav: function(view) {
        var me = this;

        if (!view.rendered) {
            view.on({
                render: Ext.Function.bind(me.initKeyNav, me, [view]),
                single: true
            });
            return;
        }

        view.el.set({
            tabIndex: -1
        });
        me.keyNav = new Ext.util.KeyNav({
            target: view.el,
            ignoreInputFields: true,
            down: Ext.pass(me.onNavKey, [1], me),
            right: Ext.pass(me.onNavKey, [1], me),
            left: Ext.pass(me.onNavKey, [-1], me),
            up: Ext.pass(me.onNavKey, [-1], me),
            scope: me
        });
    },

    onNavKey: function(step) {
        step = step || 1;
        var me = this,
            view = me.view,
            selected = me.getSelection()[0],
            numRecords = me.view.store.getCount(),
            idx;

        if (selected) {
            idx = view.indexOf(view.getNode(selected)) + step;
        } else {
            idx = 0;
        }

        if (idx < 0) {
            idx = numRecords - 1;
        } else if (idx >= numRecords) {
            idx = 0;
        }

        me.select(idx);
    },

    
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            view = me.view,
            eventName = isSelected ? 'select' : 'deselect';

        if ((suppressEvent || me.fireEvent('before' + eventName, me, record)) !== false &&
                commitFn() !== false) {

            if (view) {
                if (isSelected) {
                    view.onItemSelect(record);
                } else {
                    view.onItemDeselect(record);
                }
            }

            if (!suppressEvent) {
                me.fireEvent(eventName, me, record);
            }
        }
    },
    
    onLastFocusChanged: function(oldFocus, newFocus, suppressFocus){
        var view = this.view;
        if (view && !suppressFocus && newFocus) {
            view.focusNode(newFocus);
            this.fireEvent('focuschange', this, oldFocus, newFocus);
        }
    },
    
    destroy: function(){
        Ext.destroy(this.keyNav);
        this.callParent();
    }
});


Ext.define('Ext.view.AbstractView', {
    extend:  Ext.Component ,
               
                       
                                
                                   
                       
                                     
      
    mixins: {
        bindable:  Ext.util.Bindable 
    },

    inheritableStatics: {
        getRecord: function(node) {
            return this.getBoundView(node).getRecord(node);
        },

        getBoundView: function(node) {
            return Ext.getCmp(node.boundView);
        }
    },

    
    

    
    deferInitialRefresh: true,

    

    
    itemCls: Ext.baseCSSPrefix + 'dataview-item',

    

    

    
    
    loadingText: 'Loading...',
    

    
    loadMask: true,

    

    
    loadingUseMsg: true,


    

    
    selectedItemCls: Ext.baseCSSPrefix + 'item-selected',

    
    
    emptyText: "",
    

    
    deferEmptyText: true,

    
    trackOver: false,

    
    blockRefresh: false,

    

    
    preserveScrollOnRefresh: false,

    
    last: false,

    triggerEvent: 'itemclick',
    triggerCtEvent: 'containerclick',

    addCmpEvents: function() {

    },

    
    initComponent : function(){
        var me = this,
            isDef = Ext.isDefined,
            itemTpl = me.itemTpl,
            memberFn = {};

        if (itemTpl) {
            if (Ext.isArray(itemTpl)) {
                
                itemTpl = itemTpl.join('');
            } else if (Ext.isObject(itemTpl)) {
                
                memberFn = Ext.apply(memberFn, itemTpl.initialConfig);
                itemTpl = itemTpl.html;
            }

            if (!me.itemSelector) {
                me.itemSelector = '.' + me.itemCls;
            }

            itemTpl = Ext.String.format('<tpl for="."><div class="{0}">{1}</div></tpl>', me.itemCls, itemTpl);
            me.tpl = new Ext.XTemplate(itemTpl, memberFn);
        }


        me.callParent();
        me.tpl = me.getTpl('tpl');

        
        if (me.overItemCls) {
            me.trackOver = true;
        }

        me.addEvents(
            
            'beforerefresh',
            
            'refresh',
            
            'viewready',
            
            'itemupdate',
            
            'itemadd',
            
            'itemremove'
        );

        me.addCmpEvents();

        
        me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');

        
        if (!me.dataSource) {
            me.dataSource = me.store;
        }
        
        
        me.bindStore(me.dataSource, true, 'dataSource');
        if (!me.all) {
            me.all = new Ext.CompositeElementLite();
        }

        
        me.scrollState = {
            top: 0,
            left: 0
        };
        me.on({
            scroll: me.onViewScroll,
            element: 'el',
            scope: me
        });
    },

    onRender: function() {
        var me = this,
            mask = me.loadMask,
            maskStore = me.getMaskStore(),
            cfg = {
                target: me,
                msg: me.loadingText,
                msgCls: me.loadingCls,
                useMsg: me.loadingUseMsg,
                
                
                store: maskStore
            };

        me.callParent(arguments);

        if (mask && !maskStore.proxy.isSynchronous) {
            
            if (Ext.isObject(mask)) {
                cfg = Ext.apply(cfg, mask);
            }
            
            
            
            
            me.loadMask = new Ext.LoadMask(cfg);
            me.loadMask.on({
                scope: me,
                beforeshow: me.onMaskBeforeShow,
                hide: me.onMaskHide
            });
        }
    },
    
    finishRender: function() {
        var me = this;
        me.callParent(arguments);
        
        
        if (!me.up('[collapsed],[hidden]')) {
            me.doFirstRefresh(me.dataSource);
        }
    },

    onBoxReady: function() {
        var me = this;

        me.callParent(arguments);

        
        
        if (!me.firstRefreshDone) {
            me.doFirstRefresh(me.dataSource);
        }
    },

    getMaskStore: function(){
        return this.store;    
    },

    onMaskBeforeShow: function(){
        var me = this,
            loadingHeight = me.loadingHeight;

        if (loadingHeight && loadingHeight > me.getHeight()) {
            me.hasLoadingHeight = true;
            me.oldMinHeight = me.minHeight;
            me.minHeight = loadingHeight;
            me.updateLayout();
        }
    },

    onMaskHide: function(){
        var me = this;

        if (!me.destroying && me.hasLoadingHeight) {
            me.minHeight = me.oldMinHeight;
            me.updateLayout();
            delete me.hasLoadingHeight;
        }
    },

    beforeRender: function() {
        this.callParent(arguments);
        this.getSelectionModel().beforeViewRender(this);
    },

    afterRender: function() {
        this.callParent(arguments);

        
        
        
        this.getSelectionModel().bindComponent(this);
    },

    
    getSelectionModel: function(){
        var me = this,
            mode = 'SINGLE';

        if (me.simpleSelect) {
            mode = 'SIMPLE';
        } else if (me.multiSelect) {
            mode = 'MULTI';
        }

        
        if (!me.selModel || !me.selModel.events) {
            me.selModel = new Ext.selection.DataViewModel(Ext.apply({
                allowDeselect: me.allowDeselect,
                mode: mode
            }, me.selModel));
        }

        if (!me.selModel.hasRelaySetup) {
            me.relayEvents(me.selModel, [
                'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect', 'focuschange'
            ]);
            me.selModel.hasRelaySetup = true;
        }

        
        
        if (me.disableSelection) {
            me.selModel.locked = true;
        }

        return me.selModel;
    },

    
    refresh: function() {
        var me = this,
            targetEl,
            targetParent,
            oldDisplay,
            nextSibling,
            dom,
            records;

        if (!me.rendered || me.isDestroyed) {
            return;
        }

        if (!me.hasListeners.beforerefresh || me.fireEvent('beforerefresh', me) !== false) {
            targetEl = me.getTargetEl();
            records = me.getViewRange();
            dom = targetEl.dom;

            
            
            if (!me.preserveScrollOnRefresh) {
                targetParent = dom.parentNode;
                oldDisplay = dom.style.display;
                dom.style.display = 'none';
                nextSibling = dom.nextSibling;
                targetParent.removeChild(dom);
            }

            if (me.refreshCounter) {
                me.clearViewEl();
            } else {
                me.fixedNodes = targetEl.dom.childNodes.length;
                me.refreshCounter = 1;
            }

            
            
            
            
            me.tpl.append(targetEl, me.collectData(records, me.all.startIndex));

            
            
            if (records.length < 1) {
                
                if (!this.store.loading && (!me.deferEmptyText || me.hasFirstRefresh)) {
                    Ext.core.DomHelper.insertHtml('beforeEnd', targetEl.dom, me.emptyText);
                }
                me.all.clear();
            } else {
                me.collectNodes(targetEl.dom);
                me.updateIndexes(0);
            }

            
            if (me.hasFirstRefresh) {
                
                if (me.refreshSelmodelOnRefresh !== false) {
                    me.selModel.refresh();
                } else {
                    
                    me.selModel.pruneIf();
                }
            }

            me.hasFirstRefresh = true;

            if (!me.preserveScrollOnRefresh) {
                targetParent.insertBefore(dom, nextSibling);
                dom.style.display = oldDisplay;
            }

            
            this.refreshSize();

            me.fireEvent('refresh', me);

            
            
            if (!me.viewReady) {
                
                
                me.viewReady = true;
                me.fireEvent('viewready', me);
            }
        }
    },

    
    
    collectNodes: function(targetEl) {
        this.all.fill(Ext.query(this.getItemSelector(), Ext.getDom(targetEl)), this.all.startIndex);
    },

    getViewRange: function() {
        return this.dataSource.getRange();
    },

    
    refreshSize: function() {
        var sizeModel = this.getSizeModel();
        if (sizeModel.height.shrinkWrap || sizeModel.width.shrinkWrap) {
            this.updateLayout();
        }
    },

    clearViewEl: function(){
        
        
        
        
        
        
        

        var me = this,
            el = me.getTargetEl();

        if (me.fixedNodes) {
            while (el.dom.childNodes[me.fixedNodes]) {
                el.dom.removeChild(el.dom.childNodes[me.fixedNodes]);
            }
        } else {
            el.update('');
        }
        me.refreshCounter++;
    },

    
    onViewScroll: Ext.emptyFn,
    
    onIdChanged: Ext.emptyFn,

    
    saveScrollState: function() {
        if (this.rendered) {
            var dom = this.el.dom,
                state = this.scrollState;

            state.left = dom.scrollLeft;
            state.top = dom.scrollTop;
        }
    },

    
    restoreScrollState: function() {
        if (this.rendered) {
            var dom = this.el.dom, 
                state = this.scrollState;

            dom.scrollLeft = state.left;
            dom.scrollTop = state.top;
        }
    },

    
    prepareData: function(data, index, record) {
        var associatedData, attr, hasCopied;
        if (record) {
            associatedData = record.getAssociatedData();
            for (attr in associatedData) {
                if (associatedData.hasOwnProperty(attr)) {
                    
                    
                    
                    
                    if (!hasCopied) {
                        data = Ext.Object.chain(data);
                        hasCopied = true;
                    }
                    data[attr] = associatedData[attr];
                }
            }
        }
        return data;
    },

    
    collectData: function(records, startIndex){
        var data = [],
            i = 0,
            len = records.length,
            record;

        for (; i < len; i++) {
            record = records[i];
            data[i] = this.prepareData(record.data, startIndex + i, record);
        }
        return data;
    },

    
    bufferRender : function(records, index) {
        var me = this,
            div = me.renderBuffer || (me.renderBuffer = document.createElement('div'));

        me.tpl.overwrite(div, me.collectData(records, index));
        return  Ext.DomQuery.select(me.getItemSelector(), div);
    },
    
    getNodeContainer: function() {
        return this.getTargetEl();
    },

    
    onUpdate : function(ds, record){
        var me = this,
            index,
            node;

        if (me.viewReady) {
            index = me.dataSource.indexOf(record);
            if (index > -1) {
                node = me.bufferRender([record], index)[0];
                
                if (me.getNode(record)) {
                    me.all.replaceElement(index, node, true);
                    me.updateIndexes(index, index);
                    
                    me.selModel.onUpdate(record);
                    if (me.hasListeners.itemupdate) {
                        me.fireEvent('itemupdate', record, index, node);
                    }
                    return node;
                }
            }
        }

    },

    
    onAdd : function(store, records, index) {
        var me = this,
            nodes;

        if (me.rendered) {
            
            
            if (me.all.getCount() === 0) {
                me.refresh();
                nodes = me.all.slice();
            } else {
                nodes = me.doAdd(records, index);
                
                if (me.refreshSelmodelOnRefresh !== false) {
                    me.selModel.refresh();
                }
                me.updateIndexes(index);

                
                me.refreshSize();
            }

            if (me.hasListeners.itemadd) {
                me.fireEvent('itemadd', records, index, nodes);
            }
        }

    },

    doAdd: function(records, index) {
        var me = this,
            nodes = me.bufferRender(records, index, true),
            all = me.all,
            count = all.getCount(),
            i, l;

        if (count === 0) {
            for (i = 0, l = nodes.length; i < l; i++) {
                this.getNodeContainer().appendChild(nodes[i]);
            }
        } else if (index < count) {
            if (index === 0) {
                all.item(index).insertSibling(nodes, 'before', true);
            } else {
                all.item(index - 1).insertSibling(nodes, 'after', true);
            }
        } else {
            all.last().insertSibling(nodes, 'after', true);
        }

        all.insert(index, nodes);
        return nodes;
    },

    
    onRemove : function(ds, records, indexes) {
        var me = this,
            fireItemRemove = me.hasListeners.itemremove,
            i,
            record,
            index;

        if (me.all.getCount()) {
            if (me.dataSource.getCount() === 0) {
                
                if (fireItemRemove) {
                    for (i = indexes.length - 1; i >= 0; --i) {
                        me.fireEvent('itemremove', records[i], indexes[i]);
                    }
                }
                me.refresh();
            } else {
                
                
                for (i = indexes.length - 1; i >= 0; --i) {
                    record = records[i];
                    index = indexes[i];
                    me.doRemove(record, index);
                    if (fireItemRemove) {
                        me.fireEvent('itemremove', record, index);
                    }
                }
                me.updateIndexes(indexes[0]);
            }

            
            this.refreshSize();
        }
    },

    
    doRemove: function(record, index) {
        this.all.removeElement(index, true);
    },

    
    refreshNode : function(index) {
        this.onUpdate(this.dataSource, this.dataSource.getAt(index));
    },

    
    updateIndexes : function(startIndex, endIndex) {
        var nodes = this.all.elements,
            records = this.getViewRange(),
            i;

        startIndex = startIndex || 0;
        endIndex = endIndex || ((endIndex === 0) ? 0 : (nodes.length - 1));
        for (i = startIndex; i <= endIndex; i++) {
            nodes[i].viewIndex = i;
            nodes[i].viewRecordId = records[i].internalId;
            if (!nodes[i].boundView) {
                nodes[i].boundView = this.id;
            }
        }
    },

    
    getStore : function() {
        return this.store;
    },

    
    bindStore : function(store, initial, propName) {
        var me = this;
        me.mixins.bindable.bindStore.apply(me, arguments);

        
        
        if (!initial) {
            me.getSelectionModel().bindStore(store);
        }

        
        
        
        if (me.componentLayoutCounter) {
            me.doFirstRefresh(store);
        }
    },

    
    doFirstRefresh: function(store) {
        var me = this;

        
        me.firstRefreshDone = true;

        
        
        
        
        
        if (store && !store.loading) {
            if (me.deferInitialRefresh) {
                me.applyFirstRefresh();
            } else {
                me.refresh();
            }
        }
    },
    
    applyFirstRefresh: function(){
        var me = this;
        if (me.isDestroyed) {
            return;
        }
        
        
        
        
        
        
        
        
        
        if (me.up('[isCollapsingOrExpanding]')) {
            Ext.Function.defer(me.applyFirstRefresh, 100, me);
        } else {
            Ext.Function.defer(function () {
                if (!me.isDestroyed) {
                    me.refresh();
                }
            }, 1);
        }
    },

    onUnbindStore: function(store) {
        this.setMaskBind(null);
    },

    onBindStore: function(store, initial, propName) {
        this.setMaskBind(store);
        if (!initial && propName === 'store') {
            this.bindStore(store, false, 'dataSource');
        }
    },

    setMaskBind: function(store) {
        var mask = this.loadMask;
        if (mask && mask.bindStore) {
            mask.bindStore(store);
        }
    },

    getStoreListeners: function() {
        var me = this;
        return {
            idchanged: me.onIdChanged,
            refresh: me.onDataRefresh,
            add: me.onAdd,
            bulkremove: me.onRemove,
            update: me.onUpdate,
            clear: me.refresh
        };
    },

    
    onDataRefresh: function() {
        this.refreshView();
    },
    
    refreshView: function() {
        var me = this,
            
            
            blockedByAncestor = !me.firstRefreshDone && (!me.rendered || me.up('[collapsed],[isCollapsingOrExpanding],[hidden]'));

        
        
        
        
        if (blockedByAncestor) {
            me.deferInitialRefresh = false;
        } else if (me.blockRefresh !== true) {
            me.firstRefreshDone = true;
            me.refresh();
        }
    },

    
    findItemByChild: function(node){
        return Ext.fly(node).findParent(this.getItemSelector(), this.getTargetEl());
    },

    
    findTargetByEvent: function(e) {
        return e.getTarget(this.getItemSelector(), this.getTargetEl());
    },


    
    getSelectedNodes: function(){
        var nodes   = [],
            records = this.selModel.getSelection(),
            ln = records.length,
            i  = 0;

        for (; i < ln; i++) {
            nodes.push(this.getNode(records[i]));
        }

        return nodes;
    },

    
    getRecords: function(nodes) {
        var records = [],
            i = 0,
            len = nodes.length,
            data = this.dataSource.data;

        for (; i < len; i++) {
            records[records.length] = data.getByKey(nodes[i].viewRecordId);
        }

        return records;
    },

    
    getRecord: function(node){
        return this.dataSource.data.getByKey(Ext.getDom(node).viewRecordId);
    },


    
    isSelected : function(node) {
        
        var r = this.getRecord(node);
        return this.selModel.isSelected(r);
    },

    
    select: function(records, keepExisting, suppressEvent) {
        this.selModel.select(records, keepExisting, suppressEvent);
    },

    
    deselect: function(records, suppressEvent) {
        this.selModel.deselect(records, suppressEvent);
    },

    
    getNode : function(nodeInfo) {
        if ((!nodeInfo && nodeInfo !== 0) || !this.rendered) {
            return null;
        }

        if (Ext.isString(nodeInfo)) {
            return document.getElementById(nodeInfo);
        }
        if (Ext.isNumber(nodeInfo)) {
            return this.all.elements[nodeInfo];
        }
        if (nodeInfo.isModel) {
            return this.getNodeByRecord(nodeInfo);
        }
        return nodeInfo; 
    },

    
    getNodeByRecord: function(record) {
        var ns = this.all.elements,
            ln = ns.length,
            i = 0;

        for (; i < ln; i++) {
            if (ns[i].viewRecordId === record.internalId) {
                return ns[i];
            }
        }

        return null;
    },

    
    getNodes: function(start, end) {
        var all = this.all;

        if (end === undefined) {
            end = all.getCount();
        } else {
            end++;
        }
        return all.slice(start||0, end);
    },

    
    indexOf: function(node) {
        node = this.getNode(node);
        if (!node && node !== 0) {
            return -1;
        }
        if (Ext.isNumber(node.viewIndex)) {
            return node.viewIndex;
        }
        return this.all.indexOf(node);
    },

    onDestroy : function() {
        var me = this;

        me.all.clear();
        me.callParent();
        me.bindStore(null);
        me.selModel.destroy();
    },

    
    onItemSelect: function(record) {
        var node = this.getNode(record);

        if (node) {
            Ext.fly(node).addCls(this.selectedItemCls);
        }
    },

    
    onItemDeselect: function(record) {
        var node = this.getNode(record);

        if (node) {
            Ext.fly(node).removeCls(this.selectedItemCls);
        }
    },

    getItemSelector: function() {
        return this.itemSelector;
    }
}, function() {
    
    
    
    
    Ext.deprecate('extjs', '4.0', function() {
        Ext.view.AbstractView.override({
            
            
            

            
            getSelectionCount : function(){
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: getSelectionCount will be removed, please interact with the Ext.selection.DataViewModel");
                }
                return this.selModel.getSelection().length;
            },

            
            getSelectedRecords : function(){
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: getSelectedRecords will be removed, please interact with the Ext.selection.DataViewModel");
                }
                return this.selModel.getSelection();
            },

            
            
            select: function(records, keepExisting, supressEvents) {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: select will be removed, please access select through a DataView's SelectionModel, ie: view.getSelectionModel().select()");
                }
                var sm = this.getSelectionModel();
                return sm.select.apply(sm, arguments);
            },

            
            clearSelections: function() {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: clearSelections will be removed, please access deselectAll through DataView's SelectionModel, ie: view.getSelectionModel().deselectAll()");
                }
                var sm = this.getSelectionModel();
                return sm.deselectAll();
            }
        });
    });
});


Ext.define('Ext.view.View', {
    extend:  Ext.view.AbstractView ,
    alternateClassName: 'Ext.DataView',
    alias: 'widget.dataview',

    
    
    deferHighlight: Ext.isIE7m ? 100 : 0,

    
    mouseOverOutBuffer: 20,

    inputTagRe: /^textarea$|^input$/i,

    inheritableStatics: {
        EventMap: {
            mousedown: 'MouseDown',
            mouseup: 'MouseUp',
            click: 'Click',
            dblclick: 'DblClick',
            contextmenu: 'ContextMenu',
            mouseover: 'MouseOver',
            mouseout: 'MouseOut',
            mouseenter: 'MouseEnter',
            mouseleave: 'MouseLeave',
            keydown: 'KeyDown',
            focus: 'Focus'
        }
    },

    initComponent: function() {
        var me = this;
        me.callParent();
        
        
        if (me.mouseOverOutBuffer) {
            me.handleMouseOverOrOut = 
                Ext.Function.createBuffered(me.handleMouseOverOrOut, me.mouseOverOutBuffer, me);
            me.lastMouseOverOutEvent = new Ext.EventObjectImpl();
        }
        
        
        else if (me.deferHighlight){
            me.setHighlightedItem =
                Ext.Function.createBuffered(me.setHighlightedItem, me.deferHighlight, me);
        }
    },

    addCmpEvents: function() {
        this.addEvents(
            
            'beforeitemmousedown',
            
            'beforeitemmouseup',
            
            'beforeitemmouseenter',
            
            'beforeitemmouseleave',
            
            'beforeitemclick',
            
            'beforeitemdblclick',
            
            'beforeitemcontextmenu',
            
            'beforeitemkeydown',
            
            'itemmousedown',
            
            'itemmouseup',
            
            'itemmouseenter',
            
            'itemmouseleave',
            
            'itemclick',
            
            'itemdblclick',
            
            'itemcontextmenu',
            
            'itemkeydown',
            
            'beforecontainermousedown',
            
            'beforecontainermouseup',
            
            'beforecontainermouseover',
            
            'beforecontainermouseout',
            
            'beforecontainerclick',
            
            'beforecontainerdblclick',
            
            'beforecontainercontextmenu',
            
            'beforecontainerkeydown',
            
            'containermouseup',
            
            'containermouseover',
            
            'containermouseout',
            
            'containerclick',
            
            'containerdblclick',
            
            'containercontextmenu',
            
            'containerkeydown',

            
            'selectionchange',
            
            'beforeselect',
            
            'beforedeselect',
            
            'select',
            
            'deselect',
            
            'focuschange',
            
            
            'highlightitem',
            
            
            'unhighlightitem'
        );
    },

    getFocusEl: function() {
        return this.getTargetEl();
    },

    
    afterRender: function(){
        var me = this,
            onMouseOverOut = me.mouseOverOutBuffer ? me.onMouseOverOut : me.handleMouseOverOrOut;

        me.callParent();
        me.mon(me.getTargetEl(), {
            scope: me,
            
            freezeEvent: true,
            click: me.handleEvent,
            mousedown: me.handleEvent,
            mouseup: me.handleEvent,
            dblclick: me.handleEvent,
            contextmenu: me.handleEvent,
            keydown: me.handleEvent,
            mouseover: onMouseOverOut,
            mouseout:  onMouseOverOut
        });
    },

    onMouseOverOut: function(e) {
        var me = this;

        
        
        me.lastMouseOverOutEvent.setEvent(e.browserEvent, true);
        me.handleMouseOverOrOut(me.lastMouseOverOutEvent);
    },

    handleMouseOverOrOut: function(e) {
        var me = this,
            isMouseout = e.type === 'mouseout',
            method = isMouseout ? e.getRelatedTarget : e.getTarget,
            nowOverItem = method.call(e, me.itemSelector) || method.call(e, me.dataRowSelector);

        
        if (!me.mouseOverItem || nowOverItem !== me.mouseOverItem) {

            
            if (me.mouseOverItem) {
                e.item = me.mouseOverItem;
                e.newType = 'mouseleave';
                me.handleEvent(e);
            }

            
            me.mouseOverItem = nowOverItem;
            if (me.mouseOverItem) {
                e.item = me.mouseOverItem;
                e.newType = 'mouseenter';
                me.handleEvent(e);
            }
        }
    },

    handleEvent: function(e) {
        var me = this,
            key = e.type == 'keydown' && e.getKey();

        if (me.processUIEvent(e) !== false) {
            me.processSpecialEvent(e);
        }

        
        
        
        if (key === e.SPACE) {
            if (!me.inputTagRe.test(e.getTarget().tagName)) {
                e.stopEvent();
            }
        }
    },

    
    processItemEvent: Ext.emptyFn,
    processContainerEvent: Ext.emptyFn,
    processSpecialEvent: Ext.emptyFn,

    processUIEvent: function(e) {

        
        
        if (!Ext.getBody().isAncestor(e.target)) {
            return;
        }

        var me = this,
            item = e.getTarget(me.getItemSelector(), me.getTargetEl()),
            map = this.statics().EventMap,
            index, record,
            type = e.type,
            newType = e.type,
            sm;

        
        
        if (e.newType) {
            newType = e.newType;
            item = e.item;
        }

        
        
        if (!item && type == 'keydown') {
            sm = me.getSelectionModel();
            record = sm.lastFocused || sm.getLastSelected();
            if (record) {
                item = me.getNode(record, true);
            }
        }

        if (item) {
            if (!record) {
                record = me.getRecord(item);
            }
            index = me.indexInStore ? me.indexInStore(record) : me.indexOf(item);

            
            
            
            if (!record || me.processItemEvent(record, item, index, e) === false) {
                return false;
            }

            if (
                (me['onBeforeItem' + map[newType]](record, item, index, e) === false) ||
                (me.fireEvent('beforeitem' + newType, me, record, item, index, e) === false) ||
                (me['onItem' + map[newType]](record, item, index, e) === false)
            ) {
                return false;
            }

            me.fireEvent('item' + newType, me, record, item, index, e);
        }
        else {
            if (
                (me.processContainerEvent(e) === false) ||
                (me['onBeforeContainer' + map[type]](e) === false) ||
                (me.fireEvent('beforecontainer' + type, me, e) === false) ||
                (me['onContainer' + map[type]](e) === false)
            ) {
                return false;
            }

            me.fireEvent('container' + type, me, e);
        }

        return true;
    },

    
    onItemMouseEnter: function(record, item, index, e) {
        if (this.trackOver) {
            this.highlightItem(item);
        }
    },

    
    onItemMouseLeave : function(record, item, index, e) {
        if (this.trackOver) {
            this.clearHighlight();
        }
    },

    
    onItemMouseDown: Ext.emptyFn,
    onItemMouseUp: Ext.emptyFn,
    onItemFocus: Ext.emptyFn,
    onItemClick: Ext.emptyFn,
    onItemDblClick: Ext.emptyFn,
    onItemContextMenu: Ext.emptyFn,
    onItemKeyDown: Ext.emptyFn,
    onBeforeItemMouseDown: Ext.emptyFn,
    onBeforeItemMouseUp: Ext.emptyFn,
    onBeforeItemFocus: Ext.emptyFn,
    onBeforeItemMouseEnter: Ext.emptyFn,
    onBeforeItemMouseLeave: Ext.emptyFn,
    onBeforeItemClick: Ext.emptyFn,
    onBeforeItemDblClick: Ext.emptyFn,
    onBeforeItemContextMenu: Ext.emptyFn,
    onBeforeItemKeyDown: Ext.emptyFn,

    
    onContainerMouseDown: Ext.emptyFn,
    onContainerMouseUp: Ext.emptyFn,
    onContainerMouseOver: Ext.emptyFn,
    onContainerMouseOut: Ext.emptyFn,
    onContainerClick: Ext.emptyFn,
    onContainerDblClick: Ext.emptyFn,
    onContainerContextMenu: Ext.emptyFn,
    onContainerKeyDown: Ext.emptyFn,
    onBeforeContainerMouseDown: Ext.emptyFn,
    onBeforeContainerMouseUp: Ext.emptyFn,
    onBeforeContainerMouseOver: Ext.emptyFn,
    onBeforeContainerMouseOut: Ext.emptyFn,
    onBeforeContainerClick: Ext.emptyFn,
    onBeforeContainerDblClick: Ext.emptyFn,
    onBeforeContainerContextMenu: Ext.emptyFn,
    onBeforeContainerKeyDown: Ext.emptyFn,

    
    setHighlightedItem: function(item){
        var me = this,
            highlighted = me.highlightedItem,
            overItemCls = me.overItemCls,
            beforeOverItemCls = me.beforeOverItemCls,
            previous;

        if (highlighted != item){
            if (highlighted) {
                Ext.fly(highlighted).removeCls(overItemCls);
                previous = highlighted.previousSibling;
                if (beforeOverItemCls && previous) {
                    Ext.fly(previous).removeCls(beforeOverItemCls);
                }
                me.fireEvent('unhighlightitem', me, highlighted);
            }

            me.highlightedItem = item;

            if (item) {
                Ext.fly(item).addCls(me.overItemCls);
                previous = item.previousSibling;
                if (beforeOverItemCls && previous) {
                    Ext.fly(previous).addCls(beforeOverItemCls);
                }
                me.fireEvent('highlightitem', me, item);
            }
        }
    },

    
    highlightItem: function(item) {
        this.setHighlightedItem(item);
    },

    
    clearHighlight: function() {
        this.setHighlightedItem(undefined);
    },
    
    onUpdate: function(store, record){
        var me = this,
            node,
            newNode,
            highlighted;
        
        if (me.viewReady) {
            node = me.getNode(record);
            newNode = me.callParent(arguments);
            highlighted = me.highlightedItem;
            
            if (highlighted && highlighted === node) {
                delete me.highlightedItem;
                if (newNode) {
                    me.highlightItem(newNode);
                }
            }
        }
    },

    refresh: function() {
        this.clearHighlight();
        this.callParent(arguments);
    },
    
    
    focusNode: function(rec){
        var me          = this,
            node        = me.getNode(rec, true),
            el          = me.el,
            adjustmentY = 0,
            adjustmentX = 0,
            elRegion    = el.getRegion(),
            nodeRegion;

        
        
        elRegion.bottom = elRegion.top + el.dom.clientHeight;
        elRegion.right = elRegion.left + el.dom.clientWidth;
        if (node) {
            nodeRegion = Ext.fly(node).getRegion();
            
            if (nodeRegion.top < elRegion.top) {
                adjustmentY = nodeRegion.top - elRegion.top;
            
            } else if (nodeRegion.bottom > elRegion.bottom) {
                adjustmentY = nodeRegion.bottom - elRegion.bottom;
            }

            
            if (nodeRegion.left < elRegion.left) {
                adjustmentX = nodeRegion.left - elRegion.left;
            
            } else if (nodeRegion.right > elRegion.right) {
                adjustmentX = nodeRegion.right - elRegion.right;
            }

            if (adjustmentX || adjustmentY) {
                me.scrollBy(adjustmentX, adjustmentY, false);
            }
            el.focus();
        }
    }
});


Ext.define('Ext.layout.component.BoundList', {
    extend:  Ext.layout.component.Auto ,
    alias: 'layout.boundlist',

    type: 'component',
    
    beginLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            toolbar = owner.pagingToolbar;

        me.callParent(arguments);
        
        if (owner.floating) {
            ownerContext.savedXY = owner.getXY();
            
            
            owner.setXY([0, -9999]);
        }
        
        if (toolbar) {
            ownerContext.toolbarContext = ownerContext.context.getCmp(toolbar);
        }
        ownerContext.listContext = ownerContext.getEl('listEl');
    },
    
    beginLayoutCycle: function(ownerContext){
        var owner = this.owner;
        
        this.callParent(arguments);
        if (ownerContext.heightModel.auto) {
            
            
            
            owner.el.setHeight('auto');
            owner.listEl.setHeight('auto');
        }
    },

    getLayoutItems: function() {
        var toolbar = this.owner.pagingToolbar;
        return toolbar ? [toolbar] : [];
    },
    
    isValidParent: function() {
        
        
        return true;
    },

    finishedLayout: function(ownerContext) {
        var xy = ownerContext.savedXY;
        
        this.callParent(arguments);
        if (xy) {
            this.owner.setXY(xy);
        }
    },
    
    measureContentWidth: function(ownerContext) {
        return this.owner.listEl.getWidth();
    },
    
    measureContentHeight: function(ownerContext) {
        return this.owner.listEl.getHeight();
    },
    
    publishInnerHeight: function(ownerContext, height) {
        var toolbar = ownerContext.toolbarContext,
            toolbarHeight = 0;
            
        if (toolbar) {
            toolbarHeight = toolbar.getProp('height');
        }
        
        if (toolbarHeight === undefined) {
            this.done = false;
        } else {
            ownerContext.listContext.setHeight(height - ownerContext.getFrameInfo().height - toolbarHeight);
        }
    },
    
    calculateOwnerHeightFromContentHeight: function(ownerContext){
        var height = this.callParent(arguments),
            toolbar = ownerContext.toolbarContext;
            
        if (toolbar) {
            height += toolbar.getProp('height');
        }
        return height;
    }
});


Ext.define('Ext.toolbar.TextItem', {
    extend:  Ext.toolbar.Item ,
                                
    alias: 'widget.tbtext',
    alternateClassName: 'Ext.Toolbar.TextItem',

    
    text: '',

    renderTpl: '{text}',
    
    baseCls: Ext.baseCSSPrefix + 'toolbar-text',

    beforeRender : function() {
        var me = this;

        me.callParent();

        Ext.apply(me.renderData, {
            text: me.text
        });
    },

    
    setText : function(text) {
        var me = this;
        me.text = text;
        if (me.rendered) {
            me.el.update(text);
            me.updateLayout();
        }
    }
});


Ext.define('Ext.form.field.Spinner', {
    extend:  Ext.form.field.Trigger ,
    alias: 'widget.spinnerfield',
    alternateClassName: 'Ext.form.Spinner',
                                  

    trigger1Cls: Ext.baseCSSPrefix + 'form-spinner-up',
    trigger2Cls: Ext.baseCSSPrefix + 'form-spinner-down',

    
    spinUpEnabled: true,

    
    spinDownEnabled: true,

    
    keyNavEnabled: true,

    
    mouseWheelEnabled: true,

    
    repeatTriggerClick: true,

    
    onSpinUp: Ext.emptyFn,

    
    onSpinDown: Ext.emptyFn,

    triggerTpl: '<td style="{triggerStyle}" class="{triggerCls}">' +
                    '<div class="' + Ext.baseCSSPrefix + 'trigger-index-0 ' + Ext.baseCSSPrefix + 'form-trigger ' + Ext.baseCSSPrefix + 'form-spinner-up {spinnerUpCls} {childElCls}" role="button"></div>' +
                    '<div class="' + Ext.baseCSSPrefix + 'trigger-index-1 ' + Ext.baseCSSPrefix + 'form-trigger ' + Ext.baseCSSPrefix + 'form-spinner-down {spinnerDownCls} {childElCls}" role="button"></div>' +
                '</td>' +
            '</tr>',

    initComponent: function() {
        this.callParent();

        this.addEvents(
            
            'spin',

            
            'spinup',

            
            'spindown'
        );
    },

    
    onRender: function() {
        var me = this,
            triggers;

        me.callParent(arguments);
        triggers = me.triggerEl;
        
        
        me.spinUpEl = triggers.item(0);
        
        me.spinDownEl = triggers.item(1);
        
        me.triggerCell = me.spinUpEl.parent(); 

        
        if (me.keyNavEnabled) {
            me.spinnerKeyNav = new Ext.util.KeyNav(me.inputEl, {
                scope: me,
                up: me.spinUp,
                down: me.spinDown
            });
        }

        
        if (me.mouseWheelEnabled) {
            me.mon(me.bodyEl, 'mousewheel', me.onMouseWheel, me);
        }
    },

    getSubTplMarkup: function(values) {
        var me = this,
            childElCls = values.childElCls, 
            field = Ext.form.field.Base.prototype.getSubTplMarkup.apply(me, arguments);

        return '<table id="' + me.id + '-triggerWrap" class="' + Ext.baseCSSPrefix + 'form-trigger-wrap' + childElCls + '" cellpadding="0" cellspacing="0">' +
            '<tbody>' +
                '<tr><td id="' + me.id + '-inputCell" class="' + Ext.baseCSSPrefix + 'form-trigger-input-cell' + childElCls + '">' + field + '</td>' +
                me.getTriggerMarkup() +
            '</tbody></table>';
    },

    getTriggerMarkup: function() {
        return this.getTpl('triggerTpl').apply(this.getTriggerData());
    },
    
    getTriggerData: function(){
        var me = this,
            hideTrigger = (me.readOnly || me.hideTrigger);
            
        return {
            triggerCls: Ext.baseCSSPrefix + 'trigger-cell',
            triggerStyle: hideTrigger ? 'display:none' : '',
            spinnerUpCls: !me.spinUpEnabled ? me.trigger1Cls + '-disabled': '',
            spinnerDownCls: !me.spinDownEnabled ? me.trigger2Cls + '-disabled': ''
        };
    },

    
    getTriggerWidth: function() {
        var me = this,
            totalTriggerWidth = 0;

        if (me.triggerWrap && !me.hideTrigger && !me.readOnly) {
            totalTriggerWidth = me.triggerWidth;
        }
        return totalTriggerWidth;
    },

    
    onTrigger1Click: function() {
        this.spinUp();
    },

    
    onTrigger2Click: function() {
        this.spinDown();
    },

    
    
    onTriggerWrapMouseup: function() {
        this.inputEl.focus();
    },

    
    spinUp: function() {
        var me = this;
        if (me.spinUpEnabled && !me.disabled) {
            me.fireEvent('spin', me, 'up');
            me.fireEvent('spinup', me);
            me.onSpinUp();
        }
    },

    
    spinDown: function() {
        var me = this;
        if (me.spinDownEnabled && !me.disabled) {
            me.fireEvent('spin', me, 'down');
            me.fireEvent('spindown', me);
            me.onSpinDown();
        }
    },

    
    setSpinUpEnabled: function(enabled) {
        var me = this,
            wasEnabled = me.spinUpEnabled;
        me.spinUpEnabled = enabled;
        if (wasEnabled !== enabled && me.rendered) {
            me.spinUpEl[enabled ? 'removeCls' : 'addCls'](me.trigger1Cls + '-disabled');
        }
    },

    
    setSpinDownEnabled: function(enabled) {
        var me = this,
            wasEnabled = me.spinDownEnabled;
        me.spinDownEnabled = enabled;
        if (wasEnabled !== enabled && me.rendered) {
            me.spinDownEl[enabled ? 'removeCls' : 'addCls'](me.trigger2Cls + '-disabled');
        }
    },

    
    onMouseWheel: function(e) {
        var me = this,
            delta;
        if (me.hasFocus) {
            delta = e.getWheelDelta();
            if (delta > 0) {
                me.spinUp();
            } else if (delta < 0) {
                me.spinDown();
            }
            e.stopEvent();
        }
    },

    onDestroy: function() {
        Ext.destroyMembers(this, 'spinnerKeyNav', 'spinUpEl', 'spinDownEl');
        this.callParent();
    }

});


Ext.define('Ext.form.field.Number', {
    extend: Ext.form.field.Spinner ,
    alias: 'widget.numberfield',
    alternateClassName: ['Ext.form.NumberField', 'Ext.form.Number'],

    
    
     
    
    allowExponential: true,

    
    allowDecimals : true,

    
    
    decimalSeparator : '.',
    
    
    
    
    submitLocaleSeparator: true,
    

    
    
    decimalPrecision : 2,
    

    
    minValue: Number.NEGATIVE_INFINITY,

    
    maxValue: Number.MAX_VALUE,

    
    step: 1,

    
    
    minText : 'The minimum value for this field is {0}',
    

    
    
    maxText : 'The maximum value for this field is {0}',
    

    
    
    nanText : '{0} is not a valid number',
    

    
    
    negativeText : 'The value cannot be negative',
    

    
    baseChars : '0123456789',

    
    autoStripChars: false,

    initComponent: function() {
        var me = this;
        me.callParent();

        me.setMinValue(me.minValue);
        me.setMaxValue(me.maxValue);
    },

    
    getErrors: function(value) {
        var me = this,
            errors = me.callParent(arguments),
            format = Ext.String.format,
            num;

        value = Ext.isDefined(value) ? value : this.processRawValue(this.getRawValue());

        if (value.length < 1) { 
             return errors;
        }

        value = String(value).replace(me.decimalSeparator, '.');

        if(isNaN(value)){
            errors.push(format(me.nanText, value));
        }

        num = me.parseValue(value);

        if (me.minValue === 0 && num < 0) {
            errors.push(this.negativeText);
        }
        else if (num < me.minValue) {
            errors.push(format(me.minText, me.minValue));
        }

        if (num > me.maxValue) {
            errors.push(format(me.maxText, me.maxValue));
        }


        return errors;
    },

    rawToValue: function(rawValue) {
        var value = this.fixPrecision(this.parseValue(rawValue));
        if (value === null) {
            value = rawValue || null;
        }
        return  value;
    },

    valueToRaw: function(value) {
        var me = this,
            decimalSeparator = me.decimalSeparator;
        value = me.parseValue(value);
        value = me.fixPrecision(value);
        value = Ext.isNumber(value) ? value : parseFloat(String(value).replace(decimalSeparator, '.'));
        value = isNaN(value) ? '' : String(value).replace('.', decimalSeparator);
        return value;
    },
    
    getSubmitValue: function() {
        var me = this,
            value = me.callParent();
            
        if (!me.submitLocaleSeparator) {
            value = value.replace(me.decimalSeparator, '.');
        }  
        return value;
    },

    onChange: function() {
        this.toggleSpinners();
        this.callParent(arguments);
    },
    
    toggleSpinners: function(){
        var me = this,
            value = me.getValue(),
            valueIsNull = value === null,
            enabled;
        
        
        
        if (me.spinUpEnabled || me.spinUpDisabledByToggle) {
            enabled = valueIsNull || value < me.maxValue;
            me.setSpinUpEnabled(enabled, true);
        }
        
        
        if (me.spinDownEnabled || me.spinDownDisabledByToggle) {
            enabled = valueIsNull || value > me.minValue;
            me.setSpinDownEnabled(enabled, true);
        }
    },

    
    setMinValue : function(value) {
        var me = this,
            allowed;
        
        me.minValue = Ext.Number.from(value, Number.NEGATIVE_INFINITY);
        me.toggleSpinners();
        
        
        if (me.disableKeyFilter !== true) {
            allowed = me.baseChars + '';
            
            if (me.allowExponential) {
                allowed += me.decimalSeparator + 'e+-';
            }
            else {
                if (me.allowDecimals) {
                    allowed += me.decimalSeparator;
                }
                if (me.minValue < 0) {
                    allowed += '-';
                }
            }
            
            allowed = Ext.String.escapeRegex(allowed);
            me.maskRe = new RegExp('[' + allowed + ']');
            if (me.autoStripChars) {
                me.stripCharsRe = new RegExp('[^' + allowed + ']', 'gi');
            }
        }
    },

    
    setMaxValue: function(value) {
        this.maxValue = Ext.Number.from(value, Number.MAX_VALUE);
        this.toggleSpinners();
    },

    
    parseValue : function(value) {
        value = parseFloat(String(value).replace(this.decimalSeparator, '.'));
        return isNaN(value) ? null : value;
    },

    
    fixPrecision : function(value) {
        var me = this,
            nan = isNaN(value),
            precision = me.decimalPrecision;

        if (nan || !value) {
            return nan ? '' : value;
        } else if (!me.allowDecimals || precision <= 0) {
            precision = 0;
        }

        return parseFloat(Ext.Number.toFixed(parseFloat(value), precision));
    },

    beforeBlur : function() {
        var me = this,
            v = me.parseValue(me.getRawValue());

        if (!Ext.isEmpty(v)) {
            me.setValue(v);
        }
    },
    
    setSpinUpEnabled: function(enabled,  internal){
        this.callParent(arguments);
        if (!internal) {
            delete this.spinUpDisabledByToggle;
        } else {
            this.spinUpDisabledByToggle = !enabled;
        }
    },

    onSpinUp: function() {
        var me = this;
            
        if (!me.readOnly) {
            me.setSpinValue(Ext.Number.constrain(me.getValue() + me.step, me.minValue, me.maxValue));
        }
    },
    
    setSpinDownEnabled: function(enabled,  internal){
        this.callParent(arguments);
        if (!internal) {
            delete this.spinDownDisabledByToggle;
        } else {
            this.spinDownDisabledByToggle = !enabled;
        }   
    },

    onSpinDown: function() {
        var me = this;
        
        if (!me.readOnly) {
            me.setSpinValue(Ext.Number.constrain(me.getValue() - me.step, me.minValue, me.maxValue));
        }
    },
    
    setSpinValue: function(value) {
        var me = this,
            len;
            
        if (me.enforceMaxLength) {
            
            
            if (me.fixPrecision(value).toString().length > me.maxLength) {
                return;
            }
        }
        me.setValue(value);
    }
});


Ext.define('Ext.toolbar.Paging', {
    extend:  Ext.toolbar.Toolbar ,
    alias: 'widget.pagingtoolbar',
    alternateClassName: 'Ext.PagingToolbar',
                                                                
    mixins: {
        bindable:  Ext.util.Bindable     
    },
    

    
    displayInfo: false,

    
    prependButtons: false,

    
    
    displayMsg : 'Displaying {0} - {1} of {2}',
    

    
    
    emptyMsg : 'No data to display',
    

    
    
    beforePageText : 'Page',
    

    
    
    afterPageText : 'of {0}',
    

    
    
    firstText : 'First Page',
    

    
    
    prevText : 'Previous Page',
    

    
    
    nextText : 'Next Page',
    

    
    
    lastText : 'Last Page',
    

    
    
    refreshText : 'Refresh',
    

    
    inputItemWidth : 30,

    
    getPagingItems: function() {
        var me = this;

        return [{
            itemId: 'first',
            tooltip: me.firstText,
            overflowText: me.firstText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-first',
            disabled: true,
            handler: me.moveFirst,
            scope: me
        },{
            itemId: 'prev',
            tooltip: me.prevText,
            overflowText: me.prevText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-prev',
            disabled: true,
            handler: me.movePrevious,
            scope: me
        },
        '-',
        me.beforePageText,
        {
            xtype: 'numberfield',
            itemId: 'inputItem',
            name: 'inputItem',
            cls: Ext.baseCSSPrefix + 'tbar-page-number',
            allowDecimals: false,
            minValue: 1,
            hideTrigger: true,
            enableKeyEvents: true,
            keyNavEnabled: false,
            selectOnFocus: true,
            submitValue: false,
            
            isFormField: false,
            width: me.inputItemWidth,
            margins: '-1 2 3 2',
            listeners: {
                scope: me,
                keydown: me.onPagingKeyDown,
                blur: me.onPagingBlur
            }
        },{
            xtype: 'tbtext',
            itemId: 'afterTextItem',
            text: Ext.String.format(me.afterPageText, 1)
        },
        '-',
        {
            itemId: 'next',
            tooltip: me.nextText,
            overflowText: me.nextText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-next',
            disabled: true,
            handler: me.moveNext,
            scope: me
        },{
            itemId: 'last',
            tooltip: me.lastText,
            overflowText: me.lastText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-last',
            disabled: true,
            handler: me.moveLast,
            scope: me
        },
        '-',
        {
            itemId: 'refresh',
            tooltip: me.refreshText,
            overflowText: me.refreshText,
            iconCls: Ext.baseCSSPrefix + 'tbar-loading',
            handler: me.doRefresh,
            scope: me
        }];
    },

    initComponent : function(){
        var me = this,
            pagingItems = me.getPagingItems(),
            userItems   = me.items || me.buttons || [];

        if (me.prependButtons) {
            me.items = userItems.concat(pagingItems);
        } else {
            me.items = pagingItems.concat(userItems);
        }
        delete me.buttons;

        if (me.displayInfo) {
            me.items.push('->');
            me.items.push({xtype: 'tbtext', itemId: 'displayItem'});
        }

        me.callParent();

        me.addEvents(
            
            'change',

            
            'beforechange'
        );
        me.on('beforerender', me.onLoad, me, {single: true});

        me.bindStore(me.store || 'ext-empty-store', true);
    },
    
    updateInfo : function(){
        var me = this,
            displayItem = me.child('#displayItem'),
            store = me.store,
            pageData = me.getPageData(),
            count, msg;

        if (displayItem) {
            count = store.getCount();
            if (count === 0) {
                msg = me.emptyMsg;
            } else {
                msg = Ext.String.format(
                    me.displayMsg,
                    pageData.fromRecord,
                    pageData.toRecord,
                    pageData.total
                );
            }
            displayItem.setText(msg);
        }
    },

    
    onLoad : function(){
        var me = this,
            pageData,
            currPage,
            pageCount,
            afterText,
            count,
            isEmpty,
            item;

        count = me.store.getCount();
        isEmpty = count === 0;
        if (!isEmpty) {
            pageData = me.getPageData();
            currPage = pageData.currentPage;
            pageCount = pageData.pageCount;
            afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);
        } else {
            currPage = 0;
            pageCount = 0;
            afterText = Ext.String.format(me.afterPageText, 0);
        }

        Ext.suspendLayouts();
        item = me.child('#afterTextItem');
        if (item) {    
            item.setText(afterText);
        }
        item = me.getInputItem();
        if (item) {
            item.setDisabled(isEmpty).setValue(currPage);
        }
        me.setChildDisabled('#first', currPage === 1 || isEmpty);
        me.setChildDisabled('#prev', currPage === 1 || isEmpty);
        me.setChildDisabled('#next', currPage === pageCount  || isEmpty);
        me.setChildDisabled('#last', currPage === pageCount  || isEmpty);
        me.setChildDisabled('#refresh', false);
        me.updateInfo();
        Ext.resumeLayouts(true);

        if (me.rendered) {
            me.fireEvent('change', me, pageData);
        }
    },
    
    setChildDisabled: function(selector, disabled){
        var item = this.child(selector);
        if (item) {
            item.setDisabled(disabled);
        }
    },

    
    getPageData : function(){
        var store = this.store,
            totalCount = store.getTotalCount();

        return {
            total : totalCount,
            currentPage : store.currentPage,
            pageCount: Math.ceil(totalCount / store.pageSize),
            fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
            toRecord: Math.min(store.currentPage * store.pageSize, totalCount)

        };
    },

    
    onLoadError : function(){
        if (!this.rendered) {
            return;
        }
        this.setChildDisabled('#refresh', false);
    },
    
    getInputItem: function(){
        return this.child('#inputItem');
    },

    
    readPageFromInput : function(pageData){
        var inputItem = this.getInputItem(),
            pageNum = false,
            v;

        if (inputItem) {
            v = inputItem.getValue();
            pageNum = parseInt(v, 10);
            if (!v || isNaN(pageNum)) {
                inputItem.setValue(pageData.currentPage);
                return false;
            }
        }
        return pageNum;
    },

    onPagingFocus : function(){
        var inputItem = this.getInputItem();
        if (inputItem) {
            inputItem.select();
        }
    },

    
    onPagingBlur : function(e){
        var inputItem = this.getInputItem(),
            curPage;
            
        if (inputItem) {
            curPage = this.getPageData().currentPage;
            inputItem.setValue(curPage);
        }
    },

    
    onPagingKeyDown : function(field, e){
        var me = this,
            k = e.getKey(),
            pageData = me.getPageData(),
            increment = e.shiftKey ? 10 : 1,
            pageNum;

        if (k == e.RETURN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum !== false) {
                pageNum = Math.min(Math.max(1, pageNum), pageData.pageCount);
                if(me.fireEvent('beforechange', me, pageNum) !== false){
                    me.store.loadPage(pageNum);
                }
            }
        } else if (k == e.HOME || k == e.END) {
            e.stopEvent();
            pageNum = k == e.HOME ? 1 : pageData.pageCount;
            field.setValue(pageNum);
        } else if (k == e.UP || k == e.PAGE_UP || k == e.DOWN || k == e.PAGE_DOWN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum) {
                if (k == e.DOWN || k == e.PAGE_DOWN) {
                    increment *= -1;
                }
                pageNum += increment;
                if (pageNum >= 1 && pageNum <= pageData.pageCount) {
                    field.setValue(pageNum);
                }
            }
        }
    },

    
    beforeLoad : function(){
        if (this.rendered) {
            this.setChildDisabled('#refresh', true);
        }
    },

    
    moveFirst : function(){
        if (this.fireEvent('beforechange', this, 1) !== false){
            this.store.loadPage(1);
        }
    },

    
    movePrevious : function(){
        var me = this,
            prev = me.store.currentPage - 1;

        if (prev > 0) {
            if (me.fireEvent('beforechange', me, prev) !== false) {
                me.store.previousPage();
            }
        }
    },

    
    moveNext : function(){
        var me = this,
            total = me.getPageData().pageCount,
            next = me.store.currentPage + 1;

        if (next <= total) {
            if (me.fireEvent('beforechange', me, next) !== false) {
                me.store.nextPage();
            }
        }
    },

    
    moveLast : function(){
        var me = this,
            last = me.getPageData().pageCount;

        if (me.fireEvent('beforechange', me, last) !== false) {
            me.store.loadPage(last);
        }
    },

    
    doRefresh : function(){
        var me = this,
            current = me.store.currentPage;

        if (me.fireEvent('beforechange', me, current) !== false) {
            me.store.loadPage(current);
        }
    },
    
    getStoreListeners: function() {
        return {
            beforeload: this.beforeLoad,
            load: this.onLoad,
            exception: this.onLoadError
        };
    },

    
    unbind : function(store){
        this.bindStore(null);
    },

    
    bind : function(store){
        this.bindStore(store);
    },

    
    onDestroy : function(){
        this.unbind();
        this.callParent();
    }
});


Ext.define('Ext.view.BoundList', {
    extend:  Ext.view.View ,
    alias: 'widget.boundlist',
    alternateClassName: 'Ext.BoundList',
                                                                       
    
    mixins: {
        queryable:  Ext.Queryable 
    },

    
    pageSize: 0,

    

    

    
    baseCls: Ext.baseCSSPrefix + 'boundlist',
    itemCls: Ext.baseCSSPrefix + 'boundlist-item',
    listItemCls: '',
    shadow: false,
    trackOver: true,
    refreshed: 0,

    
    deferInitialRefresh: false,

    componentLayout: 'boundlist',

    childEls: [
        'listEl'
    ],

    renderTpl: [
        '<div id="{id}-listEl" class="{baseCls}-list-ct ', Ext.dom.Element.unselectableCls, '" style="overflow:auto"></div>',
        '{%',
            'var me=values.$comp, pagingToolbar=me.pagingToolbar;',
            'if (pagingToolbar) {',
                'pagingToolbar.ownerLayout = me.componentLayout;',
                'Ext.DomHelper.generateMarkup(pagingToolbar.getRenderTree(), out);',
            '}',
        '%}',
        {
            disableFormats: true
        }
    ],

    

    initComponent: function() {
        var me = this,
            baseCls = me.baseCls,
            itemCls = me.itemCls;

        me.selectedItemCls = baseCls + '-selected';
        if (me.trackOver) {
            me.overItemCls = baseCls + '-item-over';
        }
        me.itemSelector = "." + itemCls;

        if (me.floating) {
            me.addCls(baseCls + '-floating');
        }

        if (!me.tpl) {
            
            
            me.tpl = new Ext.XTemplate(
                '<ul class="' + Ext.plainListCls + '"><tpl for=".">',
                    '<li role="option" unselectable="on" class="' + itemCls + '">' + me.getInnerTpl(me.displayField) + '</li>',
                '</tpl></ul>'
            );
        } else if (!me.tpl.isTemplate) {
            me.tpl = new Ext.XTemplate(me.tpl);
        }

        if (me.pageSize) {
            me.pagingToolbar = me.createPagingToolbar();
        }

        me.callParent();
    },

    beforeRender: function() {
        var me = this;

        me.callParent(arguments);

        
        
        if (me.up('menu')) {
            me.addCls(Ext.baseCSSPrefix + 'menu');
        }
    },

    getRefOwner: function() {
        return this.pickerField || this.callParent();
    },

    getRefItems: function() {
        return this.pagingToolbar ? [ this.pagingToolbar ] : [];
    },

    createPagingToolbar: function() {
        return Ext.widget('pagingtoolbar', {
            id: this.id + '-paging-toolbar',
            pageSize: this.pageSize,
            store: this.dataSource,
            border: false,
            ownerCt: this,
            ownerLayout: this.getComponentLayout()
        });
    },

    
    
    finishRenderChildren: function () {
        var toolbar = this.pagingToolbar;

        this.callParent(arguments);

        if (toolbar) {
            toolbar.finishRender();
        }
    },

    refresh: function(){
        var me = this,
            tpl = me.tpl,
            toolbar = me.pagingToolbar,
            rendered = me.rendered;

        
        tpl.field = me.pickerField;
        tpl.store = me.store;
        me.callParent();
        tpl.field =  tpl.store = null;

        
        
        if (rendered && toolbar && toolbar.rendered && !me.preserveScrollOnRefresh) {
            me.el.appendChild(toolbar.el);
        }

        
        
        if (rendered && Ext.isIE6 && Ext.isStrict) {
            me.listEl.repaint();
        }
    },

    bindStore : function(store, initial) {
        var toolbar = this.pagingToolbar;

        this.callParent(arguments);
        if (toolbar) {
            toolbar.bindStore(store, initial);
        }
    },

    getTargetEl: function() {
        return this.listEl || this.el;
    },

    
    getInnerTpl: function(displayField) {
        return '{' + displayField + '}';
    },

    onDestroy: function() {
        Ext.destroyMembers(this, 'pagingToolbar', 'listEl');
        this.callParent();
    }
});


Ext.define('Ext.view.BoundListKeyNav', {
    extend:  Ext.util.KeyNav ,
                                   

    

    constructor: function(el, config) {
        var me = this;
        me.boundList = config.boundList;
        me.callParent([el, Ext.apply({}, config, me.defaultHandlers)]);
    },

    defaultHandlers: {
        up: function() {
            var me = this,
                boundList = me.boundList,
                allItems = boundList.all,
                oldItem = boundList.highlightedItem,
                oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1,
                newItemIdx = oldItemIdx > 0 ? oldItemIdx - 1 : allItems.getCount() - 1; 
            me.highlightAt(newItemIdx);
        },

        down: function() {
            var me = this,
                boundList = me.boundList,
                allItems = boundList.all,
                oldItem = boundList.highlightedItem,
                oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1,
                newItemIdx = oldItemIdx < allItems.getCount() - 1 ? oldItemIdx + 1 : 0; 
            me.highlightAt(newItemIdx);
        },

        pageup: function() {
            
        },

        pagedown: function() {
            
        },

        home: function() {
            this.highlightAt(0);
        },

        end: function() {
            var me = this;
            me.highlightAt(me.boundList.all.getCount() - 1);
        },

        enter: function(e) {
            this.selectHighlighted(e);
        }
    },

    
    highlightAt: function(index) {
        var boundList = this.boundList,
            item = boundList.all.item(index);
        if (item) {
            item = item.dom;
            boundList.highlightItem(item);
            boundList.getTargetEl().scrollChildIntoView(item, false);
        }
    },

    
    selectHighlighted: function(e) {
        var me = this,
            boundList = me.boundList,
            highlighted = boundList.highlightedItem,
            selModel = boundList.getSelectionModel();
        if (highlighted) {
            selModel.selectWithEvent(boundList.getRecord(highlighted), e);
        }
    }

});


Ext.define('Ext.layout.component.field.ComboBox', {
    extend:  Ext.layout.component.field.Trigger ,
    alias: 'layout.combobox',
                                       

    type: 'combobox',

    startingWidth: null,

    getTextWidth: function () {
        var me = this,
            owner = me.owner,
            store = owner.store,
            field = owner.displayField,
            storeLn = store.data.length,
            value = '',
            i = 0, n = 0, ln, item, width;

        for (; i < storeLn; i++) {
            item = store.getAt(i).data[field];
            ln = item.length;
            
            if (ln > n) {
                n = ln;
                value = item;
            }
        }

        width = Math.max(me.callParent(arguments), owner.inputEl.getTextWidth(value + owner.growAppend));

        
        
        if (!me.startingWidth || owner.removingRecords) {
            me.startingWidth = width;

            
            
            if (width < owner.growMin) {
                owner.defaultListConfig.minWidth = owner.growMin;
            }

            owner.removingRecords = false;
        }
 
        
        return (width < me.startingWidth) ? me.startingWidth : width;
    }
});


Ext.define('Ext.form.field.ComboBox', {
    extend: Ext.form.field.Picker ,
                                                                                                                                                                            
    alternateClassName: 'Ext.form.ComboBox',
    alias: ['widget.combobox', 'widget.combo'],
    mixins: {
        bindable:  Ext.util.Bindable     
    },

    componentLayout: 'combobox',

    
    triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',
    
    
    hiddenName: '',
    
    

    
    hiddenDataCls: Ext.baseCSSPrefix + 'hide-display ' + Ext.baseCSSPrefix + 'form-data-hidden',

    
    fieldSubTpl: [
        '<div class="{hiddenDataCls}" role="presentation"></div>',
        '<input id="{id}" type="{type}" {inputAttrTpl} class="{fieldCls} {typeCls} {editableCls}" autocomplete="off"',
            '<tpl if="value"> value="{[Ext.util.Format.htmlEncode(values.value)]}"</tpl>',
            '<tpl if="name"> name="{name}"</tpl>',
            '<tpl if="placeholder"> placeholder="{placeholder}"</tpl>',
            '<tpl if="size"> size="{size}"</tpl>',
            '<tpl if="maxLength !== undefined"> maxlength="{maxLength}"</tpl>',
            '<tpl if="readOnly"> readonly="readonly"</tpl>',
            '<tpl if="disabled"> disabled="disabled"</tpl>',
            '<tpl if="tabIdx"> tabIndex="{tabIdx}"</tpl>',
            '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>',
            '/>',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    getSubTplData: function(){
        var me = this;
        Ext.applyIf(me.subTplData, {
            hiddenDataCls: me.hiddenDataCls
        });
        return me.callParent(arguments);
    },

    afterRender: function(){
        var me = this;
        me.callParent(arguments);
        me.setHiddenValue(me.value);
    },

    

    
    multiSelect: false,

    
    
    delimiter: ', ',
    

    
    displayField: 'text',

    

    
    triggerAction: 'all',

    
    allQuery: '',

    
    queryParam: 'query',

    
    queryMode: 'remote',

    
    queryCaching: true,

    
    pageSize: 0,

    

    

    
    anyMatch: false,

    
    caseSensitive: false,

    
    autoSelect: true,

    
    typeAhead: false,

    
    typeAheadDelay: 250,

    
    selectOnTab: true,

    
    forceSelection: false,

    
    growToLongestValue: true,
    
    

    

    

    
    defaultListConfig: {
        loadingHeight: 70,
        minWidth: 70,
        maxHeight: 300,
        shadow: 'sides'
    },

    

    

    
    ignoreSelection: 0,

    
    removingRecords: null,

    
    resizeComboToGrow: function () {
        var me = this;
        return me.grow && me.growToLongestValue;
    },

    initComponent: function() {
        var me = this,
            isDefined = Ext.isDefined,
            store = me.store,
            transform = me.transform,
            transformSelect, isLocalMode;

        Ext.applyIf(me.renderSelectors, {
            hiddenDataEl: '.' + me.hiddenDataCls.split(' ').join('.')
        });
        

        this.addEvents(
            
            'beforequery',

            
            'select',

            
            'beforeselect',

            
            'beforedeselect'
        );

        
        if (transform) {
            transformSelect = Ext.getDom(transform);
            if (transformSelect) {
                if (!me.store) {
                    store = Ext.Array.map(Ext.Array.from(transformSelect.options), function(option){
                        return [option.value, option.text];
                    });
                }
                if (!me.name) {
                    me.name = transformSelect.name;
                }
                if (!('value' in me)) {
                    me.value = transformSelect.value;
                }
            }
        }

        me.bindStore(store || 'ext-empty-store', true);
        store = me.store;
        if (store.autoCreated) {
            me.queryMode = 'local';
            me.valueField = me.displayField = 'field1';
            if (!store.expanded) {
                me.displayField = 'field2';
            }
        }

        if (!isDefined(me.valueField)) {
            me.valueField = me.displayField;
        }

        isLocalMode = me.queryMode === 'local';
        if (!isDefined(me.queryDelay)) {
            me.queryDelay = isLocalMode ? 10 : 500;
        }
        if (!isDefined(me.minChars)) {
            me.minChars = isLocalMode ? 0 : 4;
        }

        if (!me.displayTpl) {
            me.displayTpl = new Ext.XTemplate(
                '<tpl for=".">' +
                    '{[typeof values === "string" ? values : values["' + me.displayField + '"]]}' +
                    '<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' +
                '</tpl>'
            );
        } else if (Ext.isString(me.displayTpl)) {
            me.displayTpl = new Ext.XTemplate(me.displayTpl);
        }

        me.callParent();

        me.doQueryTask = new Ext.util.DelayedTask(me.doRawQuery, me);

        
        if (me.store.getCount() > 0) {
            me.setValue(me.value);
        }

        
        if (transformSelect) {
            me.render(transformSelect.parentNode, transformSelect);
            Ext.removeNode(transformSelect);
            delete me.renderTo;
        }
    },

    
    getStore : function(){
        return this.store;
    },

    beforeBlur: function() {
        this.doQueryTask.cancel();
        this.assertValue();
    },

    
    assertValue: function() {
        var me = this,
            value = me.getRawValue(),
            rec, currentValue;

        if (me.forceSelection) {
            if (me.multiSelect) {
                
                
                if (value !== me.getDisplayValue()) {
                    me.setValue(me.lastSelection);
                }
            } else {
                
                
                rec = me.findRecordByDisplay(value);
                if (rec) {
                    currentValue = me.value;
                    
                    
                    if (!me.findRecordByValue(currentValue)) {
                        me.select(rec, true);
                    }
                } else {
                    me.setValue(me.lastSelection);
                }
            }
        }
        me.collapse();
    },

    onTypeAhead: function() {
        var me = this,
            displayField = me.displayField,
            record = me.store.findRecord(displayField, me.getRawValue()),
            boundList = me.getPicker(),
            newValue, len, selStart;

        if (record) {
            newValue = record.get(displayField);
            len = newValue.length;
            selStart = me.getRawValue().length;

            boundList.highlightItem(boundList.getNode(record));

            if (selStart !== 0 && selStart !== len) {
                me.setRawValue(newValue);
                me.selectText(selStart, newValue.length);
            }
        }
    },

    
    
    resetToDefault: Ext.emptyFn,

    beforeReset: function() {
        this.callParent();

        
        if (this.queryFilter && !this.queryFilter.disabled) {
            this.queryFilter.disabled = true;
            this.store.filter();
        }
    },

    onUnbindStore: function(store) {
        var me = this,
            picker = me.picker;

        
        if (me.queryFilter) {
            me.store.removeFilter(me.queryFilter);
        }
        if (!store && picker) {
            picker.bindStore(null);
        }
    },

    onBindStore: function(store, initial) {
        var picker = this.picker;
        if (!initial) {
            this.resetToDefault();
        }

        if (picker) {
            picker.bindStore(store);
        }
    },

    getStoreListeners: function() {
        var me = this;

        return {
            beforeload: me.onBeforeLoad,
            clear: me.onClear,
            datachanged: me.onDataChanged,
            load: me.onLoad,
            exception: me.onException,
            remove: me.onRemove
        }; 
    },

    onBeforeLoad: function(){
        
        
        
        ++this.ignoreSelection;    
    },

    onDataChanged: function() {
        var me = this;

        if (me.resizeComboToGrow()) {
            me.updateLayout();
        }
    },

    onClear: function() {
        var me = this;

        if (me.resizeComboToGrow()) {
            me.removingRecords = true;
            me.onDataChanged();
        }
    },

    onRemove: function() {
        var me = this;

        if (me.resizeComboToGrow()) {
            me.removingRecords = true;
        }
    },

    onException: function(){
        if (this.ignoreSelection > 0) {
            --this.ignoreSelection;
        }
        this.collapse();    
    },

    onLoad: function(store, records, success) {
        var me = this;

        if (me.ignoreSelection > 0) {
            --me.ignoreSelection;
        }

        
        if (success && !store.lastOptions.rawQuery) {
            

            
            if (me.value == null) {
                
                if (me.store.getCount()) {
                    me.doAutoSelect();
                } else {
                    
                    me.setValue(me.value);
                }
            } else {
                me.setValue(me.value);
            }
        }
    },

    
    doRawQuery: function() {
        this.doQuery(this.getRawValue(), false, true);
    },

    
    doQuery: function(queryString, forceAll, rawQuery) {
        var me = this,

            
            queryPlan = me.beforeQuery({
                query: queryString || '',
                rawQuery: rawQuery,
                forceAll: forceAll,
                combo: me,
                cancel: false
            });

        
        if (queryPlan === false || queryPlan.cancel) {
            return false;
        }

        
        if (me.queryCaching && queryPlan.query === me.lastQuery) {
            me.expand();
        }
        
        
        else {
            me.lastQuery = queryPlan.query;

            if (me.queryMode === 'local') {
                me.doLocalQuery(queryPlan);

            } else {
                me.doRemoteQuery(queryPlan);
            }
        }

        return true;
    },

    
    beforeQuery: function(queryPlan) {
        var me = this;

        
        if (me.fireEvent('beforequery', queryPlan) === false) {
            queryPlan.cancel = true;
        }

        
        else if (!queryPlan.cancel) {

            
            if (queryPlan.query.length < me.minChars && !queryPlan.forceAll) {
                queryPlan.cancel = true;
            }
        }
        return queryPlan;
    },

    doLocalQuery: function(queryPlan) {
        var me = this,
            queryString = queryPlan.query;

        
        if (!me.queryFilter) {
            
            me.queryFilter = new Ext.util.Filter({
                id: me.id + '-query-filter',
                anyMatch: me.anyMatch,
                caseSensitive: me.caseSensitive,
                root: 'data',
                property: me.displayField
            });
            me.store.addFilter(me.queryFilter, false);
        }

        
        if (queryString || !queryPlan.forceAll) {
            me.queryFilter.disabled = false;
            me.queryFilter.setValue(me.enableRegEx ? new RegExp(queryString) : queryString);
        }

        
        else {
            me.queryFilter.disabled = true;
        }

        
        me.store.filter();

        
        if (me.store.getCount()) {
            me.expand();
        } else {
            me.collapse();
        }

        me.afterQuery(queryPlan);
    },

    doRemoteQuery: function(queryPlan) {
        var me = this,
            loadCallback = function() {
                me.afterQuery(queryPlan);
            };

        
        me.expand();

        
        
        if (me.pageSize) {
            
            me.loadPage(1, {
                rawQuery: queryPlan.rawQuery,
                callback: loadCallback
            });
        } else {
            me.store.load({
                params: me.getParams(queryPlan.query),
                rawQuery: queryPlan.rawQuery,
                callback: loadCallback
            });
        }
    },

    
    afterQuery: function(queryPlan) {
        var me = this;

        if (me.store.getCount()) {
            if (me.typeAhead) {
                me.doTypeAhead();
            }

            
            if (me.getRawValue() !== me.getDisplayValue()) {
                me.ignoreSelection++;
                me.picker.getSelectionModel().deselectAll();
                me.ignoreSelection--;
            }

            if (queryPlan.rawQuery) {
                me.syncSelection();
                if (me.picker && !me.picker.getSelectionModel().hasSelection()) {
                    me.doAutoSelect();
                }
            } else {
                me.doAutoSelect();
            }
        }
    },

    loadPage: function(pageNum, options) {
        this.store.loadPage(pageNum, Ext.apply({
            params: this.getParams(this.lastQuery)
        }, options));
    },

    onPageChange: function(toolbar, newPage){
        
        this.loadPage(newPage);
        return false;
    },

    
    getParams: function(queryString) {
        var params = {},
            param = this.queryParam;

        if (param) {
            params[param] = queryString;
        }
        return params;
    },

    
    doAutoSelect: function() {
        var me = this,
            picker = me.picker,
            lastSelected, itemNode;
        if (picker && me.autoSelect && me.store.getCount() > 0) {
            
            lastSelected = picker.getSelectionModel().lastSelected;
            itemNode = picker.getNode(lastSelected || 0);
            if (itemNode) {
                picker.highlightItem(itemNode);
                picker.listEl.scrollChildIntoView(itemNode, false);
            }
        }
    },

    doTypeAhead: function() {
        if (!this.typeAheadTask) {
            this.typeAheadTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
        }
        if (this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE) {
            this.typeAheadTask.delay(this.typeAheadDelay);
        }
    },

    onTriggerClick: function() {
        var me = this;
        if (!me.readOnly && !me.disabled) {
            if (me.isExpanded) {
                me.collapse();
            } else {
                me.onFocus({});
                if (me.triggerAction === 'all') {
                    me.doQuery(me.allQuery, true);
                } else if (me.triggerAction === 'last') {
                    me.doQuery(me.lastQuery, true);
                } else {
                    me.doQuery(me.getRawValue(), false, true);
                }
            }
            me.inputEl.focus();
        }
    },

    onPaste: function(){
        var me = this;
        
        if (!me.readOnly && !me.disabled && me.editable) {
            me.doQueryTask.delay(me.queryDelay);
        }
    },

    
    onKeyUp: function(e, t) {
        var me = this,
            key = e.getKey();

        if (!me.readOnly && !me.disabled && me.editable) {
            me.lastKey = key;
            
            

            
            if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
                me.doQueryTask.delay(me.queryDelay);
            }
        }

        if (me.enableKeyEvents) {
            me.callParent(arguments);
        }
    },

    initEvents: function() {
        var me = this;
        me.callParent();

        
        if (!me.enableKeyEvents) {
            me.mon(me.inputEl, 'keyup', me.onKeyUp, me);
        }
        me.mon(me.inputEl, 'paste', me.onPaste, me);
    },

    onDestroy: function() {
        Ext.destroy(this.listKeyNav);
        this.bindStore(null);
        this.callParent();
    },

    
    
    onAdded: function() {
        var me = this;
        me.callParent(arguments);
        if (me.picker) {
            me.picker.ownerCt = me.up('[floating]');
            me.picker.registerWithOwnerCt();
        }
    },

    createPicker: function() {
        var me = this,
            picker,
            pickerCfg = Ext.apply({
                xtype: 'boundlist',
                pickerField: me,
                selModel: {
                    mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
                },
                floating: true,
                hidden: true,
                store: me.store,
                displayField: me.displayField,
                focusOnToFront: false,
                pageSize: me.pageSize,
                tpl: me.tpl
            }, me.listConfig, me.defaultListConfig);

        picker = me.picker = Ext.widget(pickerCfg);
        if (me.pageSize) {
            picker.pagingToolbar.on('beforechange', me.onPageChange, me);
        }

        me.mon(picker, {
            itemclick: me.onItemClick,
            refresh: me.onListRefresh,
            scope: me
        });

        me.mon(picker.getSelectionModel(), {
            beforeselect: me.onBeforeSelect,
            beforedeselect: me.onBeforeDeselect,
            selectionchange: me.onListSelectionChange,
            scope: me
        });

        return picker;
    },

    alignPicker: function(){
        var me = this,
            picker = me.getPicker(),
            heightAbove = me.getPosition()[1] - Ext.getBody().getScroll().top,
            heightBelow = Ext.Element.getViewHeight() - heightAbove - me.getHeight(),
            space = Math.max(heightAbove, heightBelow);

        
        if (picker.height) {
            delete picker.height;
            picker.updateLayout();
        }
        
        if (picker.getHeight() > space - 5) {
            picker.setHeight(space - 5); 
        }
        me.callParent();
    },

    onListRefresh: function() {
        
        if (!this.expanding) {
            this.alignPicker();
        }
        this.syncSelection();
    },

    onItemClick: function(picker, record){
        
        var me = this,
            selection = me.picker.getSelectionModel().getSelection(),
            valueField = me.valueField;

        if (!me.multiSelect && selection.length) {
            if (record.get(valueField) === selection[0].get(valueField)) {
                
                me.displayTplData = [record.data];
                me.setRawValue(me.getDisplayValue());
                me.collapse();
            }
        }
    },

    onBeforeSelect: function(list, record) {
        return this.fireEvent('beforeselect', this, record, record.index);
    },

    onBeforeDeselect: function(list, record) {
        return this.fireEvent('beforedeselect', this, record, record.index);
    },

    onListSelectionChange: function(list, selectedRecords) {
        var me = this,
            isMulti = me.multiSelect,
            hasRecords = selectedRecords.length > 0;
        
        
        if (!me.ignoreSelection && me.isExpanded) {
            if (!isMulti) {
                Ext.defer(me.collapse, 1, me);
            }
            
            if (isMulti || hasRecords) {
                me.setValue(selectedRecords, false);
            }
            if (hasRecords) {
                me.fireEvent('select', me, selectedRecords);
            }
            me.inputEl.focus();
        }
    },

    
    onExpand: function() {
        var me = this,
            keyNav = me.listKeyNav,
            selectOnTab = me.selectOnTab,
            picker = me.getPicker();

        
        if (keyNav) {
            keyNav.enable();
        } else {
            keyNav = me.listKeyNav = new Ext.view.BoundListKeyNav(this.inputEl, {
                boundList: picker,
                forceKeyDown: true,
                tab: function(e) {
                    if (selectOnTab) {
                        this.selectHighlighted(e);
                        me.triggerBlur();
                    }
                    
                    return true;
                },
                enter: function(e){
                    var selModel = picker.getSelectionModel(),
                        count = selModel.getCount();
                        
                    this.selectHighlighted(e);
                    
                    
                    
                    if (!me.multiSelect && count === selModel.getCount()) {
                        me.collapse();
                    }
                }
            });
        }

        
        if (selectOnTab) {
            me.ignoreMonitorTab = true;
        }

        Ext.defer(keyNav.enable, 1, keyNav); 
        me.inputEl.focus();
    },

    
    onCollapse: function() {
        var me = this,
            keyNav = me.listKeyNav;
        if (keyNav) {
            keyNav.disable();
            me.ignoreMonitorTab = false;
        }
    },

    
    select: function(r,  assert) {
        var me = this,
            picker = me.picker,
            doSelect = true,
            fireSelect;
        
        if (r && r.isModel && assert === true && picker) {
            fireSelect = !picker.getSelectionModel().isSelected(r);
        }
        
        me.setValue(r, true);
        
        
        if (fireSelect) {
            me.fireEvent('select', me, r);
        }
    },

    
    findRecord: function(field, value) {
        var ds = this.store,
            idx = ds.findExact(field, value);
        return idx !== -1 ? ds.getAt(idx) : false;
    },

    
    findRecordByValue: function(value) {
        return this.findRecord(this.valueField, value);
    },

    
    findRecordByDisplay: function(value) {
        return this.findRecord(this.displayField, value);
    },

    
    setValue: function(value, doSelect) {
        var me = this,
            valueNotFoundText = me.valueNotFoundText,
            inputEl = me.inputEl,
            i, len, record,
            dataObj,
            matchedRecords = [],
            displayTplData = [],
            processedValue = [];

        if (me.store.loading) {
            
            me.value = value;
            me.setHiddenValue(me.value);
            return me;
        }

        
        value = Ext.Array.from(value);

        
        for (i = 0, len = value.length; i < len; i++) {
            record = value[i];
            if (!record || !record.isModel) {
                record = me.findRecordByValue(record);
            }
            
            if (record) {
                matchedRecords.push(record);
                displayTplData.push(record.data);
                processedValue.push(record.get(me.valueField));
            }
            
            
            else {
                
                
                if (!me.forceSelection) {
                    processedValue.push(value[i]);
                    dataObj = {};
                    dataObj[me.displayField] = value[i];
                    displayTplData.push(dataObj);
                    
                }
                
                else if (Ext.isDefined(valueNotFoundText)) {
                    displayTplData.push(valueNotFoundText);
                }
            }
        }

        
        me.setHiddenValue(processedValue);
        me.value = me.multiSelect ? processedValue : processedValue[0];
        if (!Ext.isDefined(me.value)) {
            me.value = null;
        }
        me.displayTplData = displayTplData; 
        me.lastSelection = me.valueModels = matchedRecords;

        if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
            inputEl.removeCls(me.emptyCls);
        }

        
        me.setRawValue(me.getDisplayValue());
        me.checkChange();

        if (doSelect !== false) {
            me.syncSelection();
        }
        me.applyEmptyText();

        return me;
    },

    
    setHiddenValue: function(values){
        var me = this,
            name = me.hiddenName, 
            i,
            dom, childNodes, input, valueCount, childrenCount;
            
        if (!me.hiddenDataEl || !name) {
            return;
        }
        values = Ext.Array.from(values);
        dom = me.hiddenDataEl.dom;
        childNodes = dom.childNodes;
        input = childNodes[0];
        valueCount = values.length;
        childrenCount = childNodes.length;
        
        if (!input && valueCount > 0) {
            me.hiddenDataEl.update(Ext.DomHelper.markup({
                tag: 'input', 
                type: 'hidden', 
                name: name
            }));
            childrenCount = 1;
            input = dom.firstChild;
        }
        while (childrenCount > valueCount) {
            dom.removeChild(childNodes[0]);
            -- childrenCount;
        }
        while (childrenCount < valueCount) {
            dom.appendChild(input.cloneNode(true));
            ++ childrenCount;
        }
        for (i = 0; i < valueCount; i++) {
            childNodes[i].value = values[i];
        }
    },

    
    getDisplayValue: function() {
        return this.displayTpl.apply(this.displayTplData);
    },

    getValue: function() {
        
        
        
        var me = this,
            picker = me.picker,
            rawValue = me.getRawValue(), 
            value = me.value; 

        if (me.getDisplayValue() !== rawValue) {
            value = rawValue;
            me.value = me.displayTplData = me.valueModels = null;
            if (picker) {
                me.ignoreSelection++;
                picker.getSelectionModel().deselectAll();
                me.ignoreSelection--;
            }
        }

        return value;
    },

    getSubmitValue: function() {
        var value = this.getValue();
        
        
        if (Ext.isEmpty(value)) {
            value = '';
        }
        return value;
    },

    isEqual: function(v1, v2) {
        var fromArray = Ext.Array.from,
            i, len;

        v1 = fromArray(v1);
        v2 = fromArray(v2);
        len = v1.length;

        if (len !== v2.length) {
            return false;
        }

        for(i = 0; i < len; i++) {
            if (v2[i] !== v1[i]) {
                return false;
            }
        }

        return true;
    },

    
    clearValue: function() {
        this.setValue([]);
    },

    
    syncSelection: function() {
        var me = this,
            picker = me.picker,
            selection, selModel,
            values = me.valueModels || [],
            vLen  = values.length, v, value;

        if (picker) {
            
            selection = [];
            for (v = 0; v < vLen; v++) {
                value = values[v];

                if (value && value.isModel && me.store.indexOf(value) >= 0) {
                    selection.push(value);
                }
            }

            
            me.ignoreSelection++;
            selModel = picker.getSelectionModel();
            selModel.deselectAll();
            if (selection.length) {
                selModel.select(selection, undefined, true);
            }
            me.ignoreSelection--;
        }
    },
    
    onEditorTab: function(e){
        var keyNav = this.listKeyNav;
        
        if (this.selectOnTab && keyNav) {
            keyNav.selectHighlighted(e);
        }
    }
});


Ext.define('Ext.picker.Month', {
    extend:  Ext.Component ,
               
                         
                                  
                   
                           
      
    alias: 'widget.monthpicker',
    alternateClassName: 'Ext.MonthPicker',

    childEls: [
        'bodyEl', 'prevEl', 'nextEl', 'buttonsEl', 'monthEl', 'yearEl'
    ],

    renderTpl: [
        '<div id="{id}-bodyEl" class="{baseCls}-body">',
          '<div id="{id}-monthEl" class="{baseCls}-months">',
              '<tpl for="months">',
                  '<div class="{parent.baseCls}-item {parent.baseCls}-month">',
                      
                      '<a style="{parent.monthStyle}" hidefocus="on" class="{parent.baseCls}-item-inner" href="#">{.}</a>',
                  '</div>',
              '</tpl>',
          '</div>',
          '<div id="{id}-yearEl" class="{baseCls}-years">',
              '<div class="{baseCls}-yearnav">',
                  '<div class="{baseCls}-yearnav-button-ct">',
                      
                      '<a id="{id}-prevEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-prev" href="#" hidefocus="on" ></a>',
                  '</div>',
                  '<div class="{baseCls}-yearnav-button-ct">',
                      
                      '<a id="{id}-nextEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-next" href="#" hidefocus="on" ></a>',
                  '</div>',
              '</div>',
              '<tpl for="years">',
                  '<div class="{parent.baseCls}-item {parent.baseCls}-year">',
                      
                      '<a hidefocus="on" class="{parent.baseCls}-item-inner" href="#">{.}</a>',
                  '</div>',
              '</tpl>',
          '</div>',
          '<div class="' + Ext.baseCSSPrefix + 'clear"></div>',
        '</div>',
        '<tpl if="showButtons">',
            '<div id="{id}-buttonsEl" class="{baseCls}-buttons">{%',
                'var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;',
                'okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;',
                'okBtn.ownerCt = cancelBtn.ownerCt = me;',
                'Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);',
                'Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);',
            '%}</div>',
        '</tpl>'
    ],

    
    
    okText: 'OK',
    

    
    
    cancelText: 'Cancel',
    

    
    baseCls: Ext.baseCSSPrefix + 'monthpicker',

    
    showButtons: true,

    

    
    
    measureWidth: 35,
    measureMaxHeight: 20,

    
    smallCls: Ext.baseCSSPrefix + 'monthpicker-small',

    
    totalYears: 10,
    yearOffset: 5, 
    monthOffset: 6, 

    
    
    initComponent: function(){
        var me = this;

        me.selectedCls = me.baseCls + '-selected';
        me.addEvents(
            
            'cancelclick',

            
            'monthclick',

            
            'monthdblclick',

            
            'okclick',

            
            'select',

            
            'yearclick',

            
            'yeardblclick'
        );
        if (me.small) {
            me.addCls(me.smallCls);
        }
        me.setValue(me.value);
        me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);

        if (me.showButtons) {
            me.okBtn = new Ext.button.Button({
                text: me.okText,
                handler: me.onOkClick,
                scope: me
            });
            me.cancelBtn = new Ext.button.Button({
                text: me.cancelText,
                handler: me.onCancelClick,
                scope: me
            });
        }

        this.callParent();
    },

    
    
    beforeRender: function(){
        var me = this,
            i = 0,
            months = [],
            shortName = Ext.Date.getShortMonthName,
            monthLen = me.monthOffset,
            margin = me.monthMargin,
            style = '';

        me.callParent();

        for (; i < monthLen; ++i) {
            months.push(shortName(i), shortName(i + monthLen));
        }
        
        if (Ext.isDefined(margin)) {
            style = 'margin: 0 ' + margin + 'px;';
        }

        Ext.apply(me.renderData, {
            months: months,
            years: me.getYears(),
            showButtons: me.showButtons,
            monthStyle: style
        });
    },

    
    
    afterRender: function(){
        var me = this,
            body = me.bodyEl,
            buttonsEl = me.buttonsEl;

        me.callParent();

        me.mon(body, 'click', me.onBodyClick, me);
        me.mon(body, 'dblclick', me.onBodyClick, me);

        
        me.years = body.select('.' + me.baseCls + '-year a');
        me.months = body.select('.' + me.baseCls + '-month a');

        me.backRepeater = new Ext.util.ClickRepeater(me.prevEl, {
            handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears])
        });

        me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over');
        me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {
            handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears])
        });
        me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over');
        me.updateBody();
        
        if (!Ext.isDefined(me.monthMargin)) {
            Ext.picker.Month.prototype.monthMargin = me.calculateMonthMargin();
        }
    },
    
    calculateMonthMargin: function(){
        
        
        
        
        var me = this,
            monthEl = me.monthEl,
            months = me.months,
            first = months.first(),
            itemMargin = first.getMargin('l');
            
        while (itemMargin && me.getLargest() > me.measureMaxHeight) {
            --itemMargin;
            months.setStyle('margin', '0 ' + itemMargin + 'px');
        }
        return itemMargin;
    },
    
    getLargest: function(months){
        var largest = 0;
        this.months.each(function(item){
            var h = item.getHeight();
            if (h > largest) {
                largest = h;
            }
        });
        return largest;
        
    },

    
    setValue: function(value){
        var me = this,
            active = me.activeYear,
            offset = me.monthOffset,
            year,
            index;

        if (!value) {
            me.value = [null, null];
        } else if (Ext.isDate(value)) {
            me.value = [value.getMonth(), value.getFullYear()];
        } else {
            me.value = [value[0], value[1]];
        }

        if (me.rendered) {
            year = me.value[1];
            if (year !== null) {
                if ((year < active || year > active + me.yearOffset)) {
                    me.activeYear = year - me.yearOffset + 1;
                }
            }
            me.updateBody();
        }

        return me;
    },

    
    getValue: function(){
        return this.value;
    },

    
    hasSelection: function(){
        var value = this.value;
        return value[0] !== null && value[1] !== null;
    },

    
    getYears: function(){
        var me = this,
            offset = me.yearOffset,
            start = me.activeYear, 
            end = start + offset,
            i = start,
            years = [];

        for (; i < end; ++i) {
            years.push(i, i + offset);
        }

        return years;
    },

    
    updateBody: function(){
        var me = this,
            years = me.years,
            months = me.months,
            yearNumbers = me.getYears(),
            cls = me.selectedCls,
            value = me.getYear(null),
            month = me.value[0],
            monthOffset = me.monthOffset,
            year,
            yearItems, y, yLen, el;

        if (me.rendered) {
            years.removeCls(cls);
            months.removeCls(cls);

            yearItems = years.elements;
            yLen      = yearItems.length;

            for (y = 0; y < yLen; y++) {
                el = Ext.fly(yearItems[y]);

                year = yearNumbers[y];
                el.dom.innerHTML = year;
                if (year == value) {
                    el.addCls(cls);
                }
            }
            if (month !== null) {
                if (month < monthOffset) {
                    month = month * 2;
                } else {
                    month = (month - monthOffset) * 2 + 1;
                }
                months.item(month).addCls(cls);
            }
        }
    },

    
    getYear: function(defaultValue, offset) {
        var year = this.value[1];
        offset = offset || 0;
        return year === null ? defaultValue : year + offset;
    },

    
    onBodyClick: function(e, t) {
        var me = this,
            isDouble = e.type == 'dblclick';

        if (e.getTarget('.' + me.baseCls + '-month')) {
            e.stopEvent();
            me.onMonthClick(t, isDouble);
        } else if (e.getTarget('.' + me.baseCls + '-year')) {
            e.stopEvent();
            me.onYearClick(t, isDouble);
        }
    },

    
    adjustYear: function(offset){
        if (typeof offset != 'number') {
            offset = this.totalYears;
        }
        this.activeYear += offset;
        this.updateBody();
    },

    
    onOkClick: function(){
        this.fireEvent('okclick', this, this.value);
    },

    
    onCancelClick: function(){
        this.fireEvent('cancelclick', this);
    },

    
    onMonthClick: function(target, isDouble){
        var me = this;
        me.value[0] = me.resolveOffset(me.months.indexOf(target), me.monthOffset);
        me.updateBody();
        me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);
    },

    
    onYearClick: function(target, isDouble){
        var me = this;
        me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset);
        me.updateBody();
        me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);

    },

    
    resolveOffset: function(index, offset){
        if (index % 2 === 0) {
            return (index / 2);
        } else {
            return offset + Math.floor(index / 2);
        }
    },

    
    
    beforeDestroy: function(){
        var me = this;
        me.years = me.months = null;
        Ext.destroyMembers(me, 'backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');
        me.callParent();
    },

    
    
    finishRenderChildren: function () {
        var me = this;

        this.callParent(arguments);

        if (this.showButtons) {
            me.okBtn.finishRender();
            me.cancelBtn.finishRender();
        }
    },

    onDestroy: function() {
        Ext.destroyMembers(this, 'okBtn', 'cancelBtn');
        this.callParent();
    }
    
});


Ext.define('Ext.picker.Date', {
    extend:  Ext.Component ,
               
                        
                            
                           
                                 
                          
                          
                         
                          
      
    alias: 'widget.datepicker',
    alternateClassName: 'Ext.DatePicker',

    childEls: [
        'innerEl', 'eventEl', 'prevEl', 'nextEl', 'middleBtnEl', 'footerEl'
    ],
    
    border: true,

    renderTpl: [
        '<div id="{id}-innerEl" role="grid">',
            '<div role="presentation" class="{baseCls}-header">',
                 
                '<a id="{id}-prevEl" class="{baseCls}-prev {baseCls}-arrow" href="#" role="button" title="{prevText}" hidefocus="on" ></a>',
                '<div class="{baseCls}-month" id="{id}-middleBtnEl">{%this.renderMonthBtn(values, out)%}</div>',
                 
                '<a id="{id}-nextEl" class="{baseCls}-next {baseCls}-arrow" href="#" role="button" title="{nextText}" hidefocus="on" ></a>',
            '</div>',
            '<table id="{id}-eventEl" class="{baseCls}-inner" cellspacing="0" role="grid">',
                '<thead role="presentation"><tr role="row">',
                    '<tpl for="dayNames">',
                        '<th role="columnheader" class="{parent.baseCls}-column-header" title="{.}">',
                            '<div class="{parent.baseCls}-column-header-inner">{.:this.firstInitial}</div>',
                        '</th>',
                    '</tpl>',
                '</tr></thead>',
                '<tbody role="presentation"><tr role="row">',
                    '<tpl for="days">',
                        '{#:this.isEndOfWeek}',
                        '<td role="gridcell" id="{[Ext.id()]}">',
                            
                            '<a role="presentation" hidefocus="on" class="{parent.baseCls}-date" href="#"></a>',
                        '</td>',
                    '</tpl>',
                '</tr></tbody>',
            '</table>',
            '<tpl if="showToday">',
                '<div id="{id}-footerEl" role="presentation" class="{baseCls}-footer">{%this.renderTodayBtn(values, out)%}</div>',
            '</tpl>',
        '</div>',
        {
            firstInitial: function(value) {
                return Ext.picker.Date.prototype.getDayInitial(value);
            },
            isEndOfWeek: function(value) {
                
                
                value--;
                var end = value % 7 === 0 && value !== 0;
                return end ? '</tr><tr role="row">' : '';
            },
            renderTodayBtn: function(values, out) {
                Ext.DomHelper.generateMarkup(values.$comp.todayBtn.getRenderTree(), out);
            },
            renderMonthBtn: function(values, out) {
                Ext.DomHelper.generateMarkup(values.$comp.monthBtn.getRenderTree(), out);
            }
        }
    ],

    
    
    todayText : 'Today',
    
    
    
    
    ariaTitle: 'Date Picker: {0}',
    
    
    
    
    ariaTitleDateFormat: 'F d, Y',
    

    

    

    
    
    todayTip : '{0} (Spacebar)',
    

    
    
    minText : 'This date is before the minimum date',
    

    
    
    maxText : 'This date is after the maximum date',
    

    

    
    
    disabledDaysText : 'Disabled',
    

    
    
    disabledDatesText : 'Disabled',
    

    

    

    
    
    nextText : 'Next Month (Control+Right)',
    

    
    
    prevText : 'Previous Month (Control+Left)',
    

    
    
    monthYearText : 'Choose a month (Control+Up/Down to move years)',
    
    
    
    
    monthYearFormat: 'F Y',
    

    
    
    startDay : 0,
    

    
    
    showToday : true,
    

    

    

    

    

    

    
    disableAnim: false,

    
    baseCls: Ext.baseCSSPrefix + 'datepicker',

    

    

    
    
    longDayFormat: 'F d, Y',
    

    

    
    focusOnShow: false,

    
    
    focusOnSelect: true,

    
    
    initHour: 12, 

    numDays: 42,

    
    initComponent : function() {
        var me = this,
            clearTime = Ext.Date.clearTime;

        me.selectedCls = me.baseCls + '-selected';
        me.disabledCellCls = me.baseCls + '-disabled';
        me.prevCls = me.baseCls + '-prevday';
        me.activeCls = me.baseCls + '-active';
        me.cellCls = me.baseCls + '-cell';
        me.nextCls = me.baseCls + '-prevday';
        me.todayCls = me.baseCls + '-today';
        
        
        if (!me.format) {
            me.format = Ext.Date.defaultFormat;
        }
        if (!me.dayNames) {
            me.dayNames = Ext.Date.dayNames;
        }
        me.dayNames = me.dayNames.slice(me.startDay).concat(me.dayNames.slice(0, me.startDay));

        me.callParent();

        me.value = me.value ?
                 clearTime(me.value, true) : clearTime(new Date());

        me.addEvents(
            
            'select'
        );

        me.initDisabledDays();
    },

    beforeRender: function () {
        
        var me = this,
            days = new Array(me.numDays),
            today = Ext.Date.format(new Date(), me.format);

        
        
        if (me.up('menu')) {
            me.addCls(Ext.baseCSSPrefix + 'menu');
        }

        me.monthBtn = new Ext.button.Split({
            ownerCt: me,
            ownerLayout: me.getComponentLayout(),
            text: '',
            tooltip: me.monthYearText,
            listeners: {
                click: me.showMonthPicker,
                arrowclick: me.showMonthPicker,
                scope: me
            }
        });

        if (me.showToday) {
            me.todayBtn = new Ext.button.Button({
                ownerCt: me,
                ownerLayout: me.getComponentLayout(),
                text: Ext.String.format(me.todayText, today),
                tooltip: Ext.String.format(me.todayTip, today),
                tooltipType: 'title',
                handler: me.selectToday,
                scope: me
            });
        }

        me.callParent();

        Ext.applyIf(me, {
            renderData: {}
        });

        Ext.apply(me.renderData, {
            dayNames: me.dayNames,
            showToday: me.showToday,
            prevText: me.prevText,
            nextText: me.nextText,
            days: days
        });

        me.protoEl.unselectable();
    },

    
    
    finishRenderChildren: function () {
        var me = this;
        
        me.callParent();
        me.monthBtn.finishRender();
        if (me.showToday) {
            me.todayBtn.finishRender();
        }
    },

    
    
    onRender : function(container, position){
        var me = this;

        me.callParent(arguments);

        me.cells = me.eventEl.select('tbody td');
        me.textNodes = me.eventEl.query('tbody td a');
        
        me.mon(me.eventEl, {
            scope: me,
            mousewheel: me.handleMouseWheel,
            click: {
                fn: me.handleDateClick,
                delegate: 'a.' + me.baseCls + '-date'
            }
        });
        
    },

    
    
    initEvents: function(){
        var me = this,
            eDate = Ext.Date,
            day = eDate.DAY;

        me.callParent();

        me.prevRepeater = new Ext.util.ClickRepeater(me.prevEl, {
            handler: me.showPrevMonth,
            scope: me,
            preventDefault: true,
            stopDefault: true
        });

        me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {
            handler: me.showNextMonth,
            scope: me,
            preventDefault:true,
            stopDefault:true
        });

        me.keyNav = new Ext.util.KeyNav(me.eventEl, Ext.apply({
            scope: me,
            left : function(e){
                if(e.ctrlKey){
                    me.showPrevMonth();
                }else{
                    me.update(eDate.add(me.activeDate, day, -1));
                }
            },

            right : function(e){
                if(e.ctrlKey){
                    me.showNextMonth();
                }else{
                    me.update(eDate.add(me.activeDate, day, 1));
                }
            },

            up : function(e){
                if(e.ctrlKey){
                    me.showNextYear();
                }else{
                    me.update(eDate.add(me.activeDate, day, -7));
                }
            },

            down : function(e){
                if(e.ctrlKey){
                    me.showPrevYear();
                }else{
                    me.update(eDate.add(me.activeDate, day, 7));
                }
            },

            pageUp:function (e) {
                if (e.altKey) {
                    me.showPrevYear();
                } else {
                    me.showPrevMonth();
                }
            },

            pageDown:function (e) {
                if (e.altKey) {
                    me.showNextYear();
                } else {
                    me.showNextMonth();
                }
            },

            tab:function (e) {
                me.doCancelFieldFocus = true;
                me.handleTabClick(e);
                delete me.doCancelFieldFocus;
                return true;
            },
            
            enter : function(e){
                e.stopPropagation();
                return true;
            },

            

            home:function (e) {
                me.update(eDate.getFirstDateOfMonth(me.activeDate));
            },

            end:function (e) {
                me.update(eDate.getLastDateOfMonth(me.activeDate));
            }
        }, me.keyNavConfig));

        if (me.showToday) {
            me.todayKeyListener = me.eventEl.addKeyListener(Ext.EventObject.SPACE, me.selectToday,  me);
        }
        me.update(me.value);
    },

    handleTabClick:function (e) {
        var me = this,
            t = me.getSelectedDate(me.activeDate),
            handler = me.handler;

        
        if (!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
            me.doCancelFocus = me.focusOnSelect === false;
            me.setValue(new Date(t.dateValue));
            delete me.doCancelFocus;
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            me.onSelect();
        }
    },

    getSelectedDate:function (date) {
        var me = this,
            t = date.getTime(),
            cells = me.cells,
            cls = me.selectedCls,
            cellItems = cells.elements,
            c,
            cLen = cellItems.length,
            cell;

        cells.removeCls(cls);

        for (c = 0; c < cLen; c++) {
            cell = Ext.fly(cellItems[c]);

            if (cell.dom.firstChild.dateValue == t) {
                return cell.dom.firstChild;
            }
        }
        return null;
    },

    
    initDisabledDays : function(){
        var me = this,
            dd = me.disabledDates,
            re = '(?:',
            len,
            d, dLen, dI;

        if(!me.disabledDatesRE && dd){
                len = dd.length - 1;

            dLen = dd.length;

            for (d = 0; d < dLen; d++) {
                dI = dd[d];

                re += Ext.isDate(dI) ? '^' + Ext.String.escapeRegex(Ext.Date.dateFormat(dI, me.format)) + '$' : dI;
                if (d != len) {
                    re += '|';
                }
            }

            me.disabledDatesRE = new RegExp(re + ')');
        }
    },

    
    setDisabledDates : function(dd){
        var me = this;

        if(Ext.isArray(dd)){
            me.disabledDates = dd;
            me.disabledDatesRE = null;
        }else{
            me.disabledDatesRE = dd;
        }
        me.initDisabledDays();
        me.update(me.value, true);
        return me;
    },

    
    setDisabledDays : function(dd){
        this.disabledDays = dd;
        return this.update(this.value, true);
    },

    
    setMinDate : function(dt){
        this.minDate = dt;
        return this.update(this.value, true);
    },

    
    setMaxDate : function(dt){
        this.maxDate = dt;
        return this.update(this.value, true);
    },

    
    setValue : function(value){
        this.value = Ext.Date.clearTime(value, true);
        return this.update(this.value);
    },

    
    getValue : function(){
        return this.value;
    },

    
    
    getDayInitial: function(value){
        return value.substr(0,1);
    },
    

    
    focus : function(){
        this.update(this.activeDate);
    },

    
    
    onEnable: function(){
        this.callParent();
        this.setDisabledStatus(false);
        this.update(this.activeDate);

    },

    
    
    onDisable : function(){
        this.callParent();
        this.setDisabledStatus(true);
    },

    
    setDisabledStatus : function(disabled){
        var me = this;

        me.keyNav.setDisabled(disabled);
        me.prevRepeater.setDisabled(disabled);
        me.nextRepeater.setDisabled(disabled);
        if (me.showToday) {
            me.todayKeyListener.setDisabled(disabled);
            me.todayBtn.setDisabled(disabled);
        }
    },

    
    getActive: function(){
        return this.activeDate || this.value;
    },

    
    runAnimation: function(isHide){
        var picker = this.monthPicker,
            options = {
                duration: 200,
                callback: function(){
                    if (isHide) {
                        picker.hide();
                    } else {
                        picker.show();
                    }
                }
            };

        if (isHide) {
            picker.el.slideOut('t', options);
        } else {
            picker.el.slideIn('t', options);
        }
    },

    
    hideMonthPicker : function(animate){
        var me = this,
            picker = me.monthPicker;

        if (picker) {
            if (me.shouldAnimate(animate)) {
                me.runAnimation(true);
            } else {
                picker.hide();
            }
        }
        return me;
    },

    
    showMonthPicker : function(animate){
        var me = this,
            picker;
        
        if (me.rendered && !me.disabled) {
            picker = me.createMonthPicker();
            picker.setValue(me.getActive());
            picker.setSize(me.getSize());
            picker.setPosition(-1, -1);
            if (me.shouldAnimate(animate)) {
                me.runAnimation(false);
            } else {
                picker.show();
            }
        }
        return me;
    },
    
    
    shouldAnimate: function(animate){
        return Ext.isDefined(animate) ? animate : !this.disableAnim;
    },

    
    createMonthPicker: function(){
        var me = this,
            picker = me.monthPicker;

        if (!picker) {
            me.monthPicker = picker = new Ext.picker.Month({
                renderTo: me.el,
                floating: true,
                shadow: false,
                small: me.showToday === false,
                listeners: {
                    scope: me,
                    cancelclick: me.onCancelClick,
                    okclick: me.onOkClick,
                    yeardblclick: me.onOkClick,
                    monthdblclick: me.onOkClick
                }
            });
            if (!me.disableAnim) {
                
                picker.el.setStyle('display', 'none');
            }
            me.on('beforehide', Ext.Function.bind(me.hideMonthPicker, me, [false]));
        }
        return picker;
    },

    
    onOkClick: function(picker, value){
        var me = this,
            month = value[0],
            year = value[1],
            date = new Date(year, month, me.getActive().getDate());

        if (date.getMonth() !== month) {
            
            date = Ext.Date.getLastDateOfMonth(new Date(year, month, 1));
        }
        me.setValue(date);
        me.hideMonthPicker();
    },

    
    onCancelClick: function(){
        
        this.selectedUpdate(this.activeDate);
        this.hideMonthPicker();
    },

    
    showPrevMonth : function(e){
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, -1));
    },

    
    showNextMonth : function(e){
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH, 1));
    },

    
    showPrevYear : function(){
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, -1));
    },

    
    showNextYear : function(){
        return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR, 1));
    },

    
    handleMouseWheel : function(e){
        e.stopEvent();
        if(!this.disabled){
            var delta = e.getWheelDelta();
            if(delta > 0){
                this.showPrevMonth();
            } else if(delta < 0){
                this.showNextMonth();
            }
        }
    },

    
    handleDateClick : function(e, t){
        var me = this,
            handler = me.handler;

        e.stopEvent();
        if(!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)){
            me.doCancelFocus = me.focusOnSelect === false;
            me.setValue(new Date(t.dateValue));
            delete me.doCancelFocus;
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            
            
            
            
            me.onSelect();
        }
    },

    
    onSelect: function() {
        if (this.hideOnSelect) {
             this.hide();
         }
    },

    
    selectToday : function(){
        var me = this,
            btn = me.todayBtn,
            handler = me.handler;

        if(btn && !btn.disabled){
            me.setValue(Ext.Date.clearTime(new Date()));
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            me.onSelect();
        }
        return me;
    },

    
    selectedUpdate: function(date){
        var me        = this,
            t         = date.getTime(),
            cells     = me.cells,
            cls       = me.selectedCls,
            cellItems = cells.elements,
            c,
            cLen      = cellItems.length,
            cell;

        cells.removeCls(cls);

        for (c = 0; c < cLen; c++) {
            cell = Ext.fly(cellItems[c]);

            if (cell.dom.firstChild.dateValue == t) {
                me.fireEvent('highlightitem', me, cell);
                cell.addCls(cls);

                if(me.isVisible() && !me.doCancelFocus){
                    Ext.fly(cell.dom.firstChild).focus(50);
                }

                break;
            }
        }
    },

    
    fullUpdate: function(date){
        var me = this,
            cells = me.cells.elements,
            textNodes = me.textNodes,
            disabledCls = me.disabledCellCls,
            eDate = Ext.Date,
            i = 0,
            extraDays = 0,
            visible = me.isVisible(),
            newDate = +eDate.clearTime(date, true),
            today = +eDate.clearTime(new Date()),
            min = me.minDate ? eDate.clearTime(me.minDate, true) : Number.NEGATIVE_INFINITY,
            max = me.maxDate ? eDate.clearTime(me.maxDate, true) : Number.POSITIVE_INFINITY,
            ddMatch = me.disabledDatesRE,
            ddText = me.disabledDatesText,
            ddays = me.disabledDays ? me.disabledDays.join('') : false,
            ddaysText = me.disabledDaysText,
            format = me.format,
            days = eDate.getDaysInMonth(date),
            firstOfMonth = eDate.getFirstDateOfMonth(date),
            startingPos = firstOfMonth.getDay() - me.startDay,
            previousMonth = eDate.add(date, eDate.MONTH, -1),
            longDayFormat = me.longDayFormat,
            prevStart,
            current,
            disableToday,
            tempDate,
            setCellClass,
            html,
            cls,
            formatValue,
            value;

        if (startingPos < 0) {
            startingPos += 7;
        }

        days += startingPos;
        prevStart = eDate.getDaysInMonth(previousMonth) - startingPos;
        current = new Date(previousMonth.getFullYear(), previousMonth.getMonth(), prevStart, me.initHour);

        if (me.showToday) {
            tempDate = eDate.clearTime(new Date());
            disableToday = (tempDate < min || tempDate > max ||
                (ddMatch && format && ddMatch.test(eDate.dateFormat(tempDate, format))) ||
                (ddays && ddays.indexOf(tempDate.getDay()) != -1));

            if (!me.disabled) {
                me.todayBtn.setDisabled(disableToday);
                me.todayKeyListener.setDisabled(disableToday);
            }
        }

        setCellClass = function(cell, cls){
            value = +eDate.clearTime(current, true);
            cell.title = eDate.format(current, longDayFormat);
            
            cell.firstChild.dateValue = value;
            if(value == today){
                cls += ' ' + me.todayCls;
                cell.title = me.todayText;
                
                
                me.todayElSpan = Ext.DomHelper.append(cell.firstChild, {
                    tag:'span',
                    cls: Ext.baseCSSPrefix + 'hide-clip',
                    html:me.todayText
                }, true);
            }
            if(value == newDate) {
                cls += ' ' + me.selectedCls;
                me.fireEvent('highlightitem', me, cell);
                if (visible && me.floating) {
                    Ext.fly(cell.firstChild).focus(50);
                }
            }

            if (value < min) {
                cls += ' ' + disabledCls;
                cell.title = me.minText;
            }
            else if (value > max) {
                cls += ' ' + disabledCls;
                cell.title = me.maxText;
            }
            else if (ddays && ddays.indexOf(current.getDay()) !== -1){
                cell.title = ddaysText;
                cls += ' ' + disabledCls;
            }
            else if (ddMatch && format){
                formatValue = eDate.dateFormat(current, format);
                if(ddMatch.test(formatValue)){
                    cell.title = ddText.replace('%0', formatValue);
                    cls += ' ' + disabledCls;
                }
            }
            cell.className = cls + ' ' + me.cellCls;
        };

        for(; i < me.numDays; ++i) {
            if (i < startingPos) {
                html = (++prevStart);
                cls = me.prevCls;
            } else if (i >= days) {
                html = (++extraDays);
                cls = me.nextCls;
            } else {
                html = i - startingPos + 1;
                cls = me.activeCls;
            }
            textNodes[i].innerHTML = html;
            current.setDate(current.getDate() + 1);
            setCellClass(cells[i], cls);
        }

        me.monthBtn.setText(Ext.Date.format(date, me.monthYearFormat));
    },

    
    update : function(date, forceRefresh){
        var me = this,
            active = me.activeDate;

        if (me.rendered) {
            me.activeDate = date;
            if(!forceRefresh && active && me.el && active.getMonth() == date.getMonth() && active.getFullYear() == date.getFullYear()){
                me.selectedUpdate(date, active);
            } else {
                me.fullUpdate(date, active);
            }
        }
        return me;
    },

    
    
    beforeDestroy : function() {
        var me = this;

        if (me.rendered) {
            Ext.destroy(
                me.todayKeyListener,
                me.keyNav,
                me.monthPicker,
                me.monthBtn,
                me.nextRepeater,
                me.prevRepeater,
                me.todayBtn
            );
            delete me.textNodes;
            delete me.cells.elements;
        }
        me.callParent();
    },

    
    
    onShow: function() {
        this.callParent(arguments);
        if (this.focusOnShow) {
            this.focus();
        }
    }
});


Ext.define('Ext.form.field.Date', {
    extend: Ext.form.field.Picker ,
    alias: 'widget.datefield',
                                  
    alternateClassName: ['Ext.form.DateField', 'Ext.form.Date'],

    
    
    format : "m/d/Y",
    
    
    
    altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d|n-j|n/j",
    
    
    
    disabledDaysText : "Disabled",
    
    
    
    disabledDatesText : "Disabled",
    
    
    
    minText : "The date in this field must be equal to or after {0}",
    
    
    
    maxText : "The date in this field must be equal to or before {0}",
    
    
    
    invalidText : "{0} is not a valid date - it must be in the format {1}",
    
    
    triggerCls : Ext.baseCSSPrefix + 'form-date-trigger',
    
    showToday : true,
    
    
    
    

    
    
    
    useStrict: undefined,

    
    
    initTime: '12', 

    initTimeFormat: 'H',

    matchFieldWidth: false,
    
    
    startDay: 0,
    

    initComponent : function(){
        var me = this,
            isString = Ext.isString,
            min, max;

        min = me.minValue;
        max = me.maxValue;
        if(isString(min)){
            me.minValue = me.parseDate(min);
        }
        if(isString(max)){
            me.maxValue = me.parseDate(max);
        }
        me.disabledDatesRE = null;
        me.initDisabledDays();

        me.callParent();
    },

    initValue: function() {
        var me = this,
            value = me.value;

        
        if (Ext.isString(value)) {
            me.value = me.rawToValue(value);
        }

        me.callParent();
    },

    
    initDisabledDays : function(){
        if(this.disabledDates){
            var dd   = this.disabledDates,
                len  = dd.length - 1,
                re   = "(?:",
                d,
                dLen = dd.length,
                date;

            for (d = 0; d < dLen; d++) {
                date = dd[d];

                re += Ext.isDate(date) ? '^' + Ext.String.escapeRegex(date.dateFormat(this.format)) + '$' : date;
                if (d !== len) {
                    re += '|';
                }
            }

            this.disabledDatesRE = new RegExp(re + ')');
        }
    },

    
    setDisabledDates : function(dd){
        var me = this,
            picker = me.picker;

        me.disabledDates = dd;
        me.initDisabledDays();
        if (picker) {
            picker.setDisabledDates(me.disabledDatesRE);
        }
    },

    
    setDisabledDays : function(dd){
        var picker = this.picker;

        this.disabledDays = dd;
        if (picker) {
            picker.setDisabledDays(dd);
        }
    },

    
    setMinValue : function(dt){
        var me = this,
            picker = me.picker,
            minValue = (Ext.isString(dt) ? me.parseDate(dt) : dt);

        me.minValue = minValue;
        if (picker) {
            picker.minText = Ext.String.format(me.minText, me.formatDate(me.minValue));
            picker.setMinDate(minValue);
        }
    },

    
    setMaxValue : function(dt){
        var me = this,
            picker = me.picker,
            maxValue = (Ext.isString(dt) ? me.parseDate(dt) : dt);

        me.maxValue = maxValue;
        if (picker) {
            picker.maxText = Ext.String.format(me.maxText, me.formatDate(me.maxValue));
            picker.setMaxDate(maxValue);
        }
    },

    
    getErrors: function(value) {
        var me = this,
            format = Ext.String.format,
            clearTime = Ext.Date.clearTime,
            errors = me.callParent(arguments),
            disabledDays = me.disabledDays,
            disabledDatesRE = me.disabledDatesRE,
            minValue = me.minValue,
            maxValue = me.maxValue,
            len = disabledDays ? disabledDays.length : 0,
            i = 0,
            svalue,
            fvalue,
            day,
            time;

        value = me.formatDate(value || me.processRawValue(me.getRawValue()));

        if (value === null || value.length < 1) { 
             return errors;
        }

        svalue = value;
        value = me.parseDate(value);
        if (!value) {
            errors.push(format(me.invalidText, svalue, Ext.Date.unescapeFormat(me.format)));
            return errors;
        }

        time = value.getTime();
        if (minValue && time < clearTime(minValue).getTime()) {
            errors.push(format(me.minText, me.formatDate(minValue)));
        }

        if (maxValue && time > clearTime(maxValue).getTime()) {
            errors.push(format(me.maxText, me.formatDate(maxValue)));
        }

        if (disabledDays) {
            day = value.getDay();

            for(; i < len; i++) {
                if (day === disabledDays[i]) {
                    errors.push(me.disabledDaysText);
                    break;
                }
            }
        }

        fvalue = me.formatDate(value);
        if (disabledDatesRE && disabledDatesRE.test(fvalue)) {
            errors.push(format(me.disabledDatesText, fvalue));
        }

        return errors;
    },

    rawToValue: function(rawValue) {
        return this.parseDate(rawValue) || rawValue || null;
    },

    valueToRaw: function(value) {
        return this.formatDate(this.parseDate(value));
    },

    

    
    safeParse : function(value, format) {
        var me = this,
            utilDate = Ext.Date,
            result = null,
            strict = me.useStrict,
            parsedDate;

        if (utilDate.formatContainsHourInfo(format)) {
            
            result = utilDate.parse(value, format, strict);
        } else {
            
            parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat, strict);
            if (parsedDate) {
                result = utilDate.clearTime(parsedDate);
            }
        }
        return result;
    },

    
    getSubmitValue: function() {
        var format = this.submitFormat || this.format,
            value = this.getValue();

        return value ? Ext.Date.format(value, format) : '';
    },

    
    parseDate : function(value) {
        if(!value || Ext.isDate(value)){
            return value;
        }

        var me = this,
            val = me.safeParse(value, me.format),
            altFormats = me.altFormats,
            altFormatsArray = me.altFormatsArray,
            i = 0,
            len;

        if (!val && altFormats) {
            altFormatsArray = altFormatsArray || altFormats.split('|');
            len = altFormatsArray.length;
            for (; i < len && !val; ++i) {
                val = me.safeParse(value, altFormatsArray[i]);
            }
        }
        return val;
    },

    
    formatDate : function(date){
        return Ext.isDate(date) ? Ext.Date.dateFormat(date, this.format) : date;
    },

    createPicker: function() {
        var me = this,
            format = Ext.String.format;

        return new Ext.picker.Date({
            pickerField: me,
            ownerCt: me.ownerCt,
            renderTo: document.body,
            floating: true,
            hidden: true,
            focusOnShow: true,
            minDate: me.minValue,
            maxDate: me.maxValue,
            disabledDatesRE: me.disabledDatesRE,
            disabledDatesText: me.disabledDatesText,
            disabledDays: me.disabledDays,
            disabledDaysText: me.disabledDaysText,
            format: me.format,
            showToday: me.showToday,
            startDay: me.startDay,
            minText: format(me.minText, me.formatDate(me.minValue)),
            maxText: format(me.maxText, me.formatDate(me.maxValue)),
            listeners: {
                scope: me,
                select: me.onSelect
            },
            keyNavConfig: {
                esc: function() {
                    me.collapse();
                }
            }
        });
    },
    
    onDownArrow: function(e) {
        this.callParent(arguments);
        if (this.isExpanded) {
            this.getPicker().focus();
        }
    },

    onSelect: function(m, d) {
        var me = this;

        me.setValue(d);
        me.fireEvent('select', me, d);
        me.collapse();
    },

    
    onExpand: function() {
        var value = this.getValue();
        this.picker.setValue(Ext.isDate(value) ? value : new Date());
    },

    
    onCollapse: function() {
        this.focus(false, 60);
    },

    
    beforeBlur : function(){
        var me = this,
            v = me.parseDate(me.getRawValue()),
            focusTask = me.focusTask;

        if (focusTask) {
            focusTask.cancel();
        }

        if (v) {
            me.setValue(v);
        }
    }

    
    
    
    
});


Ext.define('Ext.form.field.FileButton', {
    extend:  Ext.button.Button ,
    alias: 'widget.filebutton',
    
    childEls: [
        'btnEl', 'btnWrap', 'btnInnerEl', 'btnIconEl', 'fileInputEl'
    ],
    
    inputCls: Ext.baseCSSPrefix + 'form-file-input',
    
    cls: Ext.baseCSSPrefix + 'form-file-btn',
    
    preventDefault: false,

    renderTpl: [
        '<span id="{id}-btnWrap" class="{baseCls}-wrap',
            '<tpl if="splitCls"> {splitCls}</tpl>',
            '{childElCls}" unselectable="on">',
            '<span id="{id}-btnEl" class="{baseCls}-button">',
                '<span id="{id}-btnInnerEl" class="{baseCls}-inner {innerCls}',
                    '{childElCls}" unselectable="on">',
                    '{text}',
                '</span>',
                '<span role="img" id="{id}-btnIconEl" class="{baseCls}-icon-el {iconCls}',
                    '{childElCls} {glyphCls}" unselectable="on" style="',
                    '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>',
                    '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">',
                    '<tpl if="glyph">&#{glyph};</tpl><tpl if="iconCls || iconUrl">&#160;</tpl>',
                '</span>',
            '</span>',
        '</span>',
        '<input id="{id}-fileInputEl" class="{childElCls} {inputCls}" type="file" size="1" name="{inputName}">'
    ],
    
    getTemplateArgs: function(){
        var args = this.callParent();
        args.inputCls = this.inputCls;
        args.inputName = this.inputName;
        return args;
    },
    
    afterRender: function(){
        var me = this;
        me.callParent(arguments);
        me.fileInputEl.on('change', me.fireChange, me);    
    },
    
    fireChange: function(e){
        this.fireEvent('change', this, e, this.fileInputEl.dom.value);
    },
    
    
    createFileInput : function(isTemporary) {
        var me = this;
        me.fileInputEl = me.el.createChild({
            name: me.inputName,
            id: !isTemporary ? me.id + '-fileInputEl' : undefined,
            cls: me.inputCls,
            tag: 'input',
            type: 'file',
            size: 1
        });
        me.fileInputEl.on('change', me.fireChange, me);  
    },
    
    reset: function(remove){
        if (remove) {
            this.fileInputEl.remove();
        }
        this.createFileInput(!remove);
    },
    
    restoreInput: function(el){
        this.fileInputEl.remove();
        el = Ext.get(el);
        this.el.appendChild(el);
        this.fileInputEl = el;
    },
    
    onDisable: function(){
        this.callParent();
        this.fileInputEl.dom.disabled = true;
    },

    onEnable : function() {
        this.callParent();
        this.fileInputEl.dom.disabled = false;
    }
});


Ext.define('Ext.form.field.File', {
    extend:  Ext.form.field.Trigger ,
    alias: ['widget.filefield', 'widget.fileuploadfield'],
    alternateClassName: ['Ext.form.FileUploadField', 'Ext.ux.form.FileUploadField', 'Ext.form.File'],
               
                                   
      

    
    
    buttonText: 'Browse...',
    

    
    buttonOnly: false,

    
    buttonMargin: 3,
    
    
    clearOnSubmit: true,

    

    

    

    


    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'form-file-wrap',

    
    readOnly: true,

    
    triggerNoEditCls: '',

    
    componentLayout: 'triggerfield',

    
    childEls: ['browseButtonWrap'],

    
    onRender: function() {
        var me = this,
            id = me.id,
            inputEl;

        me.callParent(arguments);

        inputEl = me.inputEl;
        inputEl.dom.name = ''; 

        
        
        
        me.button = new Ext.form.field.FileButton(Ext.apply({
            renderTo: id + '-browseButtonWrap',
            ownerCt: me,
            ownerLayout: me.componentLayout,
            id: id + '-button',
            ui: me.ui,
            disabled: me.disabled,
            text: me.buttonText,
            style: me.buttonOnly ? '' : me.getButtonMarginProp() + me.buttonMargin + 'px',
            inputName: me.getName(),
            listeners: {
                scope: me,
                change: me.onFileChange
            }
        }, me.buttonConfig));
        me.fileInputEl = me.button.fileInputEl;

        if (me.buttonOnly) {
            me.inputCell.setDisplayed(false);
        }

        
        me.browseButtonWrap.dom.style.width = (me.browseButtonWrap.dom.lastChild.offsetWidth + me.button.getEl().getMargin('lr')) + 'px';
        if (Ext.isIE) {
            me.button.getEl().repaint();
        }
    },

    
    getTriggerMarkup: function() {
        return '<td id="' + this.id + '-browseButtonWrap"></td>';
    },

    
    onFileChange: function(button, e, value) {
        this.lastValue = null; 
        Ext.form.field.File.superclass.setValue.call(this, value);
    },

    
    setValue: Ext.emptyFn,

    reset : function(){
        var me = this,
            clear = me.clearOnSubmit;
        if (me.rendered) {
            me.button.reset(clear);
            me.fileInputEl = me.button.fileInputEl;
            if (clear) {
                me.inputEl.dom.value = '';
            }
        }
        me.callParent();
    },
    
    onShow: function(){
        this.callParent();
        
        
        this.button.updateLayout();    
    },

    onDisable: function(){
        this.callParent();
        this.button.disable();
    },

    onEnable: function(){
        this.callParent();
        this.button.enable();
    },

    isFileUpload: function() {
        return true;
    },

    extractFileInput: function() {
        var fileInput = this.button.fileInputEl.dom;
        this.reset();
        return fileInput;
    },
    
    restoreInput: function(el) {
        var button = this.button;
        button.restoreInput(el);
        this.fileInputEl = button.fileInputEl;
    },

    onDestroy: function(){
        Ext.destroyMembers(this, 'button');
        delete this.fileInputEl;
        this.callParent();
    },

    getButtonMarginProp: function() {
        return 'margin-left:';
    }
});


Ext.define('Ext.form.field.Hidden', {
    extend: Ext.form.field.Base ,
    alias: ['widget.hiddenfield', 'widget.hidden'],
    alternateClassName: 'Ext.form.Hidden',

    
    inputType : 'hidden',
    hideLabel: true,
    hidden: true,
    
    initComponent: function() {
        this.formItemCls += '-hidden';
        this.callParent();    
    },
    
    
    isEqual: function(value1, value2) {
        return this.isEqualAsString(value1, value2);
    },

    
    initEvents: Ext.emptyFn,
    setSize : Ext.emptyFn,
    setWidth : Ext.emptyFn,
    setHeight : Ext.emptyFn,
    setPosition : Ext.emptyFn,
    setPagePosition : Ext.emptyFn,
    markInvalid : Ext.emptyFn,
    clearInvalid : Ext.emptyFn
});


Ext.define('Ext.picker.Color', {
    extend:  Ext.Component ,
                              
    alias: 'widget.colorpicker',
    alternateClassName: 'Ext.ColorPalette',

    
    componentCls : Ext.baseCSSPrefix + 'color-picker',

    
    selectedCls: Ext.baseCSSPrefix + 'color-picker-selected',

    
    itemCls: Ext.baseCSSPrefix + 'color-picker-item',

    
    value : null,

    
    clickEvent :'click',

    
    allowReselect : false,

    
    colors : [
        '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
        '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
        'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
        'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
        'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
    ],

    

    

    colorRe: /(?:^|\s)color-(.{6})(?:\s|$)/,
    
    renderTpl: [
        '<tpl for="colors">',
            '<a href="#" class="color-{.} {parent.itemCls}" hidefocus="on">',
                '<span class="{parent.itemCls}-inner" style="background:#{.}">&#160;</span>',
            '</a>',
        '</tpl>'
    ],

    
    initComponent : function(){
        var me = this;

        me.callParent(arguments);
        me.addEvents(
            
            'select'
        );

        if (me.handler) {
            me.on('select', me.handler, me.scope, true);
        }
    },


    
    initRenderData : function(){
        var me = this;
        return Ext.apply(me.callParent(), {
            itemCls: me.itemCls,
            colors: me.colors
        });
    },

    onRender : function(){
        var me = this,
            clickEvent = me.clickEvent;

        me.callParent(arguments);

        me.mon(me.el, clickEvent, me.handleClick, me, {delegate: 'a'});
        
        if(clickEvent != 'click'){
            me.mon(me.el, 'click', Ext.emptyFn, me, {delegate: 'a', stopEvent: true});
        }
    },

    
    afterRender : function(){
        var me = this,
            value;

        me.callParent(arguments);
        if (me.value) {
            value = me.value;
            me.value = null;
            me.select(value, true);
        }
    },

    
    handleClick : function(event, target){
        var me = this,
            color;

        event.stopEvent();
        if (!me.disabled) {
            color = target.className.match(me.colorRe)[1];
            me.select(color.toUpperCase());
        }
    },

    
    select : function(color, suppressEvent){

        var me = this,
            selectedCls = me.selectedCls,
            value = me.value,
            el;

        color = color.replace('#', '');
        if (!me.rendered) {
            me.value = color;
            return;
        }


        if (color != value || me.allowReselect) {
            el = me.el;

            if (me.value) {
                el.down('a.color-' + value).removeCls(selectedCls);
            }
            el.down('a.color-' + color).addCls(selectedCls);
            me.value = color;
            if (suppressEvent !== true) {
                me.fireEvent('select', me, color);
            }
        }
    },
    
    
    clear: function(){
        var me = this,
            value = me.value,
            el;
            
        if (value && me.rendered) {
            el = me.el.down('a.color-' + value);
            el.removeCls(me.selectedCls);
        }
        me.value = null;  
    },

    
    getValue: function(){
        return this.value || null;
    }
});


Ext.define('Ext.layout.component.field.HtmlEditor', {
    extend:  Ext.layout.component.field.FieldContainer ,
    alias: ['layout.htmleditor'],

    type: 'htmleditor',
    
    naturalHeight: 150,
    naturalWidth: 300,

    beginLayout: function(ownerContext) {
        var owner = this.owner,
            dom;
            
        
        
        
        if (Ext.isGecko) {
            dom = owner.textareaEl.dom;
            this.lastValue = dom.value;
            dom.value = '';
        }
        this.callParent(arguments);
        
        ownerContext.toolbarContext  = ownerContext.context.getCmp(owner.toolbar);
        ownerContext.inputCmpContext = ownerContext.context.getCmp(owner.inputCmp);
        ownerContext.textAreaContext = ownerContext.getEl('textareaEl');
        ownerContext.iframeContext   = ownerContext.getEl('iframeEl');
    },
    
    beginLayoutCycle: function(ownerContext) {
        var me = this,
            widthModel = ownerContext.widthModel,
            heightModel = ownerContext.heightModel,
            owner = me.owner,
            iframeEl = owner.iframeEl,
            textareaEl = owner.textareaEl;
            
        me.callParent(arguments);
        if (widthModel.shrinkWrap) {
            iframeEl.setStyle('width', '');
            textareaEl.setStyle('width', '');
        } else if (widthModel.natural) {
            ownerContext.bodyCellContext.setWidth(me.naturalWidth);
        }
        
        if (heightModel.natural || heightModel.shrinkWrap) {
            iframeEl.setHeight(me.naturalHeight);
            textareaEl.setHeight(me.naturalHeight);
        }
    },
    
    finishedLayout: function(){
        var owner = this.owner;
        
        this.callParent(arguments);
        
        
        if (Ext.isIE9m && Ext.isIEQuirks) {
            owner.el.repaint();
        }    
        if (Ext.isGecko) {
            owner.textareaEl.dom.value = this.lastValue;
        }
    },
    
    publishOwnerWidth: function(ownerContext, width){
        this.callParent(arguments);
        width -= ownerContext.inputCmpContext.getBorderInfo().width;
        ownerContext.textAreaContext.setWidth(width);
        ownerContext.iframeContext.setWidth(width);
    },
    
    
    
    
    
    
    
    publishInnerWidth: function(ownerContext, width){
        var border = ownerContext.inputCmpContext.getBorderInfo().width,
            ieBug = Ext.isStrict && Ext.isIE8m,
            natural = ownerContext.widthModel.natural;
          
        this.callParent(arguments);
        width = ownerContext.bodyCellContext.props.width - border;
        if (natural) {
            if (ieBug) {
                width -= 2;
            }
            ownerContext.textAreaContext.setWidth(width);
            ownerContext.iframeContext.setWidth(width);
        } else if (ieBug) {
            ownerContext.textAreaContext.setWidth(width);
        }
    },

    publishInnerHeight: function (ownerContext, height) {
        var toolbarHeight = ownerContext.toolbarContext.getProp('height'),
            sourceEdit = this.owner.sourceEditMode;
        
        this.callParent(arguments);
        height = ownerContext.bodyCellContext.props.height;
        
        if (toolbarHeight !== undefined) {
            height -= toolbarHeight + ownerContext.inputCmpContext.getFrameInfo().height;
            if (Ext.isIE8 && Ext.isStrict) {
                height -= 2;
            } else if (Ext.isIEQuirks && (Ext.isIE8 || Ext.isIE9)) {
                height -= 4;
            }
            ownerContext.iframeContext.setHeight(height);    
            ownerContext.textAreaContext.setHeight(height);    
        } else {
            this.done = false;
        }
    }
 
});


Ext.define('Ext.form.field.HtmlEditor', {
    extend:  Ext.form.FieldContainer ,
    mixins: {
        field:  Ext.form.field.Field 
    },
    alias: 'widget.htmleditor',
    alternateClassName: 'Ext.form.HtmlEditor',
               
                                  
                           
                                    
                           
                              
                          
                                               
      
    
    componentLayout: 'htmleditor',

    componentTpl: [
        '{beforeTextAreaTpl}',
        '<textarea id="{id}-textareaEl" name="{name}" tabIndex="-1" {inputAttrTpl}',
                 ' class="{textareaCls}" autocomplete="off">',
            '{[Ext.util.Format.htmlEncode(values.value)]}',
        '</textarea>',
        '{afterTextAreaTpl}',
        '{beforeIFrameTpl}',
        '<iframe id="{id}-iframeEl" name="{iframeName}" frameBorder="0" {iframeAttrTpl}',
               ' src="{iframeSrc}" class="{iframeCls}"></iframe>',
        '{afterIFrameTpl}',
        {
            disableFormats: true
        }
    ],
    
    stretchInputElFixed: true,

    subTplInsertions: [
        
        'beforeTextAreaTpl',

        
        'afterTextAreaTpl',

        
        'beforeIFrameTpl',

        
        'afterIFrameTpl',

        
        'iframeAttrTpl',

        
        'inputAttrTpl'
    ],

    
    enableFormat: true,
    
    enableFontSize: true,
    
    enableColors: true,
    
    enableAlignments: true,
    
    enableLists: true,
    
    enableSourceEdit: true,
    
    enableLinks: true,
    
    enableFont: true,
    
    
    createLinkText: 'Please enter the URL for the link:',
    
    
    defaultLinkValue: 'http:/'+'/',
    
    fontFamilies: [
        'Arial',
        'Courier New',
        'Tahoma',
        'Times New Roman',
        'Verdana'
    ],
    
    defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',

    
    extraFieldBodyCls: Ext.baseCSSPrefix + 'html-editor-wrap',

    

    
    initialized: false,
    
    activated: false,
    
    sourceEditMode: false,
    
    iframePad:3,
    
    hideMode:'offsets',

    maskOnDisable: true,

    containerElCls: Ext.baseCSSPrefix + 'html-editor-container',

    
    initComponent: function(){
        var me = this;

        me.addEvents(
            
            'initialize',
            
            'activate',
             
            'beforesync',
             
            'beforepush',
             
            'sync',
             
            'push',
             
            'editmodechange'
        );

        me.items = [me.createToolbar(), me.createInputCmp()];

        me.layout = {
            type: 'vbox',
            align: 'stretch'
        };

        me.callParent(arguments);
        me.initField();
    },
    
    createInputCmp: function(){
        this.inputCmp = Ext.widget(this.getInputCmpCfg());
        return this.inputCmp;
    },
    
    getInputCmpCfg: function(){
        var me = this,
            id = me.id + '-inputCmp',
            data = {
                id          : id,
                name        : me.name,
                textareaCls : Ext.baseCSSPrefix + 'hidden',
                value       : me.value,
                iframeName  : Ext.id(),
                iframeSrc   : Ext.SSL_SECURE_URL,
                iframeCls   : Ext.baseCSSPrefix + 'htmleditor-iframe'
            };
            
        me.getInsertionRenderData(data, me.subTplInsertions);
            
        return {
            flex: 1,
            xtype: 'component',
            tpl: me.getTpl('componentTpl'),
            childEls: ['iframeEl', 'textareaEl'],
            id: id, 
            cls: Ext.baseCSSPrefix + 'html-editor-input',
            data: data 
        };    
    },

    
    createToolbar: function(){
        this.toolbar = Ext.widget(this.getToolbarCfg());
        return this.toolbar;
    },
    
    getToolbarCfg: function(){
        var me = this,
            items = [], i,
            tipsEnabled = Ext.quickTipsActive && Ext.tip.QuickTipManager.isEnabled(),
            baseCSSPrefix = Ext.baseCSSPrefix,
            fontSelectItem, undef;

        function btn(id, toggle, handler){
            return {
                itemId: id,
                cls: baseCSSPrefix + 'btn-icon',
                iconCls: baseCSSPrefix + 'edit-'+id,
                enableToggle:toggle !== false,
                scope: me,
                handler:handler||me.relayBtnCmd,
                clickEvent: 'mousedown',
                tooltip: tipsEnabled ? me.buttonTips[id] || undef : undef,
                overflowText: me.buttonTips[id].title || undef,
                tabIndex: -1
            };
        }


        if (me.enableFont && !Ext.isSafari2) {
            fontSelectItem = Ext.widget('component', {
                itemId: 'fontSelect',
                renderTpl: [
                    '<select id="{id}-selectEl" class="' + baseCSSPrefix + 'font-select">',
                    '</select>'
                ],
                childEls: ['selectEl'],
                afterRender: function() {
                    me.fontSelect = this.selectEl;
                    Ext.Component.prototype.afterRender.apply(this, arguments);
                },
                onDisable: function() {
                    var selectEl = this.selectEl;
                    if (selectEl) {
                        selectEl.dom.disabled = true;
                    }
                    Ext.Component.prototype.onDisable.apply(this, arguments);
                },
                onEnable: function() {
                    var selectEl = this.selectEl;
                    if (selectEl) {
                        selectEl.dom.disabled = false;
                    }
                    Ext.Component.prototype.onEnable.apply(this, arguments);
                },
                listeners: {
                    change: function() {
                        me.win.focus();
                        me.relayCmd('fontName', me.fontSelect.dom.value);
                        me.deferFocus();
                    },
                    element: 'selectEl'
                }
            });

            items.push(
                fontSelectItem,
                '-'
            );
        }

        if (me.enableFormat) {
            items.push(
                btn('bold'),
                btn('italic'),
                btn('underline')
            );
        }

        if (me.enableFontSize) {
            items.push(
                '-',
                btn('increasefontsize', false, me.adjustFont),
                btn('decreasefontsize', false, me.adjustFont)
            );
        }

        if (me.enableColors) {
            items.push(
                '-', {
                    itemId: 'forecolor',
                    cls: baseCSSPrefix + 'btn-icon',
                    iconCls: baseCSSPrefix + 'edit-forecolor',
                    overflowText: me.buttonTips.forecolor.title,
                    tooltip: tipsEnabled ? me.buttonTips.forecolor || undef : undef,
                    tabIndex:-1,
                    menu: Ext.widget('menu', {
                        plain: true,

                        items: [{
                            xtype: 'colorpicker',
                            allowReselect: true,
                            focus: Ext.emptyFn,
                            value: '000000',
                            plain: true,
                            clickEvent: 'mousedown',
                            handler: function(cp, color) {
                                me.relayCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
                                this.up('menu').hide();
                            }
                        }]
                    })
                }, {
                    itemId: 'backcolor',
                    cls: baseCSSPrefix + 'btn-icon',
                    iconCls: baseCSSPrefix + 'edit-backcolor',
                    overflowText: me.buttonTips.backcolor.title,
                    tooltip: tipsEnabled ? me.buttonTips.backcolor || undef : undef,
                    tabIndex:-1,
                    menu: Ext.widget('menu', {
                        plain: true,

                        items: [{
                            xtype: 'colorpicker',
                            focus: Ext.emptyFn,
                            value: 'FFFFFF',
                            plain: true,
                            allowReselect: true,
                            clickEvent: 'mousedown',
                            handler: function(cp, color) {
                                if (Ext.isGecko) {
                                    me.execCmd('useCSS', false);
                                    me.execCmd('hilitecolor', '#'+color);
                                    me.execCmd('useCSS', true);
                                    me.deferFocus();
                                } else {
                                    me.relayCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE || Ext.isOpera ? '#'+color : color);
                                }
                                this.up('menu').hide();
                            }
                        }]
                    })
                }
            );
        }

        if (me.enableAlignments) {
            items.push(
                '-',
                btn('justifyleft'),
                btn('justifycenter'),
                btn('justifyright')
            );
        }

        if (!Ext.isSafari2) {
            if (me.enableLinks) {
                items.push(
                    '-',
                    btn('createlink', false, me.createLink)
                );
            }

            if (me.enableLists) {
                items.push(
                    '-',
                    btn('insertorderedlist'),
                    btn('insertunorderedlist')
                );
            }
            if (me.enableSourceEdit) {
                items.push(
                    '-',
                    btn('sourceedit', true, function(btn){
                        me.toggleSourceEdit(!me.sourceEditMode);
                    })
                );
            }
        }
        
        
        for (i = 0; i < items.length; i++) {
            if (items[i].itemId !== 'sourceedit') {
                items[i].disabled = true;
            }
        }

        
        
        return {
            xtype: 'toolbar',
            defaultButtonUI: me.defaultButtonUI,
            cls: Ext.baseCSSPrefix + 'html-editor-tb',
            enableOverflow: true,
            items: items,

            
            listeners: {
                click: function(e){
                    e.preventDefault();
                },
                element: 'el'
            }
        }; 
    },
    
    getMaskTarget: function(){
        
        
        return Ext.isGecko ? this.inputCmp.el : this.bodyEl;
    },

    
    setReadOnly: function(readOnly) {
        var me = this,
            textareaEl = me.textareaEl,
            iframeEl = me.iframeEl,
            body;

        me.readOnly = readOnly;

        if (textareaEl) {
            textareaEl.dom.readOnly = readOnly;
        }

        if (me.initialized) {
            body = me.getEditorBody();
            if (Ext.isIE) {
                
                iframeEl.setDisplayed(false);
                body.contentEditable = !readOnly;
                iframeEl.setDisplayed(true);
            } else {
                me.setDesignMode(!readOnly);
            }
            if (body) {
                body.style.cursor = readOnly ? 'default' : 'text';
            }
            me.disableItems(readOnly);
        }
    },

    
    getDocMarkup: function() {
        var me = this,
            h = me.iframeEl.getHeight() - me.iframePad * 2,
            oldIE = Ext.isIE8m;

        
        
        
        return Ext.String.format(
            (oldIE ? '' : '<!DOCTYPE html>')                        
            + '<html><head><style type="text/css">' 
            + (Ext.isOpera ? 'p{margin:0}' : '')
            + 'body{border:0;margin:0;padding:{0}px;direction:' + (me.rtl ? 'rtl;' : 'ltr;')
            + (oldIE ? Ext.emptyString : 'min-')
            + 'height:{1}px;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;cursor:text;background-color:white;' 
            + (Ext.isIE ? '' : 'font-size:12px;font-family:{2}')
            + '}</style></head><body></body></html>'
            , me.iframePad, h, me.defaultFont);
    },

    
    getEditorBody: function() {
        var doc = this.getDoc();
        return doc.body || doc.documentElement;
    },

    
    getDoc: function() {
        return (!Ext.isIE && this.iframeEl.dom.contentDocument) || this.getWin().document;
    },

    
    getWin: function() {
        return Ext.isIE ? this.iframeEl.dom.contentWindow : window.frames[this.iframeEl.dom.name];
    },
    
    initDefaultFont: function(){
        
        
        
        
        
        var me = this,
            selIdx = 0,
            fonts, font, select,
            option, i, len, lower;
        
        if (!me.defaultFont) {
            font = me.textareaEl.getStyle('font-family');
            font = Ext.String.capitalize(font.split(',')[0]);
            fonts = Ext.Array.clone(me.fontFamilies);
            Ext.Array.include(fonts, font);
            fonts.sort();
            me.defaultFont = font;
            
            select = me.down('#fontSelect').selectEl.dom;
            for (i = 0, len = fonts.length; i < len; ++i) {
                font = fonts[i];
                lower = font.toLowerCase();
                option = new Option(font, lower);
                if (font == me.defaultFont) {
                    selIdx = i;
                }
                option.style.fontFamily = lower;
                
                if (Ext.isIE) {
                    select.add(option);
                } else {
                    select.options.add(option); 
                }
            }
            
            
            select.options[selIdx].selected = true;
        } 
    },
    
    isEqual: function(value1, value2){
        return this.isEqualAsString(value1, value2);
    },

    
    afterRender: function() {
        var me = this,
            inputCmp = me.inputCmp;

        me.callParent(arguments);
          
        me.iframeEl = inputCmp.iframeEl;
        me.textareaEl = inputCmp.textareaEl;
        
        
        
        me.inputEl = me.iframeEl;

        if (me.enableFont) {        
            me.initDefaultFont();
        }

        
        me.monitorTask = Ext.TaskManager.start({
            run: me.checkDesignMode,
            scope: me,
            interval: 100
        });
        me.relayCmd('fontName', me.defaultFont);
    },

    initFrameDoc: function() {
        var me = this,
            doc, task;

        Ext.TaskManager.stop(me.monitorTask);

        doc = me.getDoc();
        me.win = me.getWin();

        doc.open();
        doc.write(me.getDocMarkup());
        doc.close();

        task = { 
            run: function() {
                var doc = me.getDoc();
                if (doc.body || doc.readyState === 'complete') {
                    Ext.TaskManager.stop(task);
                    me.setDesignMode(true);
                    Ext.defer(me.initEditor, 10, me);
                }
            },
            interval: 10,
            duration:10000,
            scope: me
        };
        Ext.TaskManager.start(task);
    },

    checkDesignMode: function() {
        var me = this,
            doc = me.getDoc();
        if (doc && (!doc.editorInitialized || me.getDesignMode() !== 'on')) {
            me.initFrameDoc();
        }
    },

    
    setDesignMode: function(mode) {
        var me = this,
            doc = me.getDoc();
        if (doc) {
            if (me.readOnly) {
                mode = false;
            }
            doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';
        }
    },

    
    getDesignMode: function() {
        var doc = this.getDoc();
        return !doc ? '' : String(doc.designMode).toLowerCase();
    },

    disableItems: function(disabled) {
        var items = this.getToolbar().items.items,
            i,
            iLen  = items.length,
            item;

        for (i = 0; i < iLen; i++) {
            item = items[i];

            if (item.getItemId() !== 'sourceedit') {
                item.setDisabled(disabled);
            }
        }
    },

    
    toggleSourceEdit: function(sourceEditMode) {
        var me = this,
            iframe = me.iframeEl,
            textarea = me.textareaEl,
            hiddenCls = Ext.baseCSSPrefix + 'hidden',
            btn = me.getToolbar().getComponent('sourceedit');

        if (!Ext.isBoolean(sourceEditMode)) {
            sourceEditMode = !me.sourceEditMode;
        }
        me.sourceEditMode = sourceEditMode;

        if (btn.pressed !== sourceEditMode) {
            btn.toggle(sourceEditMode);
        }
        if (sourceEditMode) {
            me.disableItems(true);
            me.syncValue();
            iframe.addCls(hiddenCls);
            textarea.removeCls(hiddenCls);
            textarea.dom.removeAttribute('tabIndex');
            textarea.focus();
            me.inputEl = textarea;
        } else {
            if (me.initialized) {
                me.disableItems(me.readOnly);
            }
            me.pushValue();
            iframe.removeCls(hiddenCls);
            textarea.addCls(hiddenCls);
            textarea.dom.setAttribute('tabIndex', -1);
            me.deferFocus();
            me.inputEl = iframe;
        }
        me.fireEvent('editmodechange', me, sourceEditMode);
        me.updateLayout();
    },

    
    createLink: function() {
        var url = prompt(this.createLinkText, this.defaultLinkValue);
        if (url && url !== 'http:/'+'/') {
            this.relayCmd('createlink', url);
        }
    },

    clearInvalid: Ext.emptyFn,

    setValue: function(value) {
        var me = this,
            textarea = me.textareaEl,
            inputCmp = me.inputCmp;
            
        me.mixins.field.setValue.call(me, value);
        if (value === null || value === undefined) {
            value = '';
        }
        if (textarea) {
            textarea.dom.value = value;
        }
        me.pushValue();
        
        if (!me.rendered && me.inputCmp) {
            me.inputCmp.data.value = value;
        }
        
        return me;
    },

    
    cleanHtml: function(html) {
        html = String(html);
        if (Ext.isWebKit) { 
            html = html.replace(/\sclass="(?:Apple-style-span|Apple-tab-span|khtml-block-placeholder)"/gi, '');
        }

        
        if (html.charCodeAt(0) === parseInt(this.defaultValue.replace(/\D/g, ''), 10)) {
            html = html.substring(1);
        }
        
        return html;
    },

    
    syncValue: function(){
        var me = this,
            body, changed, html, bodyStyle, match, textElDom;

        if (me.initialized) {
            body = me.getEditorBody();
            html = body.innerHTML;
            textElDom = me.textareaEl.dom;
            
            if (Ext.isWebKit) {
                bodyStyle = body.getAttribute('style'); 
                match = bodyStyle.match(/text-align:(.*?);/i);
                if (match && match[1]) {
                    html = '<div style="' + match[0] + '">' + html + '</div>';
                }
            }
            
            html = me.cleanHtml(html);
            
            if (me.fireEvent('beforesync', me, html) !== false) {
                
                
                if (Ext.isGecko && textElDom.value === '' && html === '<br>') {
                    html = '';
                }
                
                if (textElDom.value !== html) {
                    textElDom.value = html;
                    changed = true;
                }

                me.fireEvent('sync', me, html);

                if (changed) {
                    
                    
                    me.checkChange();
                }
            }
        }
    },

    getValue: function() {
        var me = this,
            value;
        if (!me.sourceEditMode) {
            me.syncValue();
        }
        value = me.rendered ? me.textareaEl.dom.value : me.value;
        me.value = value;
        return value;
    },

    
    pushValue: function() {
        var me = this,
            v;
        if(me.initialized){
            v = me.textareaEl.dom.value || '';
            if (!me.activated && v.length < 1) {
                v = me.defaultValue;
            }
            if (me.fireEvent('beforepush', me, v) !== false) {
                me.getEditorBody().innerHTML = v;
                if (Ext.isGecko) {
                    
                    me.setDesignMode(false);  
                    me.setDesignMode(true);
                }
                me.fireEvent('push', me, v);
            }
        }
    },

    
    deferFocus: function(){
         this.focus(false, true);
    },

    getFocusEl: function() {
        var me = this,
            win = me.win;
        return win && !me.sourceEditMode ? win : me.textareaEl;
    },

    focus: function(selectText, delay) {
        var me = this,
            value, focusEl;

        if (delay) {
            if (!me.focusTask) {
                me.focusTask = new Ext.util.DelayedTask(me.focus);
            }
            me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [selectText, false]);
        }
        else {
            if (selectText) {
                if (me.textareaEl && me.textareaEl.dom) {
                    value = me.textareaEl.dom.value;
                }
                if (value && value.length) {  
                    me.execCmd('selectall', true);
                }
            }
            focusEl = me.getFocusEl();
            if (focusEl && focusEl.focus) {
                focusEl.focus();
            }
        }
        return me;
    },

    
    initEditor: function(){
        
        try {
            var me = this,
                dbody = me.getEditorBody(),
                ss = me.textareaEl.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'),
                doc,
                fn;

            ss['background-attachment'] = 'fixed'; 
            dbody.bgProperties = 'fixed'; 

            Ext.DomHelper.applyStyles(dbody, ss);

            doc = me.getDoc();

            if (doc) {
                try {
                    Ext.EventManager.removeAll(doc);
                } catch(e) {}
            }

            
            fn = Ext.Function.bind(me.onEditorEvent, me);
            Ext.EventManager.on(doc, {
                mousedown: fn,
                dblclick: fn,
                click: fn,
                keyup: fn,
                buffer:100
            });
            
            
            
            
            
            
            fn = me.onRelayedEvent;
            Ext.EventManager.on(doc, {
                mousedown: fn, 
                mousemove: fn, 
                mouseup: fn,   
                click: fn,     
                dblclick: fn,  
                scope: me
            });
            
            if (Ext.isGecko) {
                Ext.EventManager.on(doc, 'keypress', me.applyCommand, me);
            }
            
            if (me.fixKeys) {
                Ext.EventManager.on(doc, 'keydown', me.fixKeys, me);
            }
            if (me.fixKeysAfter) {
                Ext.EventManager.on(doc, 'keyup', me.fixKeysAfter, me);
            }

            if (Ext.isIE9 && Ext.isStrict) {
                Ext.EventManager.on(doc.documentElement, 'focus', me.focus, me);
            }

            
            
            
            if (Ext.isIE8m || (Ext.isIE9 && !Ext.isStrict)) {
                Ext.EventManager.on(doc, 'focusout', function() {
                    me.savedSelection = doc.selection.type !== 'None' ? doc.selection.createRange() : null;
                }, me);
                
                Ext.EventManager.on(doc, 'focusin', function() {
                    if (me.savedSelection) {
                        me.savedSelection.select();
                    }
                }, me);
            }
            
            
            Ext.EventManager.onWindowUnload(me.beforeDestroy, me);
            doc.editorInitialized = true;

            me.initialized = true;
            me.pushValue();
            me.setReadOnly(me.readOnly);
            me.fireEvent('initialize', me);
        } catch(ex) {
            
        }
    },
    
    
    beforeDestroy: function(){
        var me = this,
            monitorTask = me.monitorTask,
            doc, prop;

        if (monitorTask) {
            Ext.TaskManager.stop(monitorTask);
        }
        if (me.rendered) {
            Ext.EventManager.removeUnloadListener(me.beforeDestroy, me);
            try {
                doc = me.getDoc();
                if (doc) {
                    
                    
                    
                    
                    
                    Ext.EventManager.removeAll(Ext.fly(doc));
                    for (prop in doc) {
                        if (doc.hasOwnProperty && doc.hasOwnProperty(prop)) {
                            delete doc[prop];
                        }
                    }
                }
            } catch(e) {
                
            }
            delete me.iframeEl;
            delete me.textareaEl;
            delete me.toolbar;
            delete me.inputCmp;
        }
        me.callParent();
    },

    
    onRelayedEvent: function (event) {
        

        var iframeEl = this.iframeEl,

            
            iframeXY = Ext.Element.getTrueXY(iframeEl),
            originalEventXY = event.getXY(),

            
            
            
            eventXY = Ext.EventManager.getPageXY(event.browserEvent);

        
        
        event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];

        event.injectEvent(iframeEl); 

        event.xy = originalEventXY; 
    },

    
    onFirstFocus: function(){
        var me = this,
            selection, range;
        me.activated = true;
        me.disableItems(me.readOnly);
        if (Ext.isGecko) { 
            me.win.focus();
            selection = me.win.getSelection();
            if (!selection.focusNode || selection.focusNode.nodeType !== 3) {
                range = selection.getRangeAt(0);
                range.selectNodeContents(me.getEditorBody());
                range.collapse(true);
                me.deferFocus();
            }
            try {
                me.execCmd('useCSS', true);
                me.execCmd('styleWithCSS', false);
            } catch(e) {
                
            }
        }
        me.fireEvent('activate', me);
    },

    
    adjustFont: function(btn) {
        var adjust = btn.getItemId() === 'increasefontsize' ? 1 : -1,
            size = this.getDoc().queryCommandValue('FontSize') || '2',
            isPxSize = Ext.isString(size) && size.indexOf('px') !== -1,
            isSafari;
        size = parseInt(size, 10);
        if (isPxSize) {
            
            
            if (size <= 10) {
                size = 1 + adjust;
            }
            else if (size <= 13) {
                size = 2 + adjust;
            }
            else if (size <= 16) {
                size = 3 + adjust;
            }
            else if (size <= 18) {
                size = 4 + adjust;
            }
            else if (size <= 24) {
                size = 5 + adjust;
            }
            else {
                size = 6 + adjust;
            }
            size = Ext.Number.constrain(size, 1, 6);
        } else {
            isSafari = Ext.isSafari;
            if (isSafari) { 
                adjust *= 2;
            }
            size = Math.max(1, size + adjust) + (isSafari ? 'px' : 0);
        }
        this.relayCmd('FontSize', size);
    },

    
    onEditorEvent: function(e) {
        this.updateToolbar();
    },

    
    updateToolbar: function() {
        var me = this,
            i, l, btns, doc, name, queriedName, fontSelect,
            toolbarSubmenus;

        if (me.readOnly) {
            return;
        }

        if (!me.activated) {
            me.onFirstFocus();
            return;
        }

        btns = me.getToolbar().items.map;
        doc = me.getDoc();

        if (me.enableFont && !Ext.isSafari2) {
            
            
            queriedName = doc.queryCommandValue('fontName');
            name = (queriedName ? queriedName.split(",")[0].replace(/^'/,'').replace(/'$/,'') : me.defaultFont).toLowerCase();
            fontSelect = me.fontSelect.dom;
            if (name !== fontSelect.value || name != queriedName) {
                fontSelect.value = name;
            }
        }

        function updateButtons() {
            var state;
            
            for (i = 0, l = arguments.length, name; i < l; i++) {
                name  = arguments[i];
                
                
                
                try {
                    state = doc.queryCommandState(name);
                }
                catch (e) {
                    state = false;
                }
                
                btns[name].toggle(state);
            }
        }
        if(me.enableFormat){
            updateButtons('bold', 'italic', 'underline');
        }
        if(me.enableAlignments){
            updateButtons('justifyleft', 'justifycenter', 'justifyright');
        }
        if(!Ext.isSafari2 && me.enableLists){
            updateButtons('insertorderedlist', 'insertunorderedlist');
        }

        
        
        toolbarSubmenus = me.toolbar.query('menu');
        for (i = 0; i < toolbarSubmenus.length; i++) {
            toolbarSubmenus[i].hide();
        }
        me.syncValue();
    },

    
    relayBtnCmd: function(btn) {
        this.relayCmd(btn.getItemId());
    },

    
    relayCmd: function(cmd, value) {
        Ext.defer(function() {
            var me = this;
            
            if (!this.isDestroyed) {
                me.win.focus();
                me.execCmd(cmd, value);
                me.updateToolbar();
            }
        }, 10, this);
    },

    
    execCmd: function(cmd, value){
        var me = this,
            doc = me.getDoc();
        doc.execCommand(cmd, false, (value == undefined ? null : value));
        me.syncValue();
    },

    
    applyCommand: function(e){
        if (e.ctrlKey) {
            var me = this,
                c = e.getCharCode(), cmd;
            if (c > 0) {
                c = String.fromCharCode(c);
                switch (c) {
                    case 'b':
                        cmd = 'bold';
                    break;
                    case 'i':
                        cmd = 'italic';
                    break;
                    case 'u':
                        cmd = 'underline';
                    break;
                }
                if (cmd) {
                    me.win.focus();
                    me.execCmd(cmd);
                    me.deferFocus();
                    e.preventDefault();
                }
            }
        }
    },

    
    insertAtCursor: function(text){
        var me = this,
            range;

        if (me.activated) {
            me.win.focus();
            if (Ext.isIE) {
                range = me.getDoc().selection.createRange();
                if (range) {
                    range.pasteHTML(text);
                    me.syncValue();
                    me.deferFocus();
                }
            }else{
                me.execCmd('InsertHTML', text);
                me.deferFocus();
            }
        }
    },

    
    fixKeys: (function() { 
        if (Ext.isIE) {
            return function(e){
                var me = this,
                    k = e.getKey(),
                    doc = me.getDoc(),
                    readOnly = me.readOnly,
                    range, target;

                if (k === e.TAB) {
                    e.stopEvent();
                    if (!readOnly) {
                        range = doc.selection.createRange();
                        if (range){
                            if (range.collapse) {
                                range.collapse(true);
                                range.pasteHTML('&#160;&#160;&#160;&#160;');
                            }
                            
                            me.deferFocus();
                        }
                    }
                }
                else if (k === e.ENTER) {
                    if (!readOnly) {
                        range = doc.selection.createRange();
                        if (range) {
                            target = range.parentElement();
                            if(!target || target.tagName.toLowerCase() !== 'li'){
                                e.stopEvent();
                                range.pasteHTML('<br />');
                                range.collapse(false);
                                range.select();
                            }
                        }
                    }
                }
            };
        }

        if (Ext.isOpera) {
            return function(e){
                var me = this,
                    k = e.getKey(),
                    readOnly = me.readOnly;
                if (k === e.TAB) {
                    e.stopEvent();
                    if (!readOnly) {
                        me.win.focus();
                        me.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
                        me.deferFocus();
                    }
                }
            };
        }

        return null; 
    }()),
    
    
    fixKeysAfter: (function() {
        if (Ext.isIE) {
            return function(e) {
                var me = this,
                    k = e.getKey(),
                    doc = me.getDoc(),
                    readOnly = me.readOnly,
                    innerHTML;
                
                if (!readOnly && (k === e.BACKSPACE || k === e.DELETE)) {
                    innerHTML = doc.body.innerHTML;
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
                    if (innerHTML === '<p>&nbsp;</p>' || innerHTML === '<P>&nbsp;</P>') {
                        doc.body.innerHTML = '';
                    }
                }
            }
        }
        
        return null;
    }()),

    
    getToolbar: function(){
        return this.toolbar;
    },

    
    
    buttonTips: {
        bold: {
            title: 'Bold (Ctrl+B)',
            text: 'Make the selected text bold.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        italic: {
            title: 'Italic (Ctrl+I)',
            text: 'Make the selected text italic.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        underline: {
            title: 'Underline (Ctrl+U)',
            text: 'Underline the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        increasefontsize: {
            title: 'Grow Text',
            text: 'Increase the font size.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        decreasefontsize: {
            title: 'Shrink Text',
            text: 'Decrease the font size.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        backcolor: {
            title: 'Text Highlight Color',
            text: 'Change the background color of the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        forecolor: {
            title: 'Font Color',
            text: 'Change the color of the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifyleft: {
            title: 'Align Text Left',
            text: 'Align text to the left.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifycenter: {
            title: 'Center Text',
            text: 'Center text in the editor.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifyright: {
            title: 'Align Text Right',
            text: 'Align text to the right.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        insertunorderedlist: {
            title: 'Bullet List',
            text: 'Start a bulleted list.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        insertorderedlist: {
            title: 'Numbered List',
            text: 'Start a numbered list.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        createlink: {
            title: 'Hyperlink',
            text: 'Make the selected text a hyperlink.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        sourceedit: {
            title: 'Source Edit',
            text: 'Switch to source editing mode.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        }
    }
    

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
});


Ext.define('Ext.picker.Time', {
    extend:  Ext.view.BoundList ,
    alias: 'widget.timepicker',
                                             

    

    

    
    increment: 15,

    
    
    format : "g:i A",
    

    
    displayField: 'disp',

    
    initDate: [2008,0,1],

    componentCls: Ext.baseCSSPrefix + 'timepicker',

    
    loadMask: false,

    initComponent: function() {
        var me = this,
            dateUtil = Ext.Date,
            clearTime = dateUtil.clearTime,
            initDate = me.initDate;

        
        me.absMin = clearTime(new Date(initDate[0], initDate[1], initDate[2]));
        me.absMax = dateUtil.add(clearTime(new Date(initDate[0], initDate[1], initDate[2])), 'mi', (24 * 60) - 1);

        me.store = me.createStore();

        
        
        me.store.addFilter(me.rangeFilter = new Ext.util.Filter({
            id: 'time-picker-filter'
        }), false);

        
        me.updateList();

        me.callParent();
    },

    
    setMinValue: function(value) {
        this.minValue = value;
        this.updateList();
    },

    
    setMaxValue: function(value) {
        this.maxValue = value;
        this.updateList();
    },

    
    normalizeDate: function(date) {
        var initDate = this.initDate;
        date.setFullYear(initDate[0], initDate[1], initDate[2]);
        return date;
    },

    
    updateList: function() {
        var me = this,
            min = me.normalizeDate(me.minValue || me.absMin),
            max = me.normalizeDate(me.maxValue || me.absMax);

        me.rangeFilter.setFilterFn(function(record) {
            var date = record.get('date');
            return date >= min && date <= max;
        });
        me.store.filter();
    },

    
    createStore: function() {
        var me = this,
            utilDate = Ext.Date,
            times = [],
            min = me.absMin,
            max = me.absMax;

        while(min <= max){
            times.push({
                disp: utilDate.dateFormat(min, me.format),
                date: min
            });
            min = utilDate.add(min, 'mi', me.increment);
        }

        return new Ext.data.Store({
            fields: ['disp', 'date'],
            data: times
        });
    },

    focusNode: function (rec) {
        
        
        return false;
    }

});


Ext.define('Ext.form.field.Time', {
    extend: Ext.form.field.ComboBox ,
    alias: 'widget.timefield',
                                                                                                 
    alternateClassName: ['Ext.form.TimeField', 'Ext.form.Time'],

    
    triggerCls: Ext.baseCSSPrefix + 'form-time-trigger',

    

    

    
    
    minText : "The time in this field must be equal to or after {0}",
    

    
    
    maxText : "The time in this field must be equal to or before {0}",
    

    
    
    invalidText : "{0} is not a valid time",
    

    
    
    format : "g:i A",
    

    
    
    

    
    
    altFormats : "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A",
    

    
    increment: 15,

    
    pickerMaxHeight: 300,

    
    selectOnTab: true,
    
    
    snapToIncrement: false,

    
    initDate: '1/1/2008',
    initDateFormat: 'j/n/Y',
    
    ignoreSelection: 0,

    queryMode: 'local',

    displayField: 'disp',

    valueField: 'date',

    initComponent: function() {
        var me = this,
            min = me.minValue,
            max = me.maxValue;
        if (min) {
            me.setMinValue(min);
        }
        if (max) {
            me.setMaxValue(max);
        }
        me.displayTpl = new Ext.XTemplate(
            '<tpl for=".">' +
                '{[typeof values === "string" ? values : this.formatDate(values["' + me.displayField + '"])]}' +
                '<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' +
            '</tpl>', {
            formatDate: Ext.Function.bind(me.formatDate, me)
        });
        this.callParent();
    },

    
    transformOriginalValue: function(value) {
        if (Ext.isString(value)) {
            return this.rawToValue(value);
        }
        return value;
    },

    
    isEqual: function(v1, v2) {
        return Ext.Date.isEqual(v1, v2);
    },

    
    setMinValue: function(value) {
        var me = this,
            picker = me.picker;
        me.setLimit(value, true);
        if (picker) {
            picker.setMinValue(me.minValue);
        }
    },

    
    setMaxValue: function(value) {
        var me = this,
            picker = me.picker;
        me.setLimit(value, false);
        if (picker) {
            picker.setMaxValue(me.maxValue);
        }
    },

    
    setLimit: function(value, isMin) {
        var me = this,
            d, val;
        if (Ext.isString(value)) {
            d = me.parseDate(value);
        }
        else if (Ext.isDate(value)) {
            d = value;
        }
        if (d) {
            val = Ext.Date.clearTime(new Date(me.initDate));
            val.setHours(d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
        }
        
        else {
            val = null;
        }
        me[isMin ? 'minValue' : 'maxValue'] = val;
    },

    rawToValue: function(rawValue) {
        return this.parseDate(rawValue) || rawValue || null;
    },

    valueToRaw: function(value) {
        return this.formatDate(this.parseDate(value));
    },

    
    getErrors: function(value) {
        var me = this,
            format = Ext.String.format,
            errors = me.callParent(arguments),
            minValue = me.minValue,
            maxValue = me.maxValue,
            date;

        value = me.formatDate(value || me.processRawValue(me.getRawValue()));

        if (value === null || value.length < 1) { 
             return errors;
        }

        date = me.parseDate(value);
        if (!date) {
            errors.push(format(me.invalidText, value, Ext.Date.unescapeFormat(me.format)));
            return errors;
        }

        if (minValue && date < minValue) {
            errors.push(format(me.minText, me.formatDate(minValue)));
        }

        if (maxValue && date > maxValue) {
            errors.push(format(me.maxText, me.formatDate(maxValue)));
        }

        return errors;
    },

    formatDate: function() {
        return Ext.form.field.Date.prototype.formatDate.apply(this, arguments);
    },

    
    parseDate: function(value) {
        var me = this,
            val = value,
            altFormats = me.altFormats,
            altFormatsArray = me.altFormatsArray,
            i = 0,
            len;

        if (value && !Ext.isDate(value)) {
            val = me.safeParse(value, me.format);

            if (!val && altFormats) {
                altFormatsArray = altFormatsArray || altFormats.split('|');
                len = altFormatsArray.length;
                for (; i < len && !val; ++i) {
                    val = me.safeParse(value, altFormatsArray[i]);
                }
            }
        }

        
        if (val && me.snapToIncrement) {
            val = new Date(Ext.Number.snap(val.getTime(), me.increment * 60 * 1000));
        }
        return val;
    },

    safeParse: function(value, format){
        var me = this,
            utilDate = Ext.Date,
            parsedDate,
            result = null;

        if (utilDate.formatContainsDateInfo(format)) {
            
            result = utilDate.parse(value, format);
        } else {
            
            parsedDate = utilDate.parse(me.initDate + ' ' + value, me.initDateFormat + ' ' + format);
            if (parsedDate) {
                result = parsedDate;
            }
        }
        return result;
    },

    
    getSubmitValue: function() {
        var me = this,
            format = me.submitFormat || me.format,
            value = me.getValue();

        return value ? Ext.Date.format(value, format) : null;
    },

    
    createPicker: function() {
        var me = this,
            picker;

        me.listConfig = Ext.apply({
            xtype: 'timepicker',
            selModel: {
                mode: 'SINGLE'
            },
            cls: undefined,
            minValue: me.minValue,
            maxValue: me.maxValue,
            increment: me.increment,
            format: me.format,
            maxHeight: me.pickerMaxHeight
        }, me.listConfig);
        picker = me.callParent();
        me.bindStore(picker.store);
        return picker;
    },
    
    onItemClick: function(picker, record){
        
        var me = this,
            selected = picker.getSelectionModel().getSelection();

        if (selected.length > 0) {
            selected = selected[0];
            if (selected && Ext.Date.isEqual(record.get('date'), selected.get('date'))) {
                me.collapse();
            }
        }
    },

    
    onListSelectionChange: function(list, recordArray) {
        if (recordArray.length) {
            var me = this,
                val = recordArray[0].get('date');

            if (!me.ignoreSelection) {
                me.skipSync = true;
                me.setValue(val);
                me.skipSync = false;
                me.fireEvent('select', me, val);
                me.picker.clearHighlight();
                me.collapse();
                me.inputEl.focus();
            }
        }
    },

    
    syncSelection: function() {
        var me = this,
            picker = me.picker,
            toSelect,
            selModel,
            value,
            data, d, dLen, rec;
            
        if (picker && !me.skipSync) {
            picker.clearHighlight();
            value = me.getValue();
            selModel = picker.getSelectionModel();
            
            me.ignoreSelection++;
            if (value === null) {
                selModel.deselectAll();
            } else if (Ext.isDate(value)) {
                
                data = picker.store.data.items;
                dLen = data.length;

                for (d = 0; d < dLen; d++) {
                    rec = data[d];

                    if (Ext.Date.isEqual(rec.get('date'), value)) {
                       toSelect = rec;
                       break;
                   }
                }

                selModel.select(toSelect);
            }
            me.ignoreSelection--;
        }
    },

    postBlur: function() {
        var me = this,
            val = me.getValue();

        me.callParent(arguments);

        
        if (me.wasValid && val) {
            me.setRawValue(me.formatDate(val));
        }
    },

    setValue: function() {

        
        this.getPicker();

        return this.callParent(arguments);
    },

    getValue: function() {
        return this.parseDate(this.callParent(arguments));
    }
});


Ext.define('Ext.grid.CellContext', {

    
    isCellContext: true,
    
    constructor: function(view) {
        this.view = view;
    },
    
    
    setPosition: function(row, col) {
        var me = this;

        
        if (arguments.length === 1) {
            if (row.view) {
                me.view = row.view;
            }
            col = row.column;
            row = row.row;
        }

        me.setRow(row);
        me.setColumn(col);
        return me;
    },

    setRow: function(row) {
        var me = this;
        if (row !== undefined) {
            
            if (typeof row === 'number') {
                me.row = Math.max(Math.min(row, me.view.dataSource.getCount() - 1), 0);
                me.record = me.view.dataSource.getAt(row);
            }
            
            else if (row.isModel) {
                me.record = row;
                me.row = me.view.indexOf(row);
            }
            
            else if (row.tagName) {
                me.record = me.view.getRecord(row);
                me.row = me.view.indexOf(me.record);
            }
        }
    },
    
    setColumn: function(col) {
        var me = this,
            columnManager = me.view.ownerCt.columnManager;
        if (col !== undefined) {
            
            if (typeof col === 'number') {
                me.column = col;
                me.columnHeader = columnManager.getHeaderAtIndex(col);
            }
            
            else if (col.isHeader) {
                me.columnHeader = col;
                me.column = columnManager.getHeaderIndex(col);
            }
        }
    }
});


Ext.define('Ext.grid.CellEditor', {
    extend:  Ext.Editor ,
    constructor: function(config) {
        config = Ext.apply({}, config);
        
        if (config.field) {
            config.field.monitorTab = false;
        }
        this.callParent([config]);
    },
    
    
    onShow: function() {
        var me = this,
            innerCell = me.boundEl.first();

        if (innerCell) {
            if (me.isForTree) {
                innerCell = innerCell.child(me.treeNodeSelector);
            }
            innerCell.hide();
        }

        me.callParent(arguments);
    },

    
    onHide: function() {
        var me = this,
            innerCell = me.boundEl.first();

        if (innerCell) {
            if (me.isForTree) {
                innerCell = innerCell.child(me.treeNodeSelector);
            }
            innerCell.show();
        }
        
        me.callParent(arguments);
    },

    
    afterRender: function() {
        var me = this,
            field = me.field;

        me.callParent(arguments);
        if (field.isCheckbox) {
            field.mon(field.inputEl, {
                mousedown: me.onCheckBoxMouseDown,
                click: me.onCheckBoxClick,
                scope: me
            });
        }
    },
    
    
    onCheckBoxMouseDown: function() {
        this.completeEdit = Ext.emptyFn;
    },
    
    
    onCheckBoxClick: function() {
        delete this.completeEdit;
        this.field.focus(false, 10);
    },
    
    
    realign: function(autoSize) {
        var me = this,
            boundEl = me.boundEl,
            innerCell = boundEl.first(),
            width = boundEl.getWidth(),
            offsets = Ext.Array.clone(me.offsets),
            grid = me.grid,
            xOffset;

        if (me.isForTree) {
            
            
            xOffset = me.getTreeNodeOffset(innerCell);
            width -= Math.abs(xOffset);
            offsets[0] += xOffset;
        }

        if (grid.columnLines) {
            
            
            
            width -= boundEl.getBorderWidth('rl');
        }

        if (autoSize === true) {
            me.field.setWidth(width);
        }

        me.alignTo(innerCell, me.alignment, offsets);
    },

    
    getTreeNodeOffset: function(innerCell) {
        return innerCell.child(this.treeNodeSelector).getOffsetsTo(innerCell)[0];
    },
    
    onEditorTab: function(e){
        var field = this.field;
        if (field.onEditorTab) {
            field.onEditorTab(e);
        }
    },
    
    alignment: "l-l",
    hideEl : false,
    cls: Ext.baseCSSPrefix + 'small-editor ' +
        Ext.baseCSSPrefix + 'grid-editor ' + 
        Ext.baseCSSPrefix + 'grid-cell-editor',
    treeNodeSelector: '.' + Ext.baseCSSPrefix + 'tree-node-text',
    shim: false,
    shadow: false
});


Ext.define('Ext.grid.ColumnComponentLayout', {
    extend:  Ext.layout.component.Auto ,
    alias: 'layout.columncomponent',

    type: 'columncomponent',

    setWidthInDom: true,

    beginLayout: function(ownerContext) {
        var me = this;

        me.callParent(arguments);
        ownerContext.titleContext = ownerContext.getEl('titleEl');
        ownerContext.triggerContext = ownerContext.getEl('triggerEl');
    },

    beginLayoutCycle: function(ownerContext) {
        var me = this,
            owner = me.owner;

        me.callParent(arguments);

        
        if (ownerContext.widthModel.shrinkWrap) {
            owner.el.setWidth('');
        }

        
        
        var borderRightWidth = owner.isLast && owner.isSubHeader ? '0' : '';
        if (borderRightWidth !== me.lastBorderRightWidth) {
            owner.el.dom.style.borderRightWidth = me.lasBorderRightWidth = borderRightWidth;
        }

        owner.titleEl.setStyle({
            paddingTop: '',  
            paddingBottom: ''
        });
    },

    
    publishInnerHeight: function(ownerContext, outerHeight) {
        
        
        
        if (!outerHeight) {
            return;
        }

        var me = this,
            owner = me.owner,
            innerHeight = outerHeight - ownerContext.getBorderInfo().height,
            availableHeight = innerHeight,
            textHeight,
            titleHeight,
            pt, pb;

        
        if (!owner.noWrap && !ownerContext.hasDomProp('width')) {
            me.done = false;
            return;
        }

        
        if (ownerContext.hasRawContent) {
            titleHeight = availableHeight;

            
            textHeight = owner.textEl.getHeight();
            if (textHeight) {
                availableHeight -= textHeight;
                if (availableHeight > 0) {
                    pt = Math.floor(availableHeight / 2);
                    pb = availableHeight - pt;
                    ownerContext.titleContext.setProp('padding-top', pt);
                    ownerContext.titleContext.setProp('padding-bottom', pb);
                }
            }
        }

        
        else {
            titleHeight = owner.titleEl.getHeight();
            ownerContext.setProp('innerHeight', innerHeight - titleHeight, false);
        }
        
        
        if ((Ext.isIE6 || Ext.isIEQuirks) && ownerContext.triggerContext) {
            ownerContext.triggerContext.setHeight(titleHeight);
        }

    },

    
    
    
    measureContentHeight: function(ownerContext) {
        return ownerContext.el.dom.offsetHeight;
    },

    publishOwnerHeight: function(ownerContext, contentHeight) {
        this.callParent(arguments);
        
        
        if ((Ext.isIE6 || Ext.isIEQuirks) && ownerContext.triggerContext) {
            ownerContext.triggerContext.setHeight(contentHeight);
        }
    },

    
    publishInnerWidth: function(ownerContext, outerWidth) {
        
        if (!ownerContext.hasRawContent) {
            ownerContext.setProp('innerWidth', outerWidth - ownerContext.getBorderInfo().width, false);
        }
    },

    
    calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
        var result = this.callParent(arguments);

        
        if (!ownerContext.hasRawContent) {
            if (this.owner.noWrap || ownerContext.hasDomProp('width')) {
                return contentHeight + this.owner.titleEl.getHeight() + ownerContext.getBorderInfo().height;
            }

            
            
            return null;
        }
        return result;
    },

    
    calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) {
        var owner = this.owner,
            inner = Math.max(contentWidth, owner.textEl.getWidth() + ownerContext.titleContext.getPaddingInfo().width),
            padWidth = ownerContext.getPaddingInfo().width,
            triggerOffset = this.getTriggerOffset(owner, ownerContext);
            
        return inner + padWidth + triggerOffset;
    },
    
    getTriggerOffset: function(owner, ownerContext) {
        var width = 0;
        if (ownerContext.widthModel.shrinkWrap && !owner.menuDisabled) {
            
            if (owner.query('>:not([hidden])').length === 0) {
                width = owner.self.triggerElWidth;
            }
        }
        return width;
    }
});


Ext.define('Ext.grid.ColumnLayout', {
    extend:  Ext.layout.container.HBox ,
    alias: 'layout.gridcolumn',
    type : 'gridcolumn',

    reserveOffset: false,

    firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first',
    lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last',

    initLayout: function() {
        if (!this.scrollbarWidth) {
            this.self.prototype.scrollbarWidth = Ext.getScrollbarSize().width;
        }
        this.grid = this.owner.up('[scrollerOwner]');
        this.callParent();
    },

    
    beginLayout: function (ownerContext) {
        var me = this,
            owner = me.owner,
            grid = me.grid,
            view = grid.view,
            items = me.getVisibleItems(),
            len = items.length,
            firstCls = me.firstHeaderCls, 
            lastCls = me.lastHeaderCls,
            i, item;

        
        
        
        if (grid.lockable) {
            if (owner.up('tablepanel') === view.normalGrid) {
                view = view.normalGrid.getView();
            } else {
                view = null;
            }
        }

        for (i = 0; i < len; i++) {
            item = items[i];

            
            
            item.isLast = false;
            item.removeCls([firstCls, lastCls]);
            if (i === 0) {
                item.addCls(firstCls);
            }

            if (i === len - 1) {
                item.addCls(lastCls);
                item.isLast = true;
            }
        }

        me.callParent(arguments);

        
        
        if (!owner.isColumn && Ext.getScrollbarSize().width && !grid.collapsed && view &&
                view.rendered && (ownerContext.viewTable = view.body.dom)) {
            ownerContext.viewContext = ownerContext.context.getCmp(view);
        }
    },

    roundFlex: function(width) {
        return Math.floor(width);
    },

    calculate: function(ownerContext) {
        this.callParent(arguments);

        
        
        if (ownerContext.state.parallelDone && (!this.owner.forceFit || ownerContext.flexedItems.length)) {
            
            
            ownerContext.setProp('columnWidthsDone', true);
        }

        
        if (ownerContext.viewContext) {
            ownerContext.state.tableHeight = ownerContext.viewTable.offsetHeight;
        }
    },

    completeLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            state = ownerContext.state;

        me.callParent(arguments);

        
        
        
        if (!ownerContext.flexedItems.length && !state.flexesCalculated && owner.forceFit &&

            
            
            me.convertWidthsToFlexes(ownerContext)) {
            me.cacheFlexes(ownerContext);
            ownerContext.invalidate({
                state: {
                    flexesCalculated: true
                }
            });
        } else {
            ownerContext.setProp('columnWidthsDone', true);
        }
    },

    convertWidthsToFlexes: function(ownerContext) {
        var me = this,
            totalWidth = 0,
            calculated = me.sizeModels.calculated,
            childItems, len, i, childContext, item;

        childItems = ownerContext.childItems;
        len = childItems.length;

        for (i = 0; i < len; i++) {
            childContext = childItems[i];
            item = childContext.target;

            totalWidth += childContext.props.width;

            
            if (!(item.fixed || item.resizable === false)) {

                
                
                item.flex = ownerContext.childItems[i].flex = childContext.props.width;
                item.width = null;
                childContext.widthModel = calculated;
            }
        }

        
        return totalWidth !== ownerContext.props.width;
    },

    
    getContainerSize: function(ownerContext) {
        var me = this,
            result,
            viewContext = ownerContext.viewContext,
            viewHeight;

        
        if (me.owner.isColumn) {
            result = me.getColumnContainerSize(ownerContext);
        }

        
        else {
            result = me.callParent(arguments);

            
            
            if (viewContext && !viewContext.heightModel.shrinkWrap &&
                    viewContext.target.componentLayout.ownerContext) { 
                viewHeight = viewContext.getProp('height');
                if (isNaN(viewHeight)) {
                    me.done = false;
                } else if (ownerContext.state.tableHeight > viewHeight) {
                    result.width -= Ext.getScrollbarSize().width;
                    ownerContext.state.parallelDone = false;
                    viewContext.invalidate();
                }
            }
        }




        return result;
    },

    getColumnContainerSize : function(ownerContext) {
        var padding = ownerContext.paddingContext.getPaddingInfo(),
            got = 0,
            needed = 0,
            gotWidth, gotHeight, width, height;

        
        
        

        
        
        

        if (!ownerContext.widthModel.shrinkWrap) {
            ++needed;
            width = ownerContext.getProp('innerWidth');
            gotWidth = (typeof width == 'number');
            if (gotWidth) {
                ++got;
                width -= padding.width;
                if (width < 0) {
                    width = 0;
                }
            }
        }

        if (!ownerContext.heightModel.shrinkWrap) {
            ++needed;
            height = ownerContext.getProp('innerHeight');
            gotHeight = (typeof height == 'number');
            if (gotHeight) {
                ++got;
                height -= padding.height;
                if (height < 0) {
                    height = 0;
                }
            }
        }

        return {
            width: width,
            height: height,
            needed: needed,
            got: got,
            gotAll: got == needed,
            gotWidth: gotWidth,
            gotHeight: gotHeight
        };
    },

    
    
    publishInnerCtSize: function(ownerContext) {
        var me = this,
            size = ownerContext.state.boxPlan.targetSize,
            cw = ownerContext.peek('contentWidth'),
            view;

        
        me.owner.tooNarrow = ownerContext.state.boxPlan.tooNarrow;

        
        if ((cw != null) && !me.owner.isColumn) {
            size.width = cw;

            
            view = me.owner.ownerCt.view;
            if (view.scrollFlags.y) {
                size.width += Ext.getScrollbarSize().width;
            }
        }

        return me.callParent(arguments);
    }
});


Ext.define('Ext.grid.ColumnManager', {
    alternateClassName: ['Ext.grid.ColumnModel'],

    columns: null,

    constructor: function(headerCt, secondHeaderCt) {
        this.headerCt = headerCt;

        
        if (secondHeaderCt) {
            this.secondHeaderCt = secondHeaderCt;
        }
    },

    getColumns: function() {
        if (!this.columns) {
            this.cacheColumns();
        }
        return this.columns;
    },

    
    getHeaderIndex: function(header) {
        
        if (header.isGroupHeader) {
            header = header.down(':not([isGroupHeader])');
        }
        return Ext.Array.indexOf(this.getColumns(), header);
    },

    
    getHeaderAtIndex: function(index) {
        var columns = this.getColumns();
        return columns.length ? columns[index] : null;
    },
    
    
    getHeaderById: function(id) {
        var columns = this.getColumns(),
            len = columns.length,
            i, header;
            
        for (i = 0; i < len; ++i) {
            header = columns[i];
            if (header.getItemId() === id) {
                return header;
            }
        }
        return null;
    },

    
    getVisibleHeaderClosestToIndex: function(index) {
        var result = this.getHeaderAtIndex(index);
        if (result && result.hidden) {
            result = result.next(':not([hidden])') || result.prev(':not([hidden])');
        }
        return result;
    },

    cacheColumns: function() {
        this.columns = this.headerCt.getVisibleGridColumns();
        if (this.secondHeaderCt) {
            Ext.Array.push(this.columns, this.secondHeaderCt.getVisibleGridColumns());
        }
    },

    invalidate: function() {
        this.columns = null;

        
        if (this.rootColumns) {
            this.rootColumns.invalidate();
        }
    }
}, function() {
    this.createAlias('indexOf', 'getHeaderIndex');
});


Ext.define('Ext.layout.container.Fit', {

    
    extend:  Ext.layout.container.Container ,
    alternateClassName: 'Ext.layout.FitLayout',

    alias: 'layout.fit',

    

    itemCls: Ext.baseCSSPrefix + 'fit-item',
    targetCls: Ext.baseCSSPrefix + 'layout-fit',
    type: 'fit',
   
    
    defaultMargins: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    },

    manageMargins: true,

    sizePolicies: {
        0: { readsWidth: 1, readsHeight: 1, setsWidth: 0, setsHeight: 0 },
        1: { readsWidth: 0, readsHeight: 1, setsWidth: 1, setsHeight: 0 },
        2: { readsWidth: 1, readsHeight: 0, setsWidth: 0, setsHeight: 1 },
        3: { readsWidth: 0, readsHeight: 0, setsWidth: 1, setsHeight: 1 }
    },

    getItemSizePolicy: function (item, ownerSizeModel) {
        
        var sizeModel = ownerSizeModel || this.owner.getSizeModel(),
            mode = (sizeModel.width.shrinkWrap ? 0 : 1) |
                   (sizeModel.height.shrinkWrap ? 0 : 2);

       return this.sizePolicies[mode];
    },

    beginLayoutCycle: function (ownerContext, firstCycle) {
        var me = this,
            
            resetHeight = me.lastHeightModel && me.lastHeightModel.calculated,
            resetWidth = me.lastWidthModel && me.lastWidthModel.calculated,
            resetSizes = resetWidth || resetHeight,
            maxChildMinHeight = 0, maxChildMinWidth = 0,
            c, childItems, i, item, length, margins, minHeight, minWidth, style, undef;

        me.callParent(arguments);

        
        
        
        
        if (resetSizes && ownerContext.targetContext.el.dom.tagName.toUpperCase() != 'TD') {
            resetSizes = resetWidth = resetHeight = false;
        }

        childItems = ownerContext.childItems;
        length = childItems.length;

        for (i = 0; i < length; ++i) {
            item = childItems[i];

            
            
            
            if (firstCycle) {
                c = item.target;
                minHeight = c.minHeight;
                minWidth = c.minWidth;

                if (minWidth || minHeight) {
                    margins = item.marginInfo || item.getMarginInfo();
                    
                    
                    minHeight += margins.height;
                    minWidth += margins.height;

                    
                    
                    if (maxChildMinHeight < minHeight) {
                        maxChildMinHeight = minHeight;
                    }
                    if (maxChildMinWidth < minWidth) {
                        maxChildMinWidth = minWidth;
                    }
                }
            }

            if (resetSizes) {
                style = item.el.dom.style;

                if (resetHeight) {
                    style.height = '';
                }
                if (resetWidth) {
                    style.width = '';
                }
            }
        }

        if (firstCycle) {
            ownerContext.maxChildMinHeight = maxChildMinHeight;
            ownerContext.maxChildMinWidth = maxChildMinWidth;
        }

        
        
        
        c = ownerContext.target;
        ownerContext.overflowX = (!ownerContext.widthModel.shrinkWrap && 
                                   ownerContext.maxChildMinWidth &&
                                   c.scrollFlags.x) || undef;

        ownerContext.overflowY = (!ownerContext.heightModel.shrinkWrap &&
                                   ownerContext.maxChildMinHeight &&
                                   c.scrollFlags.y) || undef;
    },

    calculate : function (ownerContext) {
        var me = this,
            childItems = ownerContext.childItems,
            length = childItems.length,
            containerSize = me.getContainerSize(ownerContext),
            info = {
                length: length,
                ownerContext: ownerContext,
                targetSize: containerSize
            },
            shrinkWrapWidth = ownerContext.widthModel.shrinkWrap,
            shrinkWrapHeight = ownerContext.heightModel.shrinkWrap,
            overflowX = ownerContext.overflowX,
            overflowY = ownerContext.overflowY,
            scrollbars, scrollbarSize, padding, i, contentWidth, contentHeight;

        if (overflowX || overflowY) {
            
            
            
            scrollbars = me.getScrollbarsNeeded(
                    overflowX && containerSize.width, overflowY && containerSize.height,
                    ownerContext.maxChildMinWidth, ownerContext.maxChildMinHeight);

            if (scrollbars) {
                scrollbarSize = Ext.getScrollbarSize();
                if (scrollbars & 1) { 
                    containerSize.height -= scrollbarSize.height;
                }
                if (scrollbars & 2) { 
                    containerSize.width -= scrollbarSize.width;
                }
            }
        }

        
        for (i = 0; i < length; ++i) {
            info.index = i;
            me.fitItem(childItems[i], info);
        }
        
        if (shrinkWrapHeight || shrinkWrapWidth) {
            padding = ownerContext.targetContext.getPaddingInfo();
            
            if (shrinkWrapWidth) {
                if (overflowY && !containerSize.gotHeight) {
                    
                    
                    
                    me.done = false;
                } else {
                    contentWidth = info.contentWidth + padding.width;
                    
                    
                    
                    if (scrollbars & 2) { 
                        contentWidth += scrollbarSize.width;
                    }
                    if (!ownerContext.setContentWidth(contentWidth)) {
                        me.done = false;
                    }
                }
            }

            if (shrinkWrapHeight) {
                if (overflowX && !containerSize.gotWidth) {
                    
                    
                    
                    me.done = false;
                } else {
                    contentHeight = info.contentHeight + padding.height;
                    
                    
                    
                    if (scrollbars & 1) { 
                        contentHeight += scrollbarSize.height;
                    }
                    if (!ownerContext.setContentHeight(contentHeight)) {
                        me.done = false;
                    }
                }
            }
        }
    },

    fitItem: function (itemContext, info) {
        var me = this;

        if (itemContext.invalid) {
            me.done = false;
            return;
        }

        info.margins = itemContext.getMarginInfo();
        info.needed = info.got = 0;

        me.fitItemWidth(itemContext, info);
        me.fitItemHeight(itemContext, info);

        
        if (info.got != info.needed) {
            me.done = false;
        }
    },

    fitItemWidth: function (itemContext, info) {
        var contentWidth, width;
        
        if (info.ownerContext.widthModel.shrinkWrap) {
            
            width = itemContext.getProp('width') + info.margins.width;
            

            contentWidth = info.contentWidth;
            if (contentWidth === undefined) {
                info.contentWidth = width;
            } else {
                info.contentWidth = Math.max(contentWidth, width);
            }
        } else if (itemContext.widthModel.calculated) {
            ++info.needed;
            if (info.targetSize.gotWidth) {
                ++info.got;
                this.setItemWidth(itemContext, info);
            }
        }

        this.positionItemX(itemContext, info);
    },

    fitItemHeight: function (itemContext, info) {
        var contentHeight, height;
        if (info.ownerContext.heightModel.shrinkWrap) {
            
            height = itemContext.getProp('height') + info.margins.height;
            

            contentHeight = info.contentHeight;
            if (contentHeight === undefined) {
                info.contentHeight = height;
            } else {
                info.contentHeight = Math.max(contentHeight, height);
            }
        } else if (itemContext.heightModel.calculated) {
            ++info.needed;
            if (info.targetSize.gotHeight) {
                ++info.got;
                this.setItemHeight(itemContext, info);
            }
        }

        this.positionItemY(itemContext, info);
    },

    positionItemX: function (itemContext, info) {
        var margins = info.margins;

        
        
        if (info.index || margins.left) {
            itemContext.setProp('x', margins.left);
        }

        if (margins.width) {
            
            itemContext.setProp('margin-right', margins.width);
        }
    },

    positionItemY: function (itemContext, info) {
        var margins = info.margins;

        if (info.index || margins.top) {
            itemContext.setProp('y', margins.top);
        }

        if (margins.height) {
            
            itemContext.setProp('margin-bottom', margins.height);
        }
    },

    setItemHeight: function (itemContext, info) {
        itemContext.setHeight(info.targetSize.height - info.margins.height);
    },

    setItemWidth: function (itemContext, info) {
        itemContext.setWidth(info.targetSize.width - info.margins.width);
    }
});


Ext.define('Ext.panel.Table', {
    extend:  Ext.panel.Panel ,

    alias: 'widget.tablepanel',

           
                                 
                                  
                                      
                                           
                                    
                                   
      

    extraBaseCls: Ext.baseCSSPrefix + 'grid',
    extraBodyCls: Ext.baseCSSPrefix + 'grid-body',

    layout: 'fit',
    
    hasView: false,

    
    
    viewType: null,

    

    

    
    selType: 'rowmodel',

    

    

    

    

    
    scroll: true,

    

    

    

    

    

    

    deferRowRender: true,
     
    
    sortableColumns: true,

    
    enableLocking: false,

    
    
    scrollerOwner: true,

    
    enableColumnMove: true,
    
    
    sealedColumns: false,

    
    enableColumnResize: true,

    

    

    
    rowLines: true,

    

    
    
    

    
    
    colLinesCls: Ext.baseCSSPrefix + 'grid-with-col-lines',
    rowLinesCls: Ext.baseCSSPrefix + 'grid-with-row-lines',
    noRowLinesCls: Ext.baseCSSPrefix + 'grid-no-row-lines',
    hiddenHeaderCtCls: Ext.baseCSSPrefix + 'grid-header-ct-hidden',
    hiddenHeaderCls: Ext.baseCSSPrefix + 'grid-header-hidden',
    resizeMarkerCls: Ext.baseCSSPrefix + 'grid-resize-marker',
    emptyCls: Ext.baseCSSPrefix + 'grid-empty',

    initComponent: function() {

        var me          = this,
            headerCtCfg = me.columns || me.colModel,
            view,
            i, len,
            
            store       = me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store'),
            columns;

        if (me.columnLines) {
            me.addCls(me.colLinesCls);
        }

        me.addCls(me.rowLines ? me.rowLinesCls : me.noRowLinesCls);


        
        
        if (headerCtCfg instanceof Ext.grid.header.Container) {
            headerCtCfg.isRootHeader = true;
            me.headerCt = headerCtCfg;
        } else {

            
            
            if (me.enableLocking || me.hasLockedColumns(headerCtCfg)) {
                me.self.mixin('lockable', Ext.grid.locking.Lockable);
                me.injectLockable();
            }
            
            else {
                if (Ext.isArray(headerCtCfg)) {
                    headerCtCfg = {
                        items: headerCtCfg
                    };
                }
                Ext.apply(headerCtCfg, {
                    grid: me,
                    forceFit: me.forceFit,
                    sortable: me.sortableColumns,
                    enableColumnMove: me.enableColumnMove,
                    enableColumnResize: me.enableColumnResize,
                    sealed: me.sealedColumns,
                    isRootHeader: true
                });

                if (Ext.isDefined(me.enableColumnHide)) {
                    headerCtCfg.enableColumnHide = me.enableColumnHide;
                }

                
                if (!me.headerCt) {
                    me.headerCt = new Ext.grid.header.Container(headerCtCfg);
                }
            }
        }

        
        me.columns = me.headerCt.getGridColumns();

        me.scrollTask = new Ext.util.DelayedTask(me.syncHorizontalScroll, me);

        me.addEvents(
            
            'reconfigure',
            
            'viewready'
        );

        me.bodyCls = me.bodyCls || '';
        me.bodyCls += (' ' + me.extraBodyCls);
        
        me.cls = me.cls || '';
        me.cls += (' ' + me.extraBaseCls);

        
        delete me.autoScroll;

        
        
        if (!me.hasView) {

            
            columns = me.headerCt.getGridColumns();

            
            if (store.buffered && !store.remoteSort) {
                for (i = 0, len = columns.length; i < len; i++) {
                    columns[i].sortable = false;
                }
            }

            if (me.hideHeaders) {
                me.headerCt.height = 0;
                
                me.headerCt.hiddenHeaders = true;
                me.headerCt.addCls(me.hiddenHeaderCtCls);
                me.addCls(me.hiddenHeaderCls);
                
                
                if (Ext.isIEQuirks) {
                    me.headerCt.style = {
                        display: 'none'
                    };
                }
            }

            me.relayHeaderCtEvents(me.headerCt);
            me.features = me.features || [];
            if (!Ext.isArray(me.features)) {
                me.features = [me.features];
            }
            me.dockedItems = [].concat(me.dockedItems || []);
            me.dockedItems.unshift(me.headerCt);
            me.viewConfig = me.viewConfig || {};

            
            
            view = me.getView();

            me.items = [view];
            me.hasView = true;

            
            
            if (!me.hideHeaders) {
                view.on({
                    scroll: {
                        fn: me.onHorizontalScroll,
                        element: 'el',
                        scope: me
                    }
                });
            }

            
            me.bindStore(store, true);

            me.mon(view, {
                viewready: me.onViewReady,
                refresh: me.onRestoreHorzScroll,
                scope: me
            });
        }

        
        me.relayEvents(me.view, [
            
            'beforeitemmousedown',
            
            'beforeitemmouseup',
            
            'beforeitemmouseenter',
            
            'beforeitemmouseleave',
            
            'beforeitemclick',
            
            'beforeitemdblclick',
            
            'beforeitemcontextmenu',
            
            'itemmousedown',
            
            'itemmouseup',
            
            'itemmouseenter',
            
            'itemmouseleave',
            
            'itemclick',
            
            'itemdblclick',
            
            'itemcontextmenu',
            
            'beforecellclick',
            
            'cellclick',
            
            'beforecelldblclick',
            
            'celldblclick',
            
            'beforecellcontextmenu',
            
            'cellcontextmenu',
            
            'beforecellmousedown',
            
            'cellmousedown',
            
            'beforecellmouseup',
            
            'cellmouseup',
            
            'beforecellkeydown',
            
            'cellkeydown',
            
            'beforecontainermousedown',
            
            'beforecontainermouseup',
            
            'beforecontainermouseover',
            
            'beforecontainermouseout',
            
            'beforecontainerclick',
            
            'beforecontainerdblclick',
            
            'beforecontainercontextmenu',
            
            'containermouseup',
            
            'containermouseover',
            
            'containermouseout',
            
            'containerclick',
            
            'containerdblclick',
            
            'containercontextmenu',
            
            'selectionchange',
            
            'beforeselect',
            
            'select',
            
            'beforedeselect',
            
            'deselect'
        ]);

        me.callParent(arguments);
        me.addStateEvents(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange', 'filterchange']);

        
        if (!me.lockable && me.headerCt) {
            me.headerCt.on('afterlayout', me.onRestoreHorzScroll, me);
        }
    },

    
    hasLockedColumns: function(columns) {
        var i,
            len,
            column;

        
        if (Ext.isObject(columns)) {
            columns = columns.items;
        }
        for (i = 0, len = columns.length; i < len; i++) {
            column = columns[i];
            if (!column.processed && column.locked) {
                return true;
            }
        }
    },

    relayHeaderCtEvents: function (headerCt) {
        this.relayEvents(headerCt, [
            
            'columnresize',
            
            'columnmove',
            
            'columnhide',
            
            'columnshow',
            
            'columnschanged',
            
            'sortchange',
            
            'headerclick',
            
            'headercontextmenu',
            
            'headertriggerclick'
        ]);
    },

    getState: function(){
        var me = this,
            state = me.callParent(),
            storeState = me.store.getState();

        state = me.addPropertyToState(state, 'columns', me.headerCt.getColumnsState());

        if (storeState) {
            state.storeState = storeState;
        }
        return state;
    },

    applyState: function(state) {
        var me = this,
            sorter = state.sort,
            storeState = state.storeState,
            store = me.store,
            columns = state.columns;

        delete state.columns;

        
        
        me.callParent(arguments);

        if (columns) {
            me.headerCt.applyColumnsState(columns);
        }

        
        if (sorter) {
            if (store.remoteSort) {
                
                store.sort({
                    property: sorter.property,
                    direction: sorter.direction,
                    root: sorter.root
                }, null, false);
            } else {
                store.sort(sorter.property, sorter.direction);
            }
        }
        
        else if (storeState) {
            store.applyState(storeState);
        }
    },

    
    getStore: function(){
        return this.store;
    },

    
    getView: function() {
        var me = this,
            sm;

        if (!me.view) {
            sm = me.getSelectionModel();

            
            Ext.widget(Ext.apply({

                
                grid: me,
                deferInitialRefresh: me.deferRowRender !== false,
                trackOver: me.trackMouseOver !== false,
                scroll: me.scroll,
                xtype: me.viewType,
                store: me.store,
                headerCt: me.headerCt,
                columnLines: me.columnLines,
                rowLines: me.rowLines,
                selModel: sm,
                features: me.features,
                panel: me,
                emptyText: me.emptyText || ''
            }, me.viewConfig));

            
            
            
            
            if (me.view.emptyText) {
                me.view.emptyText = '<div class="' + me.emptyCls + '">' + me.view.emptyText + '</div>';
            }

            
            me.view.getComponentLayout().headerCt = me.headerCt;

            me.mon(me.view, {
                uievent: me.processEvent,
                scope: me
            });
            sm.view = me.view;
            me.headerCt.view = me.view;
        }
        return me.view;
    },

    
    setAutoScroll: Ext.emptyFn,

    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {
        var me = this,
            header;

        if (cellIndex !== -1) {
            header = me.columnManager.getColumns()[cellIndex];
            return header.processEvent.apply(header, arguments);
        }
    },

    
    determineScrollbars: function () {
    },

    
    invalidateScroller: function () {
    },

    scrollByDeltaY: function(yDelta, animate) {
        this.getView().scrollBy(0, yDelta, animate);
    },

    scrollByDeltaX: function(xDelta, animate) {
        this.getView().scrollBy(xDelta, 0, animate);
    },

    afterCollapse: function() {
        var me = this;
        me.saveScrollPos();
        me.saveScrollPos();
        me.callParent(arguments);
    },

    afterExpand: function() {
        var me = this;
        me.callParent(arguments);
        me.restoreScrollPos();
        me.restoreScrollPos();
    },

    saveScrollPos: Ext.emptyFn,

    restoreScrollPos: Ext.emptyFn,
    
    onHeaderResize: function(){
        this.delayScroll();
    },

    
    onHeaderMove: function(headerCt, header, colsToMove, fromIdx, toIdx) {
        var me = this;

        
        
        if (me.optimizedColumnMove === false) {
            me.view.refresh();
        }

        
        else {
            me.view.moveColumn(fromIdx, toIdx, colsToMove);
        }
        me.delayScroll();
    },

    
    onHeaderHide: function(headerCt, header) {
        this.view.refresh();
        this.delayScroll();
    },

    onHeaderShow: function(headerCt, header) {
        this.view.refresh();
        this.delayScroll();
    },

    delayScroll: function(){
        var target = this.getScrollTarget().el;
        if (target) {
            this.scrollTask.delay(10, null, null, [target.dom.scrollLeft]);
        }
    },

    
    onViewReady: function() {
         this.fireEvent('viewready', this);   
    },

    
    onRestoreHorzScroll: function() {
        var left = this.scrollLeftPos;
        if (left) {
            
            this.syncHorizontalScroll(left, true);
        }
    },

    getScrollerOwner: function() {
        var rootCmp = this;
        if (!this.scrollerOwner) {
            rootCmp = this.up('[scrollerOwner]');
        }
        return rootCmp;
    },

    
    getLhsMarker: function() {
        var me = this;
        return me.lhsMarker || (me.lhsMarker = Ext.DomHelper.append(me.el, {
            cls: me.resizeMarkerCls
        }, true));
    },

    
    getRhsMarker: function() {
        var me = this;

        return me.rhsMarker || (me.rhsMarker = Ext.DomHelper.append(me.el, {
            cls: me.resizeMarkerCls
        }, true));
    },

    
    getSelectionModel: function(){
        var me = this,
            selModel = me.selModel,
            applyMode, mode, type;
        
        if (!selModel) {
            selModel = {};
            
            applyMode = true;
        }

        if (!selModel.events) {
            
            type = selModel.selType || me.selType;
            applyMode = !selModel.mode;
            selModel = me.selModel = Ext.create('selection.' + type, selModel);
        }

        if (me.simpleSelect) {
            mode = 'SIMPLE';
        } else if (me.multiSelect) {
            mode = 'MULTI';
        }

        Ext.applyIf(selModel, {
            allowDeselect: me.allowDeselect
        });
        
        if (mode && applyMode) {
            selModel.setSelectionMode(mode);
        }

        if (!selModel.hasRelaySetup) {
            me.relayEvents(selModel, [
                'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect'
            ]);
            selModel.hasRelaySetup = true;
        }

        
        
        if (me.disableSelection) {
            selModel.locked = true;
        }
        return selModel;
    },
    
    getScrollTarget: function(){
        var owner = this.getScrollerOwner(),
            items = owner.query('tableview');
            
        return items[1] || items[0];
    },

    onHorizontalScroll: function(event, target) {
        this.syncHorizontalScroll(target.scrollLeft);
    },
    
    syncHorizontalScroll: function(left, setBody) {
        var me = this,
            scrollTarget;
            
        setBody = setBody === true;
        
        
        if (me.rendered && (setBody || left !== me.scrollLeftPos)) {
            
            
            if (setBody) {   
                scrollTarget = me.getScrollTarget();
                scrollTarget.el.dom.scrollLeft = left;
            }
            me.headerCt.el.dom.scrollLeft = left;
            me.scrollLeftPos = left;
        }
    },

    
    onStoreLoad: Ext.emptyFn,

    getEditorParent: function() {
        return this.body;
    },

    bindStore: function(store, initial) {
        var me = this,
            view = me.getView(),
            bufferedStore = store && store.buffered,
            bufferedRenderer;

        
        me.store = store;

        
        
        
        
        bufferedRenderer = me.findPlugin('bufferedrenderer');
        if (bufferedRenderer) {
            me.verticalScroller = bufferedRenderer;
            
            if (bufferedRenderer.store) {
                bufferedRenderer.bindStore(store);
            }
        } else if (bufferedStore) {
            me.verticalScroller = bufferedRenderer = me.addPlugin(Ext.apply({
                ptype: 'bufferedrenderer'
            }, me.initialConfig.verticalScroller));
        }

        if (view.store !== store) {
            if (initial) {
                
                view.bindStore(store, false, 'dataSource');
            } else {
                
                
                view.bindStore(store, false);
            }
        }

        me.mon(store, {
            load: me.onStoreLoad,
            scope: me
        });
        me.storeRelayers = me.relayEvents(store, [
            
            'filterchange'
        ]);

        
        if (bufferedRenderer) {
            me.invalidateScrollerOnRefresh = false;
        }

        if (me.invalidateScrollerOnRefresh !== undefined) {
            view.preserveScrollOnRefresh = !me.invalidateScrollerOnRefresh;
        }
    },

    unbindStore: function() {
        var me = this,
            store = me.store;

        if (store) {
            me.store = null;
            me.mun(store, {
                load: me.onStoreLoad,
                scope: me
            });
            Ext.destroy(me.storeRelayers);
        }
    },

    
    reconfigure: function(store, columns) {
        var me = this,
            view = me.getView(),
            originalDeferinitialRefresh,
            oldStore = me.store,
            headerCt = me.headerCt,
            oldColumns = headerCt ? headerCt.items.getRange() : me.columns;

        
        if (columns) {
            columns = Ext.Array.slice(columns);
        }

        me.fireEvent('beforereconfigure', me, store, columns, oldStore, oldColumns);
        if (me.lockable) {
            me.reconfigureLockable(store, columns);
        } else {
            Ext.suspendLayouts();
            if (columns) {
                
                delete me.scrollLeftPos;
                headerCt.removeAll();
                headerCt.add(columns);
            }
            
            
            if (store && (store = Ext.StoreManager.lookup(store)) !== oldStore) {
                
                if (me.store) {
                    me.unbindStore();
                }

                
                originalDeferinitialRefresh = view.deferInitialRefresh;
                view.deferInitialRefresh = false;
                me.bindStore(store);
                view.deferInitialRefresh = originalDeferinitialRefresh;
            } else {
                me.getView().refresh();
            }
            headerCt.setSortState();
            Ext.resumeLayouts(true);
        }
        me.fireEvent('reconfigure', me, store, columns, oldStore, oldColumns);
    },
    
    beforeDestroy: function(){
        var task = this.scrollTask;
        if (task) {
            task.cancel();
            this.scrollTask = null;
        }
        this.callParent();
    },
    
    onDestroy: function(){
        if (this.lockable) {
            this.destroyLockable();
        }
        this.callParent();
        
    }
});


Ext.define('Ext.util.CSS', function() {
    var CSS,
        rules = null,
        doc = document,
        camelRe = /(-[a-z])/gi,
        camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };

    return {

        singleton: true,

        rules: rules,

        initialized: false,

        constructor: function() {
            
            CSS = this;
        },

        
        createStyleSheet : function(cssText, id) {
            var ss,
                head = doc.getElementsByTagName("head")[0],
                styleEl = doc.createElement("style");

            styleEl.setAttribute("type", "text/css");
            if (id) {
               styleEl.setAttribute("id", id);
            }

            if (Ext.isIE) {
               head.appendChild(styleEl);
               ss = styleEl.styleSheet;
               ss.cssText = cssText;
            } else {
                try{
                    styleEl.appendChild(doc.createTextNode(cssText));
                } catch(e) {
                   styleEl.cssText = cssText;
                }
                head.appendChild(styleEl);
                ss = styleEl.styleSheet ? styleEl.styleSheet : (styleEl.sheet || doc.styleSheets[doc.styleSheets.length-1]);
            }
            CSS.cacheStyleSheet(ss);
            return ss;
        },

        
        removeStyleSheet : function(id) {
            var existing = doc.getElementById(id);
            if (existing) {
                existing.parentNode.removeChild(existing);
            }
        },

        
        swapStyleSheet : function(id, url) {
            var ss;
            CSS.removeStyleSheet(id);
            ss = doc.createElement("link");
            ss.setAttribute("rel", "stylesheet");
            ss.setAttribute("type", "text/css");
            ss.setAttribute("id", id);
            ss.setAttribute("href", url);
            doc.getElementsByTagName("head")[0].appendChild(ss);
        },

        
        refreshCache : function() {
            return CSS.getRules(true);
        },

        
        cacheStyleSheet : function(ss) {
            if (!rules) {
                rules = CSS.rules = {};
            }
            try {
                var ssRules = ss.cssRules || ss.rules,
                    i = ssRules.length - 1,
                    imports = ss.imports,
                    len = imports ? imports.length : 0,
                    rule, j;
                    
                
                for (j = 0; j < len; ++j) {
                    CSS.cacheStyleSheet(imports[j]);
                }

                for (; i >= 0; --i) {
                    rule = ssRules[i];
                    
                    if (rule.styleSheet) {
                        CSS.cacheStyleSheet(rule.styleSheet);
                    }
                    CSS.cacheRule(rule, ss);
                }
            } catch(e) {}
        },

        cacheRule: function(cssRule, styleSheet) {
            
            if (cssRule.styleSheet) {
                return CSS.cacheStyleSheet(cssRule.styleSheet);
            }

            var selectorText = cssRule.selectorText,
                selectorCount, j;

            if (selectorText) {

                
                selectorText = selectorText.split(',');
                selectorCount = selectorText.length;
                for (j = 0; j < selectorCount; j++) {
                    
                    
                    rules[Ext.String.trim(selectorText[j]).toLowerCase()] = {
                        parentStyleSheet: styleSheet,
                        cssRule: cssRule
                    };
                };
            }
        },

        
        getRules : function(refreshCache) {
            var result = {},
                selector;

            if (rules === null || refreshCache) {
                CSS.refreshCache();
            }
            for (selector in rules) {
                result[selector] = rules[selector].cssRule;
            }
            return result;
        },
        
        refreshCache: function() {
            var ds = doc.styleSheets,
                i = 0,
                len = ds.length;

            rules = CSS.rules = {}
            for (; i < len; i++) {
                try {
                    if (!ds[i].disabled) {
                        CSS.cacheStyleSheet(ds[i]);
                    }
                } catch(e) {}
            }
        },

        
        getRule: function(selector, refreshCache, rawCache) {
            var i, result;

            if (!rules || refreshCache) {
                CSS.refreshCache();
            }
            if (!Ext.isArray(selector)) {
                result = rules[selector.toLowerCase()]
                if (result && !rawCache) {
                    result = result.cssRule;
                }
                return result || null;
            }
            for (i = 0; i < selector.length; i++) {
                if (rules[selector[i]]) {
                    return rawCache ? rules[selector[i].toLowerCase()] : rules[selector[i].toLowerCase()].cssRule;
                }
            }
            return null;
        },

        
        createRule: function(styleSheet, selector, cssText) {
            var result,
                ruleSet = styleSheet.cssRules || styleSheet.rules,
                index = ruleSet.length;

            if (styleSheet.insertRule) {
                styleSheet.insertRule(selector + '{' + cssText + '}', index);
            } else {
                styleSheet.addRule(selector, cssText||' ');
            }
            CSS.cacheRule(result = ruleSet[index], styleSheet);
            return result;
        },

        
        updateRule : function(selector, property, value) {
            var rule, i, styles;
            if (!Ext.isArray(selector)) {
                rule = CSS.getRule(selector);
                if (rule) {
                    
                    if (arguments.length == 2) {
                        styles = Ext.Element.parseStyles(property);
                        for (property in styles) {
                            rule.style[property.replace(camelRe, camelFn)] = styles[property];
                        }
                    } else {
                        rule.style[property.replace(camelRe, camelFn)] = value;
                    }
                    return true;
                }
            } else {
                for (i = 0; i < selector.length; i++) {
                    if (CSS.updateRule(selector[i], property, value)) {
                        return true;
                    }
                }
            }
            return false;
        },

        deleteRule: function(selector) {
            var rule = CSS.getRule(selector, false, true),
                styleSheet, index;

            if (rule) {
                styleSheet = rule.parentStyleSheet;
                index = Ext.Array.indexOf(styleSheet.cssRules || styleSheet.rules, rule.cssRule);
                if (styleSheet.deleteRule) {
                    styleSheet.deleteRule(index);
                } else {
                    styleSheet.removeRule(index);
                }
                delete rules[selector];
            }
        }
    };
});


Ext.define('Ext.view.TableLayout', {
    extend:  Ext.layout.component.Auto ,
                               

    alias: ['layout.tableview'],
    type: 'tableview',

    beginLayout: function(ownerContext) {
        var me = this,
            otherSide = me.owner.lockingPartner,
            owner = me.owner;

        me.callParent(arguments);
        
        
        if (otherSide) {
            me.lockedGrid = me.owner.up('[lockable]');
            me.lockedGrid.needsRowHeightSync = true;
            if (!ownerContext.lockingPartner) {
                ownerContext.lockingPartner = ownerContext.context.getItem(otherSide, otherSide.el);
                if (ownerContext.lockingPartner && !ownerContext.lockingPartner.lockingPartner) {
                    ownerContext.lockingPartner.lockingPartner = ownerContext;
                }
            }
        }

        
        ownerContext.headerContext = ownerContext.context.getCmp(me.headerCt);

        
        if (me.owner.body.dom) {
            ownerContext.bodyContext = ownerContext.getEl(me.owner.body);
        }
        if (Ext.isWebKit) {
            owner.el.select(owner.getBodySelector()).setStyle('table-layout', 'auto');
        }
    },

    calculate: function(ownerContext) {
        var me = this,
            lockingPartner = me.lockingPartner,
            owner = me.owner,
            contentHeight = 0,
            emptyEl;

        
        
        if (ownerContext.headerContext.hasProp('columnWidthsDone')) {
            if (!me.setColumnWidths(ownerContext)) {
                me.done = false;
                return;
            }
            ownerContext.state.columnWidthsSynced = true;
            if (ownerContext.bodyContext) {
                emptyEl = me.owner.el.down('.' + owner.ownerCt.emptyCls, true);
                if (!emptyEl) {
                    contentHeight = ownerContext.bodyContext.el.dom.offsetHeight;
                    ownerContext.bodyContext.setHeight(contentHeight, false);
                } else {
                    contentHeight = emptyEl.offsetHeight;
                }
                ownerContext.setProp('contentHeight', contentHeight);
            }
            
            
            
            if (lockingPartner && !lockingPartner.state.columnWidthsSynced) {
                me.done = false;
            } else {
                me.callParent(arguments);
            }

        } else {
            me.done = false;
        }
    },

    measureContentHeight: function(ownerContext) {
        var lockingPartner = ownerContext.lockingPartner;

        
        
        if (!ownerContext.bodyContext || (ownerContext.state.columnWidthsSynced && (!lockingPartner || lockingPartner.state.columnWidthsSynced))) {
            return this.callParent(arguments);
        }
    },

    setColumnWidths: function(ownerContext) {
        var me = this,
            owner = me.owner,
            context = ownerContext.context,
            columns = me.headerCt.getVisibleGridColumns(),
            column,
            i = 0, len = columns.length,
            tableWidth = 0,
            columnLineWidth = 0,
            childContext,
            colWidth,
            isContentBox = !Ext.isBorderBox;

        
        if (context) {
            context.currentLayout = me;
        }

        
        for (i = 0; i < len; i++) {
            column = columns[i];
            childContext = context.getCmp(column);
            colWidth = childContext.props.width;
            if (isNaN(colWidth)) {
                
                
                
                
                childContext.getProp('width');
                return false;
            }
            tableWidth += colWidth;
            
            if (isContentBox && owner.columnLines) {
                
                
                
                
                if (!columnLineWidth) {
                    columnLineWidth = context.getCmp(column).borderInfo.width || 1;
                }
                colWidth -= columnLineWidth;
            }

            
            
            
            
            
            
            
            owner.body.select(owner.getColumnSizerSelector(column)).setWidth(colWidth);

        }
        
        owner.el.select(owner.getBodySelector()).setWidth(tableWidth);
        return true;
    },

    finishedLayout: function() {
        var me = this,
            owner = me.owner;

        me.callParent(arguments);

        if (Ext.isWebKit) {
            owner.el.select(owner.getBodySelector()).setStyle('table-layout', '');
        }
        
        
        if (owner.refreshCounter && me.lockedGrid && me.lockedGrid.syncRowHeight && me.lockedGrid.needsRowHeightSync) {
            me.lockedGrid.syncRowHeights();
            me.lockedGrid.needsRowHeightSync = false;
        }
    }
});


Ext.define('Ext.view.NodeCache', {
    constructor: function(view) {
        this.view = view;
        this.clear();
        this.el = new Ext.dom.AbstractElement.Fly();
    },

    
    clear: function(removeDom) {
        var me = this,
            elements = this.elements,
            i, el;

        if (removeDom) {
            for (i in elements) {
                el = elements[i];
                el.parentNode.removeChild(el);
            }
        }
        me.elements = {};
        me.count = me.startIndex = 0;
        me.endIndex = -1;
    },

    
    fill: function(newElements, startIndex) {
        var me = this,
            elements = me.elements = {},
            i,
            len = newElements.length;

        if (!startIndex) {
            startIndex = 0;
        }
        for (i = 0; i < len; i++) {
            elements[startIndex + i] = newElements[i];
        }
        me.startIndex = startIndex;
        me.endIndex = startIndex + len - 1;
        me.count = len;
        return this;
    },

    insert: function(insertPoint, nodes) {
        var me = this,
            elements = me.elements,
            i,
            nodeCount = nodes.length;

        
        if (me.count) {

            
            if (insertPoint < me.count) {
                for (i = me.endIndex + nodeCount; i >= insertPoint + nodeCount; i--) {
                    elements[i] = elements[i - nodeCount];
                    elements[i].setAttribute('data-recordIndex', i);
                }
            }
            me.endIndex = me.endIndex + nodeCount;
        }
        
        else {
            me.startIndex = insertPoint;
            me.endIndex = insertPoint + nodeCount - 1;
        }

        
        for (i = 0; i < nodeCount; i++, insertPoint++) {
            elements[insertPoint] = nodes[i];
            elements[insertPoint].setAttribute('data-recordIndex', insertPoint);
        }
        me.count += nodeCount;
    },

    item: function(index, asDom) {
        var el = this.elements[index],
            result = null;

        if (el) {
            result = asDom ? this.elements[index] : this.el.attach(this.elements[index]);
        }
        return result;
    },

    first: function(asDom) {
        return this.item(this.startIndex, asDom);
    },

    last: function(asDom) {
        return this.item(this.endIndex, asDom);
    },

    getCount : function() {
        return this.count;
    },

    slice: function(start, end) {
        var elements = this.elements,
            result = [],
            i;

        if (arguments.length < 2) {
            end = this.endIndex;
        } else {
            end = Math.min(this.endIndex, end - 1);
        }
        for (i = start||this.startIndex; i <= end; i++) {
            result.push(elements[i]);
        }
        return result;
    },

    
    replaceElement: function(el, replacement, domReplace) {
        var elements = this.elements,
            index = (typeof el === 'number') ? el : this.indexOf(el);

        if (index > -1) {
            replacement = Ext.getDom(replacement);
            if (domReplace) {
                el = elements[index];
                el.parentNode.insertBefore(replacement, el);
                Ext.removeNode(el);
                replacement.setAttribute('data-recordIndex', index);
            }
            this.elements[index] = replacement;
        }
        return this;
    },

    
    indexOf: function(el) {
        var elements = this.elements,
            index;

        el = Ext.getDom(el);
        for (index = this.startIndex; index <= this.endIndex; index++) {
            if (elements[index] === el) {
                return index;
            }
        }
        return -1;
    },

    removeRange: function(start, end, removeDom) {
        var me = this,
            elements = me.elements,
            el,
            i, removeCount, fromPos;

        if (end === undefined) {
            end = me.count;
        } else {
            end = Math.min(me.endIndex + 1, end + 1);
        }
        if (!start) {
            start = 0;
        }
        removeCount = end - start;
        for (i = start, fromPos = end; i < me.endIndex; i++, fromPos++) {
            
            if (removeDom && i < end) {
                Ext.removeNode(elements[i]);
            }
            
            if (fromPos <= me.endIndex) {
                el = elements[i] = elements[fromPos];
                el.setAttribute('data-recordIndex', i);
            }
            
            else {
                delete elements[i];
            }
        }
        me.count -= removeCount;
        me.endIndex -= removeCount;
    },

    
    removeElement: function(keys, removeDom) {
        var me = this,
            inKeys,
            key,
            elements = me.elements,
            el,
            deleteCount,
            keyIndex = 0, index,
            fromIndex;

        
        
        if (Ext.isArray(keys)) {
            inKeys = keys;
            keys = [];
            deleteCount = inKeys.length;
            for (keyIndex = 0; keyIndex < deleteCount; keyIndex++) {
                key = inKeys[keyIndex];
                if (typeof key !== 'number') {
                    key = me.indexOf(key);
                }
                
                
                if (key >= me.startIndex && key <= me.endIndex) {
                    keys[keys.length] = key;
                }
            }
            Ext.Array.sort(keys);
            deleteCount = keys.length;
        } else {
            
            if (keys < me.startIndex || keys > me.endIndex) {
                return;
            }
            deleteCount = 1;
            keys = [keys];
        }

        
        
        for (index = fromIndex = keys[0], keyIndex = 0; index <= me.endIndex; index++, fromIndex++) {

            
            
            
            if (keyIndex < deleteCount && index === keys[keyIndex]) {
                fromIndex++;
                keyIndex++;
                if (removeDom) {
                    Ext.removeNode(elements[index]);
                }
            }

            
            if (fromIndex <= me.endIndex && fromIndex >= me.startIndex) {
                el = elements[index] = elements[fromIndex];
                el.setAttribute('data-recordIndex', index);
            } else {
                delete elements[index];
            }
        }
        me.endIndex -= deleteCount;
        me.count -= deleteCount;
    },

    
    scroll: function(newRecords, direction, removeCount) {
        var me = this,
            elements = me.elements,
            recCount = newRecords.length,
            i, el, removeEnd,
            newNodes,
            nodeContainer = me.view.getNodeContainer(),
            frag = document.createDocumentFragment();

        
        if (direction == -1) {
            for (i = (me.endIndex - removeCount) + 1; i <= me.endIndex; i++) {
                el = elements[i];
                delete elements[i];
                el.parentNode.removeChild(el);
            }
            me.endIndex -= removeCount;

            
            newNodes = me.view.bufferRender(newRecords, me.startIndex -= recCount);
            for (i = 0; i < recCount; i++) {
                elements[me.startIndex + i] = newNodes[i];
                frag.appendChild(newNodes[i]);
            }
            nodeContainer.insertBefore(frag, nodeContainer.firstChild);
        }

        
        else {
            removeEnd = me.startIndex + removeCount;
            for (i = me.startIndex; i < removeEnd; i++) {
                el = elements[i];
                delete elements[i];
                el.parentNode.removeChild(el);
            }
            me.startIndex = i;

            
            newNodes = me.view.bufferRender(newRecords, me.endIndex + 1);
            for (i = 0; i < recCount; i++) {
                elements[me.endIndex += 1] = newNodes[i];
                frag.appendChild(newNodes[i]);
            }
            nodeContainer.appendChild(frag);
        }
        
        me.count = me.endIndex - me.startIndex + 1;
    }
});


Ext.define('Ext.view.Table', {
    extend:  Ext.view.View ,
    alias: 'widget.tableview',
               
                               
                               
                             
                               
                                  
      

    componentLayout: 'tableview',

    baseCls: Ext.baseCSSPrefix + 'grid-view',

    
    firstCls: Ext.baseCSSPrefix + 'grid-cell-first',

    
    lastCls: Ext.baseCSSPrefix + 'grid-cell-last',

    headerRowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-header-row',

    selectedItemCls: Ext.baseCSSPrefix + 'grid-row-selected',
    beforeSelectedItemCls: Ext.baseCSSPrefix + 'grid-row-before-selected',
    selectedCellCls: Ext.baseCSSPrefix + 'grid-cell-selected',
    focusedItemCls: Ext.baseCSSPrefix + 'grid-row-focused',
    beforeFocusedItemCls: Ext.baseCSSPrefix + 'grid-row-before-focused',
    tableFocusedFirstCls: Ext.baseCSSPrefix + 'grid-table-focused-first',
    tableSelectedFirstCls: Ext.baseCSSPrefix + 'grid-table-selected-first',
    tableOverFirstCls: Ext.baseCSSPrefix + 'grid-table-over-first',
    overItemCls: Ext.baseCSSPrefix + 'grid-row-over',
    beforeOverItemCls: Ext.baseCSSPrefix + 'grid-row-before-over',
    altRowCls:   Ext.baseCSSPrefix + 'grid-row-alt',
    dirtyCls: Ext.baseCSSPrefix + 'grid-dirty-cell',
    rowClsRe: new RegExp('(?:^|\\s*)' + Ext.baseCSSPrefix + 'grid-row-(first|last|alt)(?:\\s+|$)', 'g'),
    cellRe: new RegExp(Ext.baseCSSPrefix + 'grid-cell-([^\\s]+) ', ''),
    positionBody: true,

    
    trackOver: true,

    
    getRowClass: null,

    
    stripeRows: true,
    
    
    markDirty : true,

    

    
    tpl: '{%values.view.tableTpl.applyOut(values, out)%}',

    tableTpl: [
        '{%',
            
            'var view=values.view,tableCls=["' + Ext.baseCSSPrefix + '" + view.id + "-table ' + Ext.baseCSSPrefix + 'grid-table"];',
             'if (view.columnLines) tableCls[tableCls.length]=view.ownerCt.colLinesCls;',
             'if (view.rowLines) tableCls[tableCls.length]=view.ownerCt.rowLinesCls;',
        '%}',
        '<table role="presentation" id="{view.id}-table" class="{[tableCls.join(" ")]}" border="0" cellspacing="0" cellpadding="0" style="{tableStyle}" tabIndex="-1">',
            '{[view.renderColumnSizer(out)]}',
            '{[view.renderTHead(values, out)]}',
            '{[view.renderTFoot(values, out)]}',
            '<tbody id="{view.id}-body">',
            '{%',
                'view.renderRows(values.rows, values.viewStartIndex, out);',
            '%}',
            '</tbody>',
        '</table>',
        {
            priority: 0
        }
    ],

    rowTpl: [
        '{%',
            'var dataRowCls = values.recordIndex === -1 ? "" : " ' + Ext.baseCSSPrefix + 'grid-data-row";',
        '%}',
        '<tr role="row" {[values.rowId ? ("id=\\"" + values.rowId + "\\"") : ""]} ',
            'data-boundView="{view.id}" ',
            'data-recordId="{record.internalId}" ',
            'data-recordIndex="{recordIndex}" ',
            'class="{[values.itemClasses.join(" ")]} {[values.rowClasses.join(" ")]}{[dataRowCls]}" ',
            '{rowAttr:attributes} tabIndex="-1">',
            '<tpl for="columns">' +
                '{%',
                    'parent.view.renderCell(values, parent.record, parent.recordIndex, xindex - 1, out, parent)',
                 '%}',
            '</tpl>',
        '</tr>',
        {
            priority: 0
        }
    ],

    cellTpl: [
        '<td role="gridcell" class="{tdCls}" {tdAttr} id="{[Ext.id()]}">',
            '<div {unselectableAttr} class="' + Ext.baseCSSPrefix + 'grid-cell-inner {innerCls}"',
                'style="text-align:{align};<tpl if="style">{style}</tpl>">{value}</div>',
        '</td>', {
            priority: 0
        }
    ],

    
    refreshSelmodelOnRefresh: false,

    tableValues: {},

    
    
    rowValues: {
        itemClasses: [],
        rowClasses: []
    },
    cellValues: {
        classes: [
            Ext.baseCSSPrefix + 'grid-cell ' + Ext.baseCSSPrefix + 'grid-td' 
        ]
    },

    
    renderBuffer: document.createElement('div'),

    constructor: function(config) {
        
        if (config.grid.isTree) {
            config.baseCls = Ext.baseCSSPrefix + 'tree-view';
        }
        this.callParent([config]);
    },

    initComponent: function() {
        var me = this,
            scroll = me.scroll;
            
        this.addEvents(
            
            'beforecellclick',
            
            'cellclick',
            
            'beforecelldblclick',
            
            'celldblclick',
            
            'beforecellcontextmenu',
            
            'cellcontextmenu',
            
            'beforecellmousedown',
            
            'cellmousedown',
            
            'beforecellmouseup',
            
            'cellmouseup',
            
            'beforecellkeydown',
            
            'cellkeydown'
        );

        
        me.body = new Ext.dom.Element.Fly();
        me.body.id = me.id + 'gridBody';

        
        
        me.autoScroll = undefined;

        
        
        if (!me.trackOver) {
            me.overItemCls = null;
            me.beforeOverItemCls = null;
        }

        
        if (scroll === true || scroll === 'both') {
            me.autoScroll = true;
        } else if (scroll === 'horizontal') {
            me.overflowX = 'auto';
        } else if (scroll === 'vertical') {
            me.overflowY = 'auto';
        }
        me.selModel.view = me;
        me.headerCt.view = me;

        
        
        me.grid.view = me;
        me.initFeatures(me.grid);
        delete me.grid;

        
        me.tpl = me.getTpl('tpl');
        me.itemSelector = me.getItemSelector();
        me.all = new Ext.view.NodeCache(me);
        me.callParent();
    },
    
    
    moveColumn: function(fromIdx, toIdx, colsToMove) {
        var me = this,
            fragment = (colsToMove > 1) ? document.createDocumentFragment() : undefined,
            destinationCellIdx = toIdx,
            colCount = me.getGridColumns().length,
            lastIndex = colCount - 1,
            doFirstLastClasses = (me.firstCls || me.lastCls) && (toIdx === 0 || toIdx == colCount || fromIdx === 0 || fromIdx == lastIndex),
            i,
            j,
            rows, len, tr, cells,
            tables;

        
        
        if (me.rendered && toIdx !== fromIdx) {
            
            
            rows = me.el.query(me.getDataRowSelector());

            if (toIdx > fromIdx && fragment) {
                destinationCellIdx -= colsToMove;
            }

            for (i = 0, len = rows.length; i < len; i++) {
                tr = rows[i];
                cells = tr.childNodes;

                
                if (doFirstLastClasses) {

                    if (cells.length === 1) {
                        Ext.fly(cells[0]).addCls(me.firstCls);
                        Ext.fly(cells[0]).addCls(me.lastCls);
                        continue;
                    }
                    if (fromIdx === 0) {
                        Ext.fly(cells[0]).removeCls(me.firstCls);
                        Ext.fly(cells[1]).addCls(me.firstCls);
                    } else if (fromIdx === lastIndex) {
                        Ext.fly(cells[lastIndex]).removeCls(me.lastCls);
                        Ext.fly(cells[lastIndex - 1]).addCls(me.lastCls);
                    }
                    if (toIdx === 0) {
                        Ext.fly(cells[0]).removeCls(me.firstCls);
                        Ext.fly(cells[fromIdx]).addCls(me.firstCls);
                    } else if (toIdx === colCount) {
                        Ext.fly(cells[lastIndex]).removeCls(me.lastCls);
                        Ext.fly(cells[fromIdx]).addCls(me.lastCls);
                    }
                }

                if (fragment) {
                    for (j = 0; j < colsToMove; j++) {
                        fragment.appendChild(cells[fromIdx]);
                    }
                    tr.insertBefore(fragment, cells[destinationCellIdx] || null);
                } else {
                    tr.insertBefore(cells[fromIdx], cells[destinationCellIdx] || null);
                }
            }

            
            tables = me.el.query(me.getBodySelector());
            for (i = 0, len = tables.length; i < len; i++) {
                tr = tables[i];
                if (fragment) {
                    for (j = 0; j < colsToMove; j++) {
                        fragment.appendChild(tr.childNodes[fromIdx]);
                    }
                    tr.insertBefore(fragment, tr.childNodes[destinationCellIdx] || null);
                } else {
                    tr.insertBefore(tr.childNodes[fromIdx], tr.childNodes[destinationCellIdx] || null);
                }
            }
        }
    },

    
    scrollToTop: Ext.emptyFn,

    
    addElListener: function(eventName, fn, scope){
        this.mon(this, eventName, fn, scope, {
            element: 'el'
        });
    },

    
    getGridColumns: function() {
        return this.ownerCt.columnManager.getColumns();
    },

    
    getHeaderAtIndex: function(index) {
        return this.ownerCt.columnManager.getHeaderAtIndex(index);
    },

    
    getCell: function(record, column) {
        var row = this.getNode(record, true);
        return Ext.fly(row).down(column.getCellSelector());
    },

    
    getFeature: function(id) {
        var features = this.featuresMC;
        if (features) {
            return features.get(id);
        }
    },

    
    
    findFeature: function(ftype) {
        if (this.features) {
            return Ext.Array.findBy(this.features, function(feature) {
                if (feature.ftype === ftype) {
                    return true;
                }
            });
        }
    },

    
    initFeatures: function(grid) {
        var me = this,
            i,
            features,
            feature,
            len;

        me.tableTpl = Ext.XTemplate.getTpl(this, 'tableTpl');
        me.rowTpl   = Ext.XTemplate.getTpl(this, 'rowTpl');
        me.cellTpl  = Ext.XTemplate.getTpl(this, 'cellTpl');

        me.featuresMC = new Ext.util.MixedCollection();
        features = me.features = me.constructFeatures();
        len = features ? features.length : 0;
        for (i = 0; i < len; i++) {
            feature = features[i];

            
            feature.view = me;
            feature.grid = grid;
            me.featuresMC.add(feature);
            feature.init(grid);
        }
    },
    
    renderTHead: function(values, out) {
        var headers = values.view.headerFns,
            len, i;
            
        if (headers) {
            for (i = 0, len = headers.length; i < len; ++i) {
                headers[i].call(this, values, out);
            }
        }
    },
    
    
    
    
    
    addHeaderFn: function(){
        var headers = this.headerFns;
        if (!headers) {
            headers = this.headerFns = [];
        }    
        headers.push(fn);
    },
    
    renderTFoot: function(values, out){
        var footers = values.view.footerFns,
            len, i;
            
        if (footers) {
            for (i = 0, len = footers.length; i < len; ++i) {
                footers[i].call(this, values, out);
            }
        }
    },
    
    addFooterFn: function(fn){
        var footers = this.footerFns;
        if (!footers) {
            footers = this.footerFns = [];
        }    
        footers.push(fn);
    },

    addTableTpl: function(newTpl) {
        return this.addTpl('tableTpl', newTpl);
    },

    addRowTpl: function(newTpl) {
        return this.addTpl('rowTpl', newTpl);
    },

    addCellTpl: function(newTpl) {
        return this.addTpl('cellTpl', newTpl);
    },

    addTpl: function(which, newTpl) {
        var me = this,
            tpl,
            prevTpl;
            
        newTpl = Ext.Object.chain(newTpl);

        
        
        
        
        
        if (!newTpl.isTemplate) {
            newTpl.applyOut = me.tplApplyOut;
        }

        
        for (tpl = me[which]; newTpl.priority < tpl.priority; tpl = tpl.nextTpl) {
            prevTpl = tpl;
        }

        
        if (prevTpl) {
            prevTpl.nextTpl = newTpl;
        }
        
        else {
            me[which] = newTpl;
        }
        newTpl.nextTpl = tpl;
        return newTpl;
    },
    
    tplApplyOut: function(values, out) {
        if (this.before) {
            if (this.before(values, out) === false) {
                return;
            }
        }
        this.nextTpl.applyOut(values, out);
        if (this.after) {
            this.after(values, out);
        }
    },

    
    constructFeatures: function() {
        var me = this,
            features = me.features,
            feature,
            result,
            i = 0, len;
        
        if (features) {
            result = [];
            len = features.length;
            for (; i < len; i++) {
                feature = features[i];
                if (!feature.isFeature) {
                    feature = Ext.create('feature.' + feature.ftype, feature);
                }
                result[i] = feature;
            }
        }
        return result;
    },

    beforeRender: function() {
        var me = this;
        me.callParent();

        if (!me.enableTextSelection) {
            me.protoEl.unselectable();
        }
    },

    
    onViewScroll: function(e, t) {
        this.callParent(arguments);
        this.fireEvent('bodyscroll', e, t);
    },

    
    
    
    createRowElement: function(record, index) {
        var me = this,
            div = me.renderBuffer;

        me.tpl.overwrite(div, me.collectData([record], index));
        
        return Ext.fly(div).down(me.getNodeContainerSelector(), true).firstChild;
    },

    
    
    
    bufferRender: function(records, index) {
        var me = this,
            div = me.renderBuffer;

        me.tpl.overwrite(div, me.collectData(records, index));
        return Ext.Array.toArray(Ext.fly(div).down(me.getNodeContainerSelector(), true).childNodes);
    },

    collectData: function(records, startIndex) {
        this.rowValues.view = this;

        return {
            view: this,
            rows: records,
            viewStartIndex: startIndex,
            tableStyle: this.bufferedRenderer ? ('position:absolute;top:' + this.bufferedRenderer.bodyTop) : ''
        };
    },

    
    
    
    collectNodes: function(targetEl) {
        this.all.fill(this.getNodeContainer().childNodes, this.all.startIndex);
    },

    
    
    
    
    
    
    refreshSize: function() {
        var me = this,
            grid,
            bodySelector = me.getBodySelector();

        
        
        if (bodySelector) {
            me.body.attach(me.el.child(bodySelector, true));
        }

        if (!me.hasLoadingHeight) {
            grid = me.up('tablepanel');

            
            
            Ext.suspendLayouts();

            me.callParent();

            
            
            grid.updateLayout();

            Ext.resumeLayouts(true);
        }
    },

    statics: {
        getBoundView: function(node) {
            return Ext.getCmp(node.getAttribute('data-boundView'));
        }
    },

    getRecord: function(node) {
        node = this.getNode(node);
        if (node) {
            var recordIndex = node.getAttribute('data-recordIndex');
            if (recordIndex) {
                recordIndex = parseInt(recordIndex, 10);
                if (recordIndex > -1) {
                    
                    
                    return this.store.data.getAt(recordIndex);
                }
            }
            return this.dataSource.data.get(node.getAttribute('data-recordId'));
        }
    },

    indexOf: function(node) {
        node = this.getNode(node, false);
        if (!node && node !== 0) {
            return -1;
        }
        return this.all.indexOf(node);
    },

    indexInStore: function(node) {
        node = this.getNode(node, true);
        if (!node && node !== 0) {
            return -1;
        }
        var recordIndex = node.getAttribute('data-recordIndex');
        if (recordIndex) {
            return parseInt(recordIndex, 10);
        }
        return this.dataSource.indexOf(this.getRecord(node));
    },

    renderRows: function(rows, viewStartIndex, out) {
        var rowValues = this.rowValues,
            rowCount = rows.length,            
            i;

        rowValues.view = this;
        rowValues.columns = this.ownerCt.columnManager.getColumns();

        for (i = 0; i < rowCount; i++, viewStartIndex++) {
            rowValues.itemClasses.length = rowValues.rowClasses.length = 0;
            this.renderRow(rows[i], viewStartIndex, out);
        }

        
        rowValues.view = rowValues.columns = rowValues.record = null;
    },

    

    renderColumnSizer: function(out) {
        var columns = this.getGridColumns(),
            len = columns.length, i,
            column, width;

        for (i = 0; i < len; i++) {
            column = columns[i];
            width = column.hidden ? 0 : (column.lastBox ? column.lastBox.width : Ext.grid.header.Container.prototype.defaultWidth);
            out.push('<colgroup><col class="', Ext.baseCSSPrefix, 'grid-cell-', columns[i].getItemId(), '" style="width:' + width + 'px"></colgroup>');
        }
    },

    
    renderRow: function(record, rowIdx, out) {
        var me = this,
            isMetadataRecord = rowIdx === -1,
            selModel = me.selModel,
            rowValues = me.rowValues,
            itemClasses = rowValues.itemClasses,
            rowClasses = rowValues.rowClasses,
            cls,
            rowTpl = me.rowTpl;

        
        rowValues.record = record;
        rowValues.recordId = record.internalId;
        rowValues.recordIndex = rowIdx;
        rowValues.rowId = me.getRowId(record);
        rowValues.itemCls = rowValues.rowCls = '';
        if (!rowValues.columns) {
            rowValues.columns = me.ownerCt.columnManager.getColumns();
        }

        itemClasses.length = rowClasses.length = 0;

        
        
        
        if (!isMetadataRecord) {
            itemClasses[0] = Ext.baseCSSPrefix + "grid-row";
            if (selModel && selModel.isRowSelected) {
                if (selModel.isRowSelected(rowIdx + 1)) {
                    itemClasses.push(me.beforeSelectedItemCls);
                }
                if (selModel.isRowSelected(record)) {
                    itemClasses.push(me.selectedItemCls);
                }
            }

            if (me.stripeRows && rowIdx % 2 !== 0) {
                rowClasses.push(me.altRowCls);
            }

            if (me.getRowClass) {
                cls = me.getRowClass(record, rowIdx, null, me.dataSource);
                if (cls) {
                    rowClasses.push(cls);
                }
            }
        }
        
        if (out) {
            rowTpl.applyOut(rowValues, out);
        } else {
            return rowTpl.apply(rowValues);
        }
    },

    
    renderCell: function(column, record, recordIndex, columnIndex, out) {
        var me = this,
            selModel = me.selModel,
            cellValues = me.cellValues,
            classes = cellValues.classes,
            fieldValue = record.data[column.dataIndex],
            cellTpl = me.cellTpl,
            value, clsInsertPoint;

        cellValues.record = record;
        cellValues.column = column;
        cellValues.recordIndex = recordIndex;
        cellValues.columnIndex = columnIndex;
        cellValues.cellIndex = columnIndex;
        cellValues.align = column.align;
        cellValues.tdCls = column.tdCls;
        cellValues.innerCls = column.innerCls;
        cellValues.style = cellValues.tdAttr = "";
        cellValues.unselectableAttr = me.enableTextSelection ? '' : 'unselectable="on"';

        if (column.renderer && column.renderer.call) {
            value = column.renderer.call(column.scope || me.ownerCt, fieldValue, cellValues, record, recordIndex, columnIndex, me.dataSource, me);
            if (cellValues.css) {
                
                
                record.cssWarning = true;
                cellValues.tdCls += ' ' + cellValues.css;
                delete cellValues.css;
            }
        } else {
            value = fieldValue;
        }
        cellValues.value = (value == null || value === '') ? '&#160;' : value;

        
        classes[1] = Ext.baseCSSPrefix + 'grid-cell-' + column.getItemId();
            
        
        
        clsInsertPoint = 2;

        if (column.tdCls) {
            classes[clsInsertPoint++] = column.tdCls;
        }
        if (me.markDirty && record.isModified(column.dataIndex)) {
            classes[clsInsertPoint++] = me.dirtyCls;
        }
        if (column.isFirstVisible) {
            classes[clsInsertPoint++] = me.firstCls;
        }
        if (column.isLastVisible) {
            classes[clsInsertPoint++] = me.lastCls;
        }
        if (!me.enableTextSelection) {
            classes[clsInsertPoint++] = Ext.baseCSSPrefix + 'unselectable';
        }

        classes[clsInsertPoint++] = cellValues.tdCls;
        if (selModel && selModel.isCellSelected && selModel.isCellSelected(me, recordIndex, columnIndex)) {
            classes[clsInsertPoint++] = (me.selectedCellCls);
        }

        
        classes.length = clsInsertPoint;

        cellValues.tdCls = classes.join(' ');

        cellTpl.applyOut(cellValues, out);
        
        
        cellValues.column = null;
    },

    
    getNode: function(nodeInfo, dataRow) {
        var fly,
            result = this.callParent(arguments);

        if (result && result.tagName) {
            if (dataRow) {
                if (!(fly = Ext.fly(result)).is(this.dataRowSelector)) {
                    return fly.down(this.dataRowSelector, true);
                }
            } else if (dataRow === false) {
                if (!(fly = Ext.fly(result)).is(this.itemSelector)) {
                    return fly.up(this.itemSelector, null, true);
                }
            }
        }
        return result;
    },
    
    getRowId: function(record){
        return this.id + '-record-' + record.internalId;
    },
    
    constructRowId: function(internalId){
        return this.id + '-record-' + internalId;
    },
    
    getNodeById: function(id, dataRow){
        id = this.constructRowId(id);
        return this.retrieveNode(id, dataRow);
    },
    
    getNodeByRecord: function(record, dataRow) {
        var id = this.getRowId(record);
        return this.retrieveNode(id, dataRow);
    },
    
    retrieveNode: function(id, dataRow){
        var result = this.el.getById(id, true),
            itemSelector = this.itemSelector,
            fly;

        if (dataRow === false && result) {
            if (!(fly = Ext.fly(result)).is(itemSelector)) {
                return fly.up(itemSelector, null, true);
            }
        }
        return result;    
    },

    
    updateIndexes: Ext.emptyFn,

    
    bodySelector: 'table',

    
    nodeContainerSelector: 'tbody',

    
    itemSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-row',

    
    dataRowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-data-row',

    
    cellSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell',
    
    
    sizerSelector: 'col.' + Ext.baseCSSPrefix + 'grid-cell',
    
    innerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-cell-inner',

    getNodeContainer: function() {
        return this.el.down(this.nodeContainerSelector, true);
    },

    
    getBodySelector: function() {
        return this.bodySelector + '.' + Ext.baseCSSPrefix + this.id + '-table';
    },

    
    getNodeContainerSelector: function() {
        return this.nodeContainerSelector;
    },

    
    getColumnSizerSelector: function(header) {
        return this.sizerSelector + '-' + header.getItemId();
    },

    
    getItemSelector: function() {
        return this.itemSelector;
    },

    
    getDataRowSelector: function() {
        return this.dataRowSelector;
    },

    
    getCellSelector: function(header) {
        var result = this.cellSelector;
        if (header) {
            result += '-' + header.getItemId();
        }
        return result;
    },

    
    getCellInnerSelector: function(header) {
        return this.getCellSelector(header) + ' ' + this.innerSelector;
    },

    
    addRowCls: function(rowInfo, cls) {
        var row = this.getNode(rowInfo, false);
        if (row) {
            Ext.fly(row).addCls(cls);
        }
    },

    
    removeRowCls: function(rowInfo, cls) {
        var row = this.getNode(rowInfo, false);
        if (row) {
            Ext.fly(row).removeCls(cls);
        }
    },

    setHighlightedItem: function(item) {
        var me = this,
            highlighted = me.highlightedItem;

        if (highlighted && me.el.isAncestor(highlighted) && me.isRowStyleFirst(highlighted)) {
            me.getRowStyleTableEl(highlighted).removeCls(me.tableOverFirstCls);
        }

        if (item && me.isRowStyleFirst(item)) {
            me.getRowStyleTableEl(item).addCls(me.tableOverFirstCls);
        }

        me.callParent(arguments);
    },

    
    onRowSelect : function(rowIdx) {
        var me = this;

        me.addRowCls(rowIdx, me.selectedItemCls);
        if (me.isRowStyleFirst(rowIdx)) {
            me.getRowStyleTableEl(rowIdx).addCls(me.tableSelectedFirstCls);
        } else {
            me.addRowCls(rowIdx - 1, me.beforeSelectedItemCls);
        }
    },

    
    onRowDeselect : function(rowIdx) {
        var me = this;

        me.removeRowCls(rowIdx, [me.selectedItemCls, me.focusedItemCls]);
        if (me.isRowStyleFirst(rowIdx)) {
            me.getRowStyleTableEl(rowIdx).removeCls([me.tableFocusedFirstCls, me.tableSelectedFirstCls]);
        } else {
            me.removeRowCls(rowIdx - 1, [me.beforeFocusedItemCls, me.beforeSelectedItemCls]);
        }
    },

    onCellSelect: function(position) {
        var cell = this.getCellByPosition(position);
        if (cell) {
            cell.addCls(this.selectedCellCls);
            this.scrollCellIntoView(cell);
        }
    },

    onCellDeselect: function(position) {
        var cell = this.getCellByPosition(position, true);
        if (cell) {
            Ext.fly(cell).removeCls(this.selectedCellCls);
        }

    },

    getCellByPosition: function(position, returnDom) {
        if (position) {
            var row   = this.getNode(position.row, true),
                header = this.ownerCt.columnManager.getHeaderAtIndex(position.column);

            if (header && row) {
                return Ext.fly(row).down(this.getCellSelector(header), returnDom);
            }
        }
        return false;
    },

    getFocusEl: function() {
        var me = this,
            result;

        if (me.refreshCounter) {
            result = me.focusedRow;

            
            if (!(result && me.el.contains(result))) {
                
                
                if (me.all.getCount() && (result = me.getNode(me.all.item(0).dom, true))) {
                    me.focusRow(result);
                } else {
                    result = me.body;
                }
            }
        } else {
            return me.el;
        }
        return Ext.get(result);
    },

    
    
    onRowFocus: function(rowIdx, highlight, supressFocus) {
        var me = this;

        if (highlight) {
            me.addRowCls(rowIdx, me.focusedItemCls);
            if (me.isRowStyleFirst(rowIdx)) {
                me.getRowStyleTableEl(rowIdx).addCls(me.tableFocusedFirstCls);
            } else {
                me.addRowCls(rowIdx - 1, me.beforeFocusedItemCls);
            }
            if (!supressFocus) {
                me.focusRow(rowIdx);
            }
            
        } else {
            me.removeRowCls(rowIdx, me.focusedItemCls);
            if (me.isRowStyleFirst(rowIdx)) {
                me.getRowStyleTableEl(rowIdx).removeCls(me.tableFocusedFirstCls);
            } else {
                me.removeRowCls(rowIdx - 1, me.beforeFocusedItemCls);
            }
        }

        if ((Ext.isIE6 || Ext.isIE7) && !me.ownerCt.rowLines) {
            me.repaintRow(rowIdx)
        }
    },

    focus: function(selectText, delay) {
        var me = this,
            saveScroll = Ext.isIE && !delay,
            scrollPos;

        
        if (saveScroll) {
            scrollPos = me.el.dom.scrollLeft;
        }
        this.callParent(arguments);
        if (saveScroll) {
            me.el.dom.scrollLeft = scrollPos;
        }
    },

    
    focusRow: function(row, delay) {
        var me = this,
            rowIdx,
            gridCollapsed = me.ownerCt && me.ownerCt.collapsed,
            record;

        
        if (me.isVisible(true) && !gridCollapsed && (row = me.getNode(row, true))) {
            me.scrollRowIntoView(row);
            record = me.getRecord(row);
            rowIdx = me.indexInStore(row);

            me.selModel.setLastFocused(record);
            me.focusedRow = row;
            me.focus(false, delay, function() {
                me.fireEvent('rowfocus', record, row, rowIdx);
            });
        }
    },

    scrollRowIntoView: function(row) {
        row = this.getNode(row, true);
        if (row) {
            Ext.fly(row).scrollIntoView(this.el, false);
        }
    },

    focusCell: function(position) {
        var me          = this,
            cell        = me.getCellByPosition(position),
            record      = me.getRecord(position.row);

        me.focusRow(record);
        if (cell) {
            me.scrollCellIntoView(cell);
            me.fireEvent('cellfocus', record, cell, position);
        }
    },

    scrollCellIntoView: function(cell) {
        
        
        if (cell.row != null && cell.column != null) {
            cell = this.getCellByPosition(cell);
        }
        if (cell) {
            Ext.fly(cell).scrollIntoView(this.el, true);
        }
    },

    
    scrollByDelta: function(delta, dir) {
        dir = dir || 'scrollTop';
        var elDom = this.el.dom;
        elDom[dir] = (elDom[dir] += delta);
    },

    
    isDataRow: function(row) {
        return Ext.fly(row).hasCls(Ext.baseCSSPrefix + 'grid-data-row');
    },

    syncRowHeights: function(firstRow, secondRow) {
        firstRow = Ext.get(firstRow);
        secondRow = Ext.get(secondRow);
        firstRow.dom.style.height = secondRow.dom.style.height = '';
        var me = this,
            rowTpl = me.rowTpl,
            firstRowHeight = firstRow.dom.offsetHeight,
            secondRowHeight = secondRow.dom.offsetHeight;

        
        if (firstRowHeight !== secondRowHeight) {

            
            while (rowTpl) {
                if (rowTpl.syncRowHeights) {
                    
                    if (rowTpl.syncRowHeights(firstRow, secondRow) === false) {
                        break;
                    }
                }
                rowTpl = rowTpl.nextTpl;
            }

            
            firstRowHeight = firstRow.dom.offsetHeight;
            secondRowHeight = secondRow.dom.offsetHeight;
            if (firstRowHeight !== secondRowHeight) {

                
                firstRow = firstRow.down('[data-recordId]') || firstRow;
                secondRow = secondRow.down('[data-recordId]') || secondRow;

                
                if (firstRow && secondRow) {
                    firstRow.dom.style.height = secondRow.dom.style.height = '';
                    firstRowHeight = firstRow.dom.offsetHeight;
                    secondRowHeight = secondRow.dom.offsetHeight;

                    if (firstRowHeight > secondRowHeight) {
                        firstRow.setHeight(firstRowHeight);
                        secondRow.setHeight(firstRowHeight);
                    } else if (secondRowHeight > firstRowHeight) {
                        firstRow.setHeight(secondRowHeight);
                        secondRow.setHeight(secondRowHeight);
                    }
                }
            }
        }
    },
    
    onIdChanged: function(store, rec, oldId, newId, oldInternalId){
        var me = this,
            rowDom;
            
        if (me.viewReady) {
            rowDom = me.getNodeById(oldInternalId);
            if (rowDom) {
                rowDom.setAttribute('data-recordId', rec.internalId);
                rowDom.id = me.getRowId(rec);
            }
        }
    },

    
    onUpdate : function(store, record, operation, changedFieldNames) {
        var me = this,
            rowTpl = me.rowTpl,
            index,
            oldRow, oldRowDom,
            newRowDom,
            newAttrs, attLen, attName, attrIndex,
            overItemCls, beforeOverItemCls,
            focusedItemCls, beforeFocusedItemCls,
            selectedItemCls, beforeSelectedItemCls,
            columns;

        if (me.viewReady) {
            
            oldRowDom = me.getNodeByRecord(record, false);

            
            if (oldRowDom) {
                overItemCls = me.overItemCls;
                beforeOverItemCls = me.overItemCls;
                focusedItemCls = me.focusedItemCls;
                beforeFocusedItemCls = me.beforeFocusedItemCls;
                selectedItemCls = me.selectedItemCls;
                beforeSelectedItemCls = me.beforeSelectedItemCls;
                
                index = me.indexInStore(record);
                oldRow = Ext.fly(oldRowDom, '_internal');
                newRowDom = me.createRowElement(record, index);
                if (oldRow.hasCls(overItemCls)) {
                    Ext.fly(newRowDom).addCls(overItemCls);
                }
                if (oldRow.hasCls(beforeOverItemCls)) {
                    Ext.fly(newRowDom).addCls(beforeOverItemCls);
                }
                if (oldRow.hasCls(focusedItemCls)) {
                    Ext.fly(newRowDom).addCls(focusedItemCls);
                }
                if (oldRow.hasCls(beforeFocusedItemCls)) {
                    Ext.fly(newRowDom).addCls(beforeFocusedItemCls);
                }
                if (oldRow.hasCls(selectedItemCls)) {
                    Ext.fly(newRowDom).addCls(selectedItemCls);
                }
                if (oldRow.hasCls(beforeSelectedItemCls)) {
                    Ext.fly(newRowDom).addCls(beforeSelectedItemCls);
                }
                columns = me.ownerCt.columnManager.getColumns();

                
                
                
                
                if (Ext.isIE9m && oldRowDom.mergeAttributes) {
                    oldRowDom.mergeAttributes(newRowDom, true);
                } else {
                    newAttrs = newRowDom.attributes;
                    attLen = newAttrs.length;
                    for (attrIndex = 0; attrIndex < attLen; attrIndex++) {
                        attName = newAttrs[attrIndex].name;
                        if (attName !== 'id') {
                            oldRowDom.setAttribute(attName, newAttrs[attrIndex].value);
                        }
                    }
                }

                
                
                if (columns.length) {
                    me.updateColumns(record, me.getNode(oldRowDom, true), me.getNode(newRowDom, true), columns, changedFieldNames);
                }

                
                while (rowTpl) {
                    if (rowTpl.syncContent) {
                        if (rowTpl.syncContent(oldRowDom, newRowDom) === false) {
                            break;
                        }
                    }
                    rowTpl = rowTpl.nextTpl;
                }

                
                
                me.fireEvent('itemupdate', record, index, oldRowDom);
                me.refreshSize();
            }
        }
    },

    updateColumns: function(record, oldRowDom, newRowDom, columns, changedFieldNames) {
        var me = this,
            newAttrs, attLen, attName, attrIndex,
            colCount = columns.length,
            colIndex,
            column,
            oldCell, newCell,
            row,
            
            
            
            
            editingPlugin = me.editingPlugin || (me.lockingPartner && me.ownerCt.ownerLockable.view.editingPlugin),

            
            isEditing = editingPlugin && editingPlugin.editing,
            cellSelector = me.getCellSelector();

            
            
            if (oldRowDom.mergeAttributes) {
                oldRowDom.mergeAttributes(newRowDom, true);
            } else {
                newAttrs = newRowDom.attributes;
                attLen = newAttrs.length;
                for (attrIndex = 0; attrIndex < attLen; attrIndex++) {
                    attName = newAttrs[attrIndex].name;
                    if (attName !== 'id') {
                        oldRowDom.setAttribute(attName, newAttrs[attrIndex].value);
                    }
                }
            }

        
        for (colIndex = 0; colIndex < colCount; colIndex++) {
            column = columns[colIndex];

            
            
            if (me.shouldUpdateCell(record, column, changedFieldNames)) {

                
                
                cellSelector = me.getCellSelector(column);
                oldCell = Ext.DomQuery.selectNode(cellSelector, oldRowDom);
                newCell = Ext.DomQuery.selectNode(cellSelector, newRowDom);

                
                if (isEditing) {
                    Ext.fly(oldCell).syncContent(newCell);
                }
                
                else {
                    
                    row = oldCell.parentNode;
                    row.insertBefore(newCell, oldCell);
                    row.removeChild(oldCell);
                }
            }
        }
    },

    shouldUpdateCell: function(record, column, changedFieldNames){
        
        
        
        
        if (column.hasCustomRenderer || !changedFieldNames) {
            return true;
        }

        if (changedFieldNames) {
            var len = changedFieldNames.length,
                i, field;

            for (i = 0; i < len; ++i) {
                field = changedFieldNames[i];
                if (field === column.dataIndex || field === record.idProperty) {
                    return true;
                }
            }
        }
        return false;
    },

    
    refresh: function() {
        var me = this,
            hasFocus = me.el && me.el.isAncestor(Ext.Element.getActiveElement());

        me.callParent(arguments);
        me.headerCt.setSortState();

        
        
        if (me.el && !me.all.getCount() && me.headerCt && me.headerCt.tooNarrow) {
            me.el.createChild({style:'position:absolute;height:1px;width:1px;left:' + (me.headerCt.getFullWidth() - 1) + 'px'});
        }

        if (hasFocus) {
            me.selModel.onLastFocusChanged(null, me.selModel.lastFocused);
        }
    },

    processItemEvent: function(record, row, rowIndex, e) {
        
        if (this.indexInStore(row) !== -1) {
            var me = this,
                cell = e.getTarget(me.getCellSelector(), row),
                cellIndex,
                map = me.statics().EventMap,
                selModel = me.getSelectionModel(),
                type = e.type,
                features = me.features,
                len = features.length,
                i, result, feature, header;

            if (type == 'keydown' && !cell && selModel.getCurrentPosition) {
                
                cell = me.getCellByPosition(selModel.getCurrentPosition(), true);
            }

            
            if (cell) {
                if (!cell.parentNode) {
                    
                    
                    return false;
                }
                
                header = me.getHeaderByCell(cell);
                cellIndex = Ext.Array.indexOf(me.getGridColumns(), header);
            } else {
                cellIndex = -1;
            }

            result = me.fireEvent('uievent', type, me, cell, rowIndex, cellIndex, e, record, row);

            if (result === false || me.callParent(arguments) === false) {
                me.selModel.onVetoUIEvent(type, me, cell, rowIndex, cellIndex, e, record, row);
                return false;
            }

            for (i = 0; i < len; ++i) {
                feature = features[i];
                
                
                if (feature.wrapsItem) {
                    if (feature.vetoEvent(record, row, rowIndex, e) === false) {
                        
                        
                        me.processSpecialEvent(e);
                        return false;
                    }
                }
            }

            
            if (type == 'mouseover' || type == 'mouseout') {
                return true;
            }

            if(!cell) {
                
                
                return true;
            }

            return !(
                
                (me['onBeforeCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) ||
                (me.fireEvent('beforecell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false) ||
                (me['onCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) ||
                (me.fireEvent('cell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false)
            );
        } else {
            
            this.processSpecialEvent(e);
            return false;
        }
    },

    processSpecialEvent: function(e) {
        var me = this,
            features = me.features,
            ln = features.length,
            type = e.type,
            i, feature, prefix, featureTarget,
            beforeArgs, args,
            panel = me.ownerCt;

        me.callParent(arguments);

        if (type == 'mouseover' || type == 'mouseout') {
            return;
        }

        for (i = 0; i < ln; i++) {
            feature = features[i];
            if (feature.hasFeatureEvent) {
                featureTarget = e.getTarget(feature.eventSelector, me.getTargetEl());
                if (featureTarget) {
                    prefix = feature.eventPrefix;
                    
                    
                    beforeArgs = feature.getFireEventArgs('before' + prefix + type, me, featureTarget, e);
                    args = feature.getFireEventArgs(prefix + type, me, featureTarget, e);

                    if (
                        
                        (me.fireEvent.apply(me, beforeArgs) === false) ||
                        
                        (panel.fireEvent.apply(panel, beforeArgs) === false) ||
                        
                        (me.fireEvent.apply(me, args) === false) ||
                        
                        (panel.fireEvent.apply(panel, args) === false)
                    ) {
                        return false;
                    }
                }
            }
        }
        return true;
    },

    onCellMouseDown: Ext.emptyFn,
    onCellMouseUp: Ext.emptyFn,
    onCellClick: Ext.emptyFn,
    onCellDblClick: Ext.emptyFn,
    onCellContextMenu: Ext.emptyFn,
    onCellKeyDown: Ext.emptyFn,
    onBeforeCellMouseDown: Ext.emptyFn,
    onBeforeCellMouseUp: Ext.emptyFn,
    onBeforeCellClick: Ext.emptyFn,
    onBeforeCellDblClick: Ext.emptyFn,
    onBeforeCellContextMenu: Ext.emptyFn,
    onBeforeCellKeyDown: Ext.emptyFn,

    
    expandToFit: function(header) {
        this.autoSizeColumn(header);
    },

    
    autoSizeColumn: function(header) {
        if (Ext.isNumber(header)) {
            header = this.getGridColumns[header];
        }
        if (header) {
            if (header.isGroupHeader) {
                header.autoSize();
                return;
            }
            delete header.flex;
            header.setWidth(this.getMaxContentWidth(header));
        }
    },

    
    getMaxContentWidth: function(header) {
        var me = this,
            cells = me.el.query(header.getCellInnerSelector()),
            originalWidth = header.getWidth(),
            i = 0,
            ln = cells.length,
            hasPaddingBug = Ext.supports.ScrollWidthInlinePaddingBug,
            columnSizer = me.body.select(me.getColumnSizerSelector(header)),
            max = Math.max,
            paddingAdjust, maxWidth;

        if (hasPaddingBug && ln > 0) {
            paddingAdjust = me.getCellPaddingAfter(cells[0]);
        }

        
        columnSizer.setWidth(1);

        
        maxWidth = header.textEl.dom.offsetWidth + header.titleEl.getPadding('lr');
        for (; i < ln; i++) {
            maxWidth = max(maxWidth, cells[i].scrollWidth);
        }
        if (hasPaddingBug) {
            
            maxWidth += paddingAdjust;
        }

        
        maxWidth = max(maxWidth, 40);

        
        columnSizer.setWidth(originalWidth);

        return maxWidth;
    },

    getPositionByEvent: function(e) {
        var me       = this,
            cellNode = e.getTarget(me.cellSelector),
            rowNode  = e.getTarget(me.itemSelector),
            record   = me.getRecord(rowNode),
            header   = me.getHeaderByCell(cellNode);

        return me.getPosition(record, header);
    },

    getHeaderByCell: function(cell) {
        if (cell) {
            var match = cell.className.match(this.cellRe);
            if (match && match[1]) {
                return this.ownerCt.columnManager.getHeaderById(match[1]);
            }
        }
        return false;
    },

    
    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {

        
        
        if (!pos) {
            return false;
        }

        var me           = this,
            row          = pos.row,
            column       = pos.column,
            rowCount     = me.dataSource.getCount(),
            lastCol      = me.ownerCt.columnManager.getColumns().length - 1,
            newRow       = row,
            newColumn    = column,
            activeHeader = me.ownerCt.columnManager.getHeaderAtIndex(column);

        
        if (!activeHeader || activeHeader.hidden || !rowCount) {
            return false;
        }

        e = e || {};
        direction = direction.toLowerCase();
        switch (direction) {
            case 'right':
                
                if (column === lastCol) {
                    
                    if (preventWrap || row === rowCount - 1) {
                        return false;
                    }
                    if (!e.ctrlKey) {
                        
                        newRow = me.walkRows(row, 1);
                        if (newRow !== row) {
                            newColumn = 0;
                        }
                    }
                
                } else {
                    if (!e.ctrlKey) {
                        newColumn = column + 1;
                    } else {
                        newColumn = lastCol;
                    }
                }
                break;

            case 'left':
                
                if (column === 0) {
                    
                    if (preventWrap || row === 0) {
                        return false;
                    }
                    if (!e.ctrlKey) {
                        
                        newRow = me.walkRows(row, -1);
                        if (newRow !== row) {
                            newColumn = lastCol;
                        }
                    }
                
                } else {
                    if (!e.ctrlKey) {
                        newColumn = column - 1;
                    } else {
                        newColumn = 0;
                    }
                }
                break;

            case 'up':
                
                if (row === 0) {
                    return false;
                
                } else {
                    if (!e.ctrlKey) {
                        newRow = me.walkRows(row, -1);
                    } else {
                        
                        newRow = me.walkRows(-1, 1);
                    }
                }
                break;

            case 'down':
                
                if (row === rowCount - 1) {
                    return false;
                
                } else {
                    if (!e.ctrlKey) {
                        newRow = me.walkRows(row, 1);
                    } else {
                        
                        newRow = me.walkRows(rowCount, -1);
                    }
                }
                break;
        }

        if (verifierFn && verifierFn.call(scope || me, {row: newRow, column: newColumn}) !== true) {
            return false;
        } else {
            return new Ext.grid.CellContext(me).setPosition(newRow, newColumn);
        }
    },

    
    walkRows: function(startRow, distance) {
        
        
        var me = this,
            moved = 0,
            lastValid = startRow,
            node,
            last = (me.dataSource.buffered ? me.dataSource.getTotalCount() : me.dataSource.getCount()) - 1,
            limit = (distance < 0) ? 0 : last,
            increment = limit ? 1 : -1,
            result = startRow;
            
        do {
            
            if (limit ? result >= limit : result <= 0) {
                return lastValid || limit;
            }
            
            
            result += increment;
            
            
            
            if ((node = Ext.fly(me.getNode(result, true))) && node.isVisible(true)) {
                moved += increment;
                lastValid = result;
            }
        } while (moved !== distance);
        return result;
    },

    
    walkRecs: function(startRec, distance) {
        
        
        var me = this,
            moved = 0,
            lastValid = startRec,
            node,
            last = (me.store.buffered ? me.store.getTotalCount() : me.store.getCount()) - 1,
            limit = (distance < 0) ? 0 : last,
            increment = limit ? 1 : -1,
            testIndex = me.store.indexOf(startRec),
            rec;

        do {
            
            if (limit ? testIndex >= limit : testIndex <= 0) {
                return lastValid;
            }

            
            testIndex += increment;

            
            
            rec = me.store.getAt(testIndex);
            if ((node = Ext.fly(me.getNodeByRecord(rec, true))) && node.isVisible(true)) {
                moved += increment;
                lastValid = rec;
            }
        } while (moved !== distance);
        return lastValid;
    },

    getFirstVisibleRowIndex: function() {
        var me = this,
            count = (me.dataSource.buffered ? me.dataSource.getTotalCount() : me.dataSource.getCount()),
            result = me.indexOf(me.all.first()) - 1;

        do {
            result += 1;
            if (result === count) {
                return;
            }
        } while (!Ext.fly(me.getNode(result, true)).isVisible(true));
        return result;
    },

    getLastVisibleRowIndex: function() {
        var me = this,
            result = me.indexOf(me.all.last());

        do {
            result -= 1;
            if (result === -1) {
                return;
            }
        } while (!Ext.fly(me.getNode(result, true)).isVisible(true));
        return result;
    },

    getHeaderCt: function() {
        return this.headerCt;
    },

    getPosition: function(record, header) {
        return new Ext.grid.CellContext(this).setPosition(record, header);
    },

    beforeDestroy: function() {
        var me = this;

        if (me.rendered) {
            me.el.removeAllListeners();
        }
        me.callParent(arguments);
    },

    onDestroy: function() {
        var me = this,
            features = me.featuresMC,
            len,
            i;
        
        if (features) {
            for (i = 0, len = features.getCount(); i < len; ++i) {
                features.getAt(i).destroy();
            }
        }
        me.featuresMC = null;
        this.callParent(arguments);    
    },

    
    onAdd: function(ds, records, index) {
        this.callParent(arguments);
        this.doStripeRows(index);
    },

    
    onRemove: function(ds, records, indexes) {
        this.callParent(arguments);
        this.doStripeRows(indexes[0]);
    },

    
    doStripeRows: function(startRow, endRow) {
        var me = this,
            rows,
            rowsLn,
            i,
            row;

        
        if (me.rendered && me.stripeRows) {
            rows = me.getNodes(startRow, endRow);

            for (i = 0, rowsLn = rows.length; i < rowsLn; i++) {
                row = rows[i];
                
                row.className = row.className.replace(me.rowClsRe, ' ');
                startRow++;
                
                if (startRow % 2 === 0) {
                    row.className += (' ' + me.altRowCls);
                }
            }
        }
    },

    repaintRow: function(rowIdx) {
        var node = this.getNode(rowIdx),
            tds = node.childNodes,
            i = tds.length;

        while (i--) {
            tds[i].className = tds[i].className;
        }
    },

    
    
    
    
    getRowStyleTableEl: function(item ) {
       var me = this;

        if (!item.tagName) {
            item = this.getNode(item);
        }

        return (me.isGrouping ? Ext.fly(item) : this.el).down('table.x-grid-table');
    },

    
    
    
    
    isRowStyleFirst: function(item ) {
        var me = this,
            index;
            
        
        if (item === -1) {
            return false;
        }

        if (!item.tagName) {
            index = item;
            item = this.getNode(item);
        } else {
            index = me.indexOf(item);
        }

        return (!index || me.isGrouping && Ext.fly(item).hasCls(Ext.baseCSSPrefix + 'grid-group-row'));
    },

    getCellPaddingAfter: function(cell) {
        return Ext.fly(cell).getPadding('r');
    }

});


Ext.define('Ext.grid.View', {
    extend:  Ext.view.Table ,
    alias: 'widget.gridview',

    
    stripeRows: true,

    autoScroll: true
});


Ext.define('Ext.grid.Panel', {
    extend:  Ext.panel.Table ,
                                
    alias: ['widget.gridpanel', 'widget.grid'],
    alternateClassName: ['Ext.list.ListView', 'Ext.ListView', 'Ext.grid.GridPanel'],
    viewType: 'gridview',

    lockable: false,

    
    rowLines: true

    
    

    

    

    
});


Ext.define('Ext.grid.plugin.BufferedRendererTableView', {
    override: 'Ext.view.Table',

    
    onAdd: function(store, records, index) {
        var me = this,
            bufferedRenderer = me.bufferedRenderer,
            rows = me.all;

        
        if (me.rendered && bufferedRenderer && (rows.getCount() + records.length) > bufferedRenderer.viewSize) {

            
            if (index < rows.startIndex + bufferedRenderer.viewSize && (index + records.length) > rows.startIndex) {
                me.refreshView();
            }
            
            else {
                bufferedRenderer.stretchView(me, bufferedRenderer.getScrollHeight());
            }
        }
        
        
        
        else {
            me.callParent([store, records, index]);
        }
    },

    onRemove: function(store, records, indices) {
        var me = this,
            bufferedRenderer = me.bufferedRenderer;

        
        me.callParent([store, records, indices]);

        
        
        
        
        if (me.rendered && bufferedRenderer) {
            if (me.dataSource.getCount() > bufferedRenderer.viewSize) {
                me.refreshView();
            }
            
            else {
                bufferedRenderer.stretchView(me, bufferedRenderer.getScrollHeight());
            }
        }
    },

    
    onDataRefresh: function() {
        var me = this;

        if (me.bufferedRenderer) {
            
            me.all.clear();
            me.bufferedRenderer.onStoreClear();
        }
        me.callParent();
    }
});


Ext.define('Ext.grid.RowEditorButtons', {
    extend:  Ext.container.Container ,
    alias: 'widget.roweditorbuttons',

    frame: true,
    shrinkWrap: true,
    position: 'bottom',

    constructor: function(config) {
        var me = this,
            rowEditor = config.rowEditor,
            cssPrefix = Ext.baseCSSPrefix,
            plugin = rowEditor.editingPlugin;

        config = Ext.apply({
            baseCls: cssPrefix + 'grid-row-editor-buttons',
            defaults: {
                xtype: 'button',
                ui: rowEditor.buttonUI,
                scope: plugin,
                flex: 1,
                minWidth: Ext.panel.Panel.prototype.minButtonWidth
            },
            items: [{
                cls: cssPrefix + 'row-editor-update-button',
                itemId: 'update',
                handler: plugin.completeEdit,
                text: rowEditor.saveBtnText,
                disabled: rowEditor.updateButtonDisabled
            }, {
                cls: cssPrefix + 'row-editor-cancel-button',
                handler: plugin.cancelEdit,
                text: rowEditor.cancelBtnText
            }]
        }, config);

        me.callParent([config]);

        me.addClsWithUI(me.position);
    },

    setButtonPosition: function(position) {
        var me = this;

        me.removeClsWithUI(me.position);
        me.position = position;
        me.addClsWithUI(position);
    },

    getFramingInfoCls: function(){
        return this.baseCls + '-' + this.ui + '-' + this.position;
    },

    getFrameInfo: function() {
        var frameInfo = this.callParent();

        
        
        
        frameInfo.top = true;

        return frameInfo;
    }
});










Ext.define('Ext.grid.RowEditor', {
    extend:  Ext.form.Panel ,
    alias: 'widget.roweditor',
               
                          
                           
                          
                                   
      

    
    saveBtnText  : 'Update',
    
    
    cancelBtnText: 'Cancel',
    
    
    errorsText: 'Errors',
    
    
    dirtyText: 'You need to commit or cancel your changes',
    

    lastScrollLeft: 0,
    lastScrollTop: 0,

    border: false,

    buttonUI: 'default',

    
    
    hideMode: 'offsets',

    initComponent: function() {
        var me = this,
            grid = me.editingPlugin.grid,
            Container = Ext.container.Container;

        me.cls = Ext.baseCSSPrefix + 'grid-editor ' + Ext.baseCSSPrefix + 'grid-row-editor';

        me.layout = {
            type: 'hbox',
            align: 'middle'
        };

        me.lockable = grid.lockable;

        
        if (me.lockable) {
            me.items = [
                
                me.lockedColumnContainer = new Container({
                    id: grid.id + '-locked-editor-cells',
                    layout: {
                        type: 'hbox',
                        align: 'middle'
                    },
                    
                    margin: '0 1 0 0'
                }),

                
                me.normalColumnContainer = new Container({
                    flex: 1,
                    id: grid.id + '-normal-editor-cells',
                    layout: {
                        type: 'hbox',
                        align: 'middle'
                    }
                })
            ];
        } else {
            me.lockedColumnContainer = me.normalColumnContainer = me;
        }

        me.callParent(arguments);

        if (me.fields) {
            me.addFieldsForColumn(me.fields, true);
            me.insertColumnEditor(me.fields);
            delete me.fields;
        }

        me.mon(me.hierarchyEventSource, {
            scope: me,
            show: me.repositionIfVisible
        });
        me.getForm().trackResetOnLoad = true;
    },

    
    
    
    
    onGridResize: function() {
        var me = this,
            clientWidth = me.getClientWidth(),
            grid = me.editingPlugin.grid,
            gridBody = grid.body,
            btns = me.getFloatingButtons();

        me.setLocalX(gridBody.getOffsetsTo(grid)[0] + gridBody.getBorderWidth('l') - grid.el.getBorderWidth('l'));
        
        me.setWidth(clientWidth);
        btns.setLocalX((clientWidth - btns.getWidth()) / 2);
    },

    onFieldRender: function(field){
        var me = this,
            column = field.column;

        if (column.isVisible()) {
            me.syncFieldWidth(column);
        } else if (!column.rendered) {
            
            me.view.headerCt.on({
                afterlayout: Ext.Function.bind(me.syncFieldWidth, me, [column]),
                single: true
            });
        }
    },

    syncFieldWidth: function(column) {
        var field = column.getEditor(),
            width;
        field._marginWidth = (field._marginWidth || field.el.getMargin('lr'));
        width = column.getWidth() - field._marginWidth;
        field.setWidth(width);
        if (field.xtype === 'displayfield') {
            
            field.inputWidth = width;
        }
    },

    onFieldChange: function() {
        var me = this,
            form = me.getForm(),
            valid = form.isValid();
        if (me.errorSummary && me.isVisible()) {
            me[valid ? 'hideToolTip' : 'showToolTip']();
        }
        me.updateButton(valid);
        me.isValid = valid;
    },

    updateButton: function(valid){
        var buttons = this.floatingButtons; 
        if (buttons) {
            buttons.child('#update').setDisabled(!valid);
        } else {
            
            this.updateButtonDisabled = !valid;
        }
    },

    afterRender: function() {
        var me = this,
            plugin = me.editingPlugin,
            grid = plugin.grid,
            view = grid.lockable ? grid.normalGrid.view : grid.view,
            field;

        me.callParent(arguments);

        
        me.scrollingView = view;
        me.scrollingViewEl = view.el;
        view.mon(me.scrollingViewEl, 'scroll', me.onViewScroll, me);

        
        me.mon(me.el, {
            click: Ext.emptyFn,
            stopPropagation: true
        });

        
        me.mon(grid, {
            resize: me.onGridResize,
            scope: me
        });

        me.el.swallowEvent([
            'keypress',
            'keydown'
        ]);

        me.fieldScroller = me.normalColumnContainer.layout.innerCt;
        me.fieldScroller.dom.style.overflow = 'hidden';
        me.fieldScroller.on({
            scroll: me.onFieldContainerScroll,
            scope: me
        });

        me.keyNav = new Ext.util.KeyNav(me.el, {
            enter: plugin.completeEdit,
            esc: plugin.onEscKey,
            scope: plugin
        });

        me.mon(plugin.view, {
            beforerefresh: me.onBeforeViewRefresh,
            refresh: me.onViewRefresh,
            itemremove: me.onViewItemRemove,
            scope: me
        });

        
        me.preventReposition = true;
        Ext.Array.each(me.query('[isFormField]'), function(field) {
            if (field.column.isVisible()) {
                me.onColumnShow(field.column);
            }
        }, me);
        delete me.preventReposition;    
    },

    onBeforeViewRefresh: function(view) {
        var me = this,
            viewDom = view.el.dom;

        if (me.el.dom.parentNode === viewDom) {
            viewDom.removeChild(me.el.dom);
        }
    },

    onViewRefresh: function(view) {
        var me = this,
            context = me.context,
            row;

        
        if (context && (row = view.getNode(context.record, true))) {
            context.row = row;
            me.reposition();
            if (me.tooltip && me.tooltip.isVisible()) {
                me.tooltip.setTarget(context.row);
            }
        } else {
            me.editingPlugin.cancelEdit();
        }
    },

    onViewItemRemove: function(record, index) {
        var context = this.context;
        if (context && record === context.record) {
            
            this.editingPlugin.cancelEdit();
        }
    },

    onViewScroll: function() {
        var me = this,
            viewEl = me.editingPlugin.view.el,
            scrollingViewEl = me.scrollingViewEl,
            scrollTop  = scrollingViewEl.dom.scrollTop,
            scrollLeft = scrollingViewEl.getScrollLeft(),
            scrollLeftChanged = scrollLeft !== me.lastScrollLeft,
            scrollTopChanged = scrollTop !== me.lastScrollTop,
            row;

        me.lastScrollTop  = scrollTop;
        me.lastScrollLeft = scrollLeft;
        if (me.isVisible()) {
            row = Ext.getDom(me.context.row.id);

            
            if (row && viewEl.contains(row)) {
                if (scrollTopChanged) {

                    
                    me.context.row = row;
                    me.reposition(null, true);
                    if ((me.tooltip && me.tooltip.isVisible()) || me.hiddenTip) {
                        me.repositionTip();
                    }

                    me.syncEditorClip();
                }
            }
            
            else {
                me.setLocalY(-400);
            }
        }

        
        if (me.rendered && scrollLeftChanged) {
            me.syncFieldsHorizontalScroll();
        }
    },

    
    syncFieldsHorizontalScroll: function() {
        
        this.fieldScroller.setScrollLeft(this.lastScrollLeft);
    },

    
    onFieldContainerScroll: function() {
        this.scrollingViewEl.setScrollLeft(this.fieldScroller.getScrollLeft());
    },

    onColumnResize: function(column, width) {
        var me = this;

        if (me.rendered) {
            
            me.onGridResize();
            me.onViewScroll();
            if (!column.isGroupHeader) {
                me.syncFieldWidth(column);
                me.repositionIfVisible();
            }
        }
    },

    onColumnHide: function(column) {
        if (!column.isGroupHeader) {
            column.getEditor().hide();
            this.repositionIfVisible();
        }
    },

    onColumnShow: function(column) {
        var me = this;

        if (me.rendered && !column.isGroupHeader) {
            column.getEditor().show();
            me.syncFieldWidth(column);
            if (!me.preventReposition) {
                this.repositionIfVisible();
            }
        }
    },

    onColumnMove: function(column, fromIdx, toIdx) {
        var me = this,
            i, incr = 1, len, field, fieldIdx,
            fieldContainer = column.isLocked() ? me.lockedColumnContainer : me.normalColumnContainer;

        
        if (column.isGroupHeader) {
            Ext.suspendLayouts();
            column = column.getGridColumns();

            if (toIdx > fromIdx) {
                toIdx--;
                incr = 0;
            }

            this.addFieldsForColumn(column);
            for (i = 0, len = column.length; i < len; i++, fromIdx += incr, toIdx += incr) {
                field = column[i].getEditor();
                fieldIdx = fieldContainer.items.indexOf(field);

                
                
                if (fieldIdx === -1) {
                    fieldContainer.insert(toIdx, field);
                }

                
                else if (fieldIdx != toIdx) {
                    fieldContainer.move(fromIdx, toIdx);
                }
            }
            Ext.resumeLayouts(true);
        } else {
            if (toIdx > fromIdx) {
                toIdx--;
            }
            this.addFieldsForColumn(column);
            field = column.getEditor();
            fieldIdx = fieldContainer.items.indexOf(field);
            if (fieldIdx === -1) {
                fieldContainer.insert(toIdx, field);
            }
            else if (fieldIdx != toIdx) {
                fieldContainer.move(fromIdx, toIdx);
            }
        }
    },

    onColumnAdd: function(column) {

        
        if (column.isGroupHeader) {
            column = column.getGridColumns();
        }
        
        this.addFieldsForColumn(column);
        this.insertColumnEditor(column);
        this.preventReposition = false;
    },

    insertColumnEditor: function(column) {
        var me = this,
            fieldContainer,
            len, i;

        if (Ext.isArray(column)) {
            for (i = 0, len = column.length; i < len; i++) {
                me.insertColumnEditor(column[i]);
            }
            return;
        }

        if (!column.getEditor) {
            return;
        }

        fieldContainer = column.isLocked() ? me.lockedColumnContainer : me.normalColumnContainer;
        
        
        fieldContainer.insert(column.getVisibleIndex(), column.getEditor());
    },

    onColumnRemove: function(ct, column) {
        column = column.isGroupHeader ? column.getGridColumns() : column;
        this.removeColumnEditor(column);
    },

    removeColumnEditor: function(column) {
        var me = this,
            field,
            len, i;

        if (Ext.isArray(column)) {
            for (i = 0, len = column.length; i < len; i++) {
                me.removeColumnEditor(column[i]);
            }
            return;
        }

        if (column.hasEditor()) {
            field = column.getEditor();
            if (field && field.ownerCt) {
                field.ownerCt.remove(field, false);
            }
        }
    },

    onColumnReplace: function(map, fieldId, column, oldColumn) {
        this.onColumnRemove(oldColumn.ownerCt, oldColumn);
    },

    getFloatingButtons: function() {
        var me = this,
            btns = me.floatingButtons;

        if (!btns) {
            me.floatingButtons = btns = new Ext.grid.RowEditorButtons({
                rowEditor: me
            });
        }
        return btns;
    },

    repositionIfVisible: function(c) {
        var me = this,
            view = me.view;

        
        
        if (c && (c == me || !c.el.isAncestor(view.el))) {
            return;
        }

        if (me.isVisible() && view.isVisible(true)) {
            me.reposition();
        }
    },

    getRefOwner: function() {
        return this.editingPlugin.grid;
    },

    
    
    
    getRefItems: function() {
        var me = this,
            result;

        if (me.lockable) {
            result = me.lockedColumnContainer.getRefItems();
            result.push.apply(result, me.normalColumnContainer.getRefItems());
        } else {
            result = me.callParent();
        }
        result.push.apply(result, me.getFloatingButtons().getRefItems());
        return result;
    },

    reposition: function(animateConfig, fromScrollHandler) {
        var me = this,
            context = me.context,
            row = context && Ext.get(context.row),
            yOffset = 0,
            rowTop,
            localY,
            deltaY,
            afterPosition;

        
        if (row && Ext.isElement(row.dom)) {

            deltaY = me.syncButtonPosition(me.getScrollDelta());

            if (!me.editingPlugin.grid.rowLines) { 
                
                
                
                
                yOffset = -parseInt(row.first().getStyle('border-bottom-width'));
            }
            rowTop = me.calculateLocalRowTop(row);
            localY = me.calculateEditorTop(rowTop) + yOffset;

            
            
            
            
            if (!fromScrollHandler) {
                afterPosition = function() {
                    if (deltaY) {
                        me.scrollingViewEl.scrollBy(0, deltaY, true);
                    }
                    me.focusContextCell();
                }
            }

            me.syncEditorClip();

            
            
            
            if (animateConfig) {
                me.animate(Ext.applyIf({
                    to: {
                        top: localY
                    },
                    duration: animateConfig.duration || 125,
                    callback: afterPosition
                }, animateConfig));
            } else {
                me.setLocalY(localY);
                if (afterPosition) {
                    afterPosition();
                }
            }
        }
    },

    
    getScrollDelta: function() {
        var me = this,
            scrollingViewDom = me.scrollingViewEl.dom,
            context = me.context,
            body = me.body,
            deltaY = 0;

        if (context) {
            deltaY = Ext.fly(context.row).getOffsetsTo(scrollingViewDom)[1] - body.getBorderPadding().beforeY;
            if (deltaY > 0) {
                deltaY = Math.max(deltaY + me.getHeight() + me.floatingButtons.getHeight() -
                    scrollingViewDom.clientHeight - body.getBorderWidth('b'), 0);
            }
        }
        return deltaY;
    },

    
    
    
    
    calculateLocalRowTop: function(row) {
        var grid = this.editingPlugin.grid;
        return Ext.fly(row).getOffsetsTo(grid)[1] - grid.el.getBorderWidth('t') + this.lastScrollTop;
    },

    
    
    
    calculateEditorTop: function(rowTop) {
        return rowTop - this.body.getBorderPadding().beforeY - this.lastScrollTop;
    },

    getClientWidth: function() {
        var me = this,
            grid = me.editingPlugin.grid,
            result;

        if (me.lockable) {
            result =
               grid.lockedGrid.getWidth() +
               grid.normalGrid.view.el.dom.clientWidth - 1;
        }
        else {
            result = grid.view.el.dom.clientWidth;
        }
        return result;
    },

    getEditor: function(fieldInfo) {
        var me = this;

        if (Ext.isNumber(fieldInfo)) {
            
            
            
            return me.query('[isFormField]')[fieldInfo];
        } else if (fieldInfo.isHeader && !fieldInfo.isGroupHeader) {
            return fieldInfo.getEditor();
        }
    },

    addFieldsForColumn: function(column, initial) {
        var me = this,
            i,
            length, field;

        if (Ext.isArray(column)) {
            for (i = 0, length = column.length; i < length; i++) {
                me.addFieldsForColumn(column[i], initial);
            }
            return;
        }

        if (column.getEditor) {

            
            field = column.getEditor(null, {
                xtype: 'displayfield',
                
                
                getModelData: function() {
                    return null;
                }
            });
            if (column.align === 'right') {
                field.fieldStyle = 'text-align:right';
            }

            if (column.xtype === 'actioncolumn') {
                field.fieldCls += ' ' + Ext.baseCSSPrefix + 'form-action-col-field'
            }

            if (me.isVisible() && me.context) {
                if (field.is('displayfield')) {
                    me.renderColumnData(field, me.context.record, column);
                } else {
                    field.suspendEvents();
                    field.setValue(me.context.record.get(column.dataIndex));
                    field.resumeEvents();
                }
            }
            if (column.hidden) {
                me.onColumnHide(column);
            } else if (column.rendered && !initial) {
                
                me.onColumnShow(column);
            }
        }
    },

    loadRecord: function(record) {
        var me     = this,
            form   = me.getForm(),
            fields = form.getFields(),
            items  = fields.items,
            length = items.length,
            i, displayFields,
            isValid;

        
        for (i = 0; i < length; i++) {
            items[i].suspendEvents();
        }

        form.loadRecord(record);

        for (i = 0; i < length; i++) {
            items[i].resumeEvents();
        }

        isValid = form.isValid();
        if (me.errorSummary) {
            if (isValid) {
                me.hideToolTip();
            } else {
                me.showToolTip();
            }
        }

        me.updateButton(isValid);

        
        displayFields = me.query('>displayfield');
        length = displayFields.length;

        for (i = 0; i < length; i++) {
            me.renderColumnData(displayFields[i], record);
        }
    },

    renderColumnData: function(field, record, activeColumn) {
        var me = this,
            grid = me.editingPlugin.grid,
            headerCt = grid.headerCt,
            view = me.scrollingView,
            store = view.dataSource,
            column = activeColumn || field.column,
            value = record.get(column.dataIndex),
            renderer = column.editRenderer || column.renderer,
            metaData,
            rowIdx,
            colIdx;

        
        if (renderer) {
            metaData = { tdCls: '', style: '' };
            rowIdx = store.indexOf(record);
            colIdx = headerCt.getHeaderIndex(column);

            value = renderer.call(
                column.scope || headerCt.ownerCt,
                value,
                metaData,
                record,
                rowIdx,
                colIdx,
                store,
                view
            );
        }

        field.setRawValue(value);
        field.resetOriginalValue();
    },

    beforeEdit: function() {
        var me = this,
            scrollDelta;

        if (me.isVisible() && me.errorSummary && !me.autoCancel && me.isDirty()) {

            
            scrollDelta = me.getScrollDelta();
            if (scrollDelta) {
                me.scrollingViewEl.scrollBy(0, scrollDelta, true)
            }
            me.showToolTip();
            return false;
        }
    },

    
    startEdit: function(record, columnHeader) {
        var me = this,
            editingPlugin = me.editingPlugin,
            grid = editingPlugin.grid,
            context = me.context = editingPlugin.context;

        if (!me.rendered) {
            me.width = me.getClientWidth();
            me.render(grid.el, grid.el.dom.firstChild);
            me.getFloatingButtons().render(me.el);
            
            me.onViewScroll();
        } else {
            me.syncFieldsHorizontalScroll();
        }

        if (me.isVisible()) {
            me.reposition(true);
        } else {
            me.show();
        }

        
        me.onGridResize();

        
        context.grid.getSelectionModel().select(record);

        
        me.loadRecord(record);
    },

    
    
    
    
    syncButtonPosition: function(scrollDelta) {
        var me = this,
            floatingButtons = me.getFloatingButtons(),
            scrollingViewElDom = me.scrollingViewEl.dom,
            overflow = this.getScrollDelta() - (scrollingViewElDom.scrollHeight -
                scrollingViewElDom.scrollTop - scrollingViewElDom.clientHeight);

        if (overflow > 0) {
            if (!me._buttonsOnTop) {
                floatingButtons.setButtonPosition('top');
                me._buttonsOnTop = true;
            }
            scrollDelta = 0;
        } else if (me._buttonsOnTop) {
            floatingButtons.setButtonPosition('bottom');
            me._buttonsOnTop = false;
        }

        return scrollDelta;
    },

    
    
    syncEditorClip: function() {
        var me = this,
            overflow = me.getScrollDelta(),
            btnHeight;

        if (overflow) {
            
            me.isOverflowing = true;
            btnHeight = me.floatingButtons.getHeight();

            if (overflow > 0) {
                
                me.clipBottom(Math.max(me.getHeight() - overflow + btnHeight, -btnHeight));
            } else if (overflow < 0) {
                
                overflow = Math.abs(overflow);
                me.clipTop(Math.max(overflow, 0));
            }
        } else if (me.isOverflowing) {
            me.clearClip();
            me.isOverflowing = false;
        }
    },

    
    focusContextCell: function() {
        var field = this.getEditor(this.context.column);
        if (field && field.focus) {
            field.focus();
        }
    },

    cancelEdit: function() {
        var me     = this,
            form   = me.getForm(),
            fields = form.getFields(),
            items  = fields.items,
            length = items.length,
            i;

        me.hide();
        form.clearInvalid();

        
        for (i = 0; i < length; i++) {
            items[i].suspendEvents();
        }

        form.reset();

        for (i = 0; i < length; i++) {
            items[i].resumeEvents();
        }
    },

    completeEdit: function() {
        var me = this,
            form = me.getForm();

        if (!form.isValid()) {
            return false;
        }

        form.updateRecord(me.context.record);
        me.hide();
        return true;
    },

    onShow: function() {
        var me = this;

        me.callParent(arguments);
        me.reposition();
    },

    onHide: function() {
        var me = this;

        me.callParent(arguments);
        if (me.tooltip) {
            me.hideToolTip();
        }
        if (me.context) {
            me.context.view.focusRow(me.context.record);
            me.context = null;
        }
    },

    isDirty: function() {
        var me = this,
            form = me.getForm();
        return form.isDirty();
    },

    getToolTip: function() {
        return this.tooltip || (this.tooltip = new Ext.tip.ToolTip({
            cls: Ext.baseCSSPrefix + 'grid-row-editor-errors',
            title: this.errorsText,
            autoHide: false,
            closable: true,
            closeAction: 'disable',
            anchor: 'left',
            anchorToTarget: false
        }));
    },

    hideToolTip: function() {
        var me = this,
            tip = me.getToolTip();
        if (tip.rendered) {
            tip.disable();
        }
        me.hiddenTip = false;
    },

    showToolTip: function() {
        var me = this,
            tip = me.getToolTip();

        tip.showAt([0, 0]);
        tip.update(me.getErrors());
        me.repositionTip();
        tip.enable();
    },

    repositionTip: function() {
        var me = this,
            tip = me.getToolTip(),
            context = me.context,
            row = Ext.get(context.row),
            viewEl = me.scrollingViewEl,
            viewHeight = viewEl.dom.clientHeight,
            viewTop = me.lastScrollTop,
            viewBottom = viewTop + viewHeight,
            rowHeight = row.getHeight(),
            rowTop = row.getOffsetsTo(me.context.view.body)[1],
            rowBottom = rowTop + rowHeight;

        if (rowBottom > viewTop && rowTop < viewBottom) {
            tip.showAt(tip.getAlignToXY(viewEl, 'tl-tr', [15, row.getOffsetsTo(viewEl)[1]]));
            me.hiddenTip = false;
        } else {
            tip.hide();
            me.hiddenTip = true;
        }
    },

    getErrors: function() {
        var me        = this,
            errors    = [],
            fields    = me.query('>[isFormField]'),
            length    = fields.length,
            i;

        for (i = 0; i < length; i++) {
            errors = errors.concat(
                Ext.Array.map(fields[i].getErrors(), me.createErrorListItem)
            );
        }

        
        if (!errors.length && !me.autoCancel && me.isDirty()) {
            errors[0] = me.createErrorListItem(me.dirtyText);
        }

        return '<ul class="' + Ext.plainListCls + '">' + errors.join('') + '</ul>';
    },

    createErrorListItem: function(e) {
        return '<li class="' + Ext.baseCSSPrefix + 'grid-row-editor-errors-item">' + e + '</li>';
    },

    beforeDestroy: function(){
        Ext.destroy(this.floatingButtons, this.tooltip);
        this.callParent();    
    },

    clipBottom: function(value) {
        this.el.setStyle('clip', 'rect(-1000px auto ' + value + 'px auto)');
    },

    clipTop: function(value) {
        this.el.setStyle('clip', 'rect(' + value + 'px auto 1000px auto)');
    },

    clearClip: function(el) {
        this.el.setStyle(
            'clip',
            Ext.isIE8m || Ext.isIEQuirks ? 'rect(-1000px auto 1000px auto)' : 'auto'
        );
    }
});



Ext.define('Ext.view.DropZone', {
    extend:  Ext.dd.DropZone ,

    indicatorHtml: '<div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-left"></div><div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-right"></div>',
    indicatorCls: Ext.baseCSSPrefix + 'grid-drop-indicator',

    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);

        
        
        
        
        
        if (!me.ddGroup) {
            me.ddGroup = 'view-dd-zone-' + me.view.id;
        }

        
        
        
        me.callParent([me.view.el]);
    },



    fireViewEvent: function() {
        var me = this,
            result;

        me.lock();
        result = me.view.fireEvent.apply(me.view, arguments);
        me.unlock();
        return result;
    },

    getTargetFromEvent : function(e) {
        var node = e.getTarget(this.view.getItemSelector()),
            mouseY, nodeList, testNode, i, len, box;



        if (!node) {
            mouseY = e.getPageY();
            for (i = 0, nodeList = this.view.getNodes(), len = nodeList.length; i < len; i++) {
                testNode = nodeList[i];
                box = Ext.fly(testNode).getBox();
                if (mouseY <= box.bottom) {
                    return testNode;
                }
            }
        }
        return node;
    },

    getIndicator: function() {
        var me = this;

        if (!me.indicator) {
            me.indicator = new Ext.Component({
                html: me.indicatorHtml,
                cls: me.indicatorCls,
                ownerCt: me.view,
                floating: true,
                shadow: false
            });
        }
        return me.indicator;
    },

    getPosition: function(e, node) {
        var y      = e.getXY()[1],
            region = Ext.fly(node).getRegion(),
            pos;

        if ((region.bottom - y) >= (region.bottom - region.top) / 2) {
            pos = "before";
        } else {
            pos = "after";
        }
        return pos;
    },

    
    containsRecordAtOffset: function(records, record, offset) {
        if (!record) {
            return false;
        }
        var view = this.view,
            recordIndex = view.indexOf(record),
            nodeBefore = view.getNode(recordIndex + offset, true),
            recordBefore = nodeBefore ? view.getRecord(nodeBefore) : null;

        return recordBefore && Ext.Array.contains(records, recordBefore);
    },

    positionIndicator: function(node, data, e) {
        var me = this,
            view = me.view,
            pos = me.getPosition(e, node),
            overRecord = view.getRecord(node),
            draggingRecords = data.records,
            indicatorY;

        if (!Ext.Array.contains(draggingRecords, overRecord) && (
            pos == 'before' && !me.containsRecordAtOffset(draggingRecords, overRecord, -1) ||
            pos == 'after' && !me.containsRecordAtOffset(draggingRecords, overRecord, 1)
        )) {
            me.valid = true;

            if (me.overRecord != overRecord || me.currentPosition != pos) {

                indicatorY = Ext.fly(node).getY() - view.el.getY() - 1;
                if (pos == 'after') {
                    indicatorY += Ext.fly(node).getHeight();
                }
                me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, indicatorY);

                
                me.overRecord = overRecord;
                me.currentPosition = pos;
            }
        } else {
            me.invalidateDrop();
        }
    },

    invalidateDrop: function() {
        if (this.valid) {
            this.valid = false;
            this.getIndicator().hide();
        }
    },

    
    onNodeOver: function(node, dragZone, e, data) {
        var me = this;

        if (!Ext.Array.contains(data.records, me.view.getRecord(node))) {
            me.positionIndicator(node, data, e);
        }
        return me.valid ? me.dropAllowed : me.dropNotAllowed;
    },

    
    
    notifyOut: function(node, dragZone, e, data) {
        var me = this;

        me.callParent(arguments);
        me.overRecord = me.currentPosition = null
        me.valid = false;
        if (me.indicator) {
            me.indicator.hide();
        }
    },

    
    onContainerOver : function(dd, e, data) {
        var me = this,
            view = me.view,
            count = view.dataSource.getCount();

        
        if (count) {
            me.positionIndicator(view.all.last(), data, e);
        }

        
        else {
            me.overRecord = me.currentPosition = null;
            me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, 0);
            me.valid = true;
        }
        return me.dropAllowed;
    },

    onContainerDrop : function(dd, e, data) {
        return this.onNodeDrop(dd, null, e, data);
    },

    onNodeDrop: function(targetNode, dragZone, e, data) {
        var me = this,
            dropHandled = false,
 
            
            
            
            
            
            dropHandlers = {
                wait: false,
                processDrop: function () {
                    me.invalidateDrop();
                    me.handleNodeDrop(data, me.overRecord, me.currentPosition);
                    dropHandled = true;
                    me.fireViewEvent('drop', targetNode, data, me.overRecord, me.currentPosition);
                },
 
                cancelDrop: function() {
                    me.invalidateDrop();
                    dropHandled = true;
                }
            },
            performOperation = false;
 
        if (me.valid) {
            performOperation = me.fireViewEvent('beforedrop', targetNode, data, me.overRecord, me.currentPosition, dropHandlers);
            if (dropHandlers.wait) {
                return;
            }
 
            if (performOperation !== false) {
                
                if (!dropHandled) {
                    dropHandlers.processDrop();
                }
            }
        }
        return performOperation;
    },
    
    destroy: function(){
        Ext.destroy(this.indicator);
        delete this.indicator;
        this.callParent();
    }
});


Ext.define('Ext.grid.ViewDropZone', {
    extend:  Ext.view.DropZone ,

    indicatorHtml: '<div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-left"></div><div class="' + Ext.baseCSSPrefix + 'grid-drop-indicator-right"></div>',
    indicatorCls: Ext.baseCSSPrefix + 'grid-drop-indicator',

    handleNodeDrop : function(data, record, position) {
        var view = this.view,
            store = view.getStore(),
            index, records, i, len;

        
        if (data.copy) {
            records = data.records;
            data.records = [];
            for (i = 0, len = records.length; i < len; i++) {
                data.records.push(records[i].copy());
            }
        } else {
            
            data.view.store.remove(data.records, data.view === view);
        }

        if (record && position) {
            index = store.indexOf(record);

            
            if (position !== 'before') {
                index++;
            }
            store.insert(index, data.records);
        }
        
        else {
            store.add(data.records);
        }

        view.getSelectionModel().select(data.records);
    }
});


Ext.define('Ext.grid.plugin.HeaderResizer', {
    extend:  Ext.AbstractPlugin ,
                                                        
    alias: 'plugin.gridheaderresizer',

    disabled: false,

    config: {
        
        dynamic: false
    },

    colHeaderCls: Ext.baseCSSPrefix + 'column-header',

    minColWidth: 40,
    maxColWidth: 1000,
    wResizeCursor: 'col-resize',
    eResizeCursor: 'col-resize',
    
    
    
    

    init: function(headerCt) {
        this.headerCt = headerCt;
        headerCt.on('render', this.afterHeaderRender, this, {single: true});
    },

    
    destroy: function() {
        if (this.tracker) {
            this.tracker.destroy();
        }
    },

    afterHeaderRender: function() {
        var headerCt = this.headerCt,
            el = headerCt.el;

        headerCt.mon(el, 'mousemove', this.onHeaderCtMouseMove, this);

        this.tracker = new Ext.dd.DragTracker({
            disabled: this.disabled,
            onBeforeStart: Ext.Function.bind(this.onBeforeStart, this),
            onStart: Ext.Function.bind(this.onStart, this),
            onDrag: Ext.Function.bind(this.onDrag, this),
            onEnd: Ext.Function.bind(this.onEnd, this),
            tolerance: 3,
            autoStart: 300,
            el: el
        });
    },

    
    
    
    onHeaderCtMouseMove: function(e, t) {
        var me = this,
            prevSiblings,
            headerEl, overHeader, resizeHeader, resizeHeaderOwnerGrid, ownerGrid;

        if (me.headerCt.dragging) {
            if (me.activeHd) {
                me.activeHd.el.dom.style.cursor = '';
                delete me.activeHd;
            }
        } else {
            headerEl = e.getTarget('.' + me.colHeaderCls, 3, true);

            if (headerEl){
                overHeader = Ext.getCmp(headerEl.id);

                
                if (overHeader.isOnLeftEdge(e)) {
                    resizeHeader = overHeader.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])')
                    
                    if (resizeHeader) {

                        ownerGrid = me.headerCt.up('tablepanel');
                        resizeHeaderOwnerGrid = resizeHeader.up('tablepanel');

                        
                        
                        
                        if (!((resizeHeaderOwnerGrid === ownerGrid) || ((ownerGrid.ownerCt.isXType('tablepanel')) && ownerGrid.ownerCt.view.lockedGrid === resizeHeaderOwnerGrid))) {
                            resizeHeader = null;
                        }
                    }
                }
                
                else if (overHeader.isOnRightEdge(e)) {
                    resizeHeader = overHeader;
                }
                
                else {
                    resizeHeader = null;
                }

                
                if (resizeHeader) {
                    
                    
                    
                    if (resizeHeader.isGroupHeader) {
                        prevSiblings = resizeHeader.getGridColumns();
                        resizeHeader = prevSiblings[prevSiblings.length - 1];
                    }

                    
                    
                    if (resizeHeader && !(resizeHeader.fixed || (resizeHeader.resizable === false) || me.disabled)) {
                        me.activeHd = resizeHeader;
                        overHeader.el.dom.style.cursor = me.eResizeCursor;
                        if (overHeader.triggerEl) {
                            overHeader.triggerEl.dom.style.cursor = me.eResizeCursor;
                        }
                    }
                
                } else {
                    overHeader.el.dom.style.cursor = '';
                    if (overHeader.triggerEl) {
                        overHeader.triggerEl.dom.style.cursor = '';
                    }
                    me.activeHd = null;
                }
            }
        }
    },

    
    onBeforeStart : function(e) {
        
        this.dragHd = this.activeHd;

        if (!!this.dragHd && !this.headerCt.dragging) {
            this.tracker.constrainTo = this.getConstrainRegion();
            return true;
        } else {
            this.headerCt.dragging = false;
            return false;
        }
    },

    
    getConstrainRegion: function() {
        var me       = this,
            dragHdEl = me.dragHd.el,
            rightAdjust = 0,
            nextHd,
            lockedGrid;

        
        
        if (me.headerCt.forceFit) {
            nextHd = me.dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])');
            if (nextHd) {
                if (!me.headerInSameGrid(nextHd)) {
                    nextHd = null;
                }
                rightAdjust = nextHd.getWidth() - me.minColWidth;
            }
        }

        
        else if ((lockedGrid = me.dragHd.up('tablepanel')).isLocked) {
            rightAdjust = me.dragHd.up('[scrollerOwner]').getWidth() - lockedGrid.getWidth() - 30;
        }

        
        else {
            rightAdjust = me.maxColWidth - dragHdEl.getWidth();
        }

        return me.adjustConstrainRegion(
            dragHdEl.getRegion(),
            0,
            rightAdjust,
            0,
            me.minColWidth
        );
    },

    
    
    onStart: function(e){
        var me       = this,
            dragHd   = me.dragHd,
            width    = dragHd.el.getWidth(),
            headerCt = dragHd.getOwnerHeaderCt(),
            x, y, gridSection, markerOwner, lhsMarker, rhsMarker, markerHeight;

        me.headerCt.dragging = true;
        me.origWidth = width;

        
        if (!me.dynamic) {
            gridSection  = markerOwner = headerCt.up('tablepanel');
            if (gridSection.ownerLockable) {
                markerOwner = gridSection.ownerLockable;
            }
            x            = me.getLeftMarkerX(markerOwner);
            lhsMarker    = markerOwner.getLhsMarker();
            rhsMarker    = markerOwner.getRhsMarker();
            markerHeight = gridSection.body.getHeight() + headerCt.getHeight();
            y            = headerCt.getOffsetsTo(markerOwner)[1];

            lhsMarker.setLocalY(y);
            rhsMarker.setLocalY(y);
            lhsMarker.setHeight(markerHeight);
            rhsMarker.setHeight(markerHeight);
            me.setMarkerX(lhsMarker, x);
            me.setMarkerX(rhsMarker, x + width);
        }
    },

    
    onDrag: function(e){
        var me = this,
            markerOwner;
            
        if (me.dynamic) {
            me.doResize();
        } else {
            markerOwner = this.headerCt.up('tablepanel');
            if (markerOwner.ownerLockable) {
                markerOwner = markerOwner.ownerLockable;
            }
            this.setMarkerX(this.getMovingMarker(markerOwner), this.calculateDragX(markerOwner));
        }
    },
    
    getMovingMarker: function(markerOwner){
        return markerOwner.getRhsMarker();
    },

    onEnd: function(e) {
        this.headerCt.dragging = false;
        if (this.dragHd) {
            if (!this.dynamic) {
                var markerOwner = this.headerCt.up('tablepanel');

                
                if (markerOwner.ownerLockable) {
                    markerOwner = markerOwner.ownerLockable;
                }
                this.setMarkerX(markerOwner.getLhsMarker(), -9999);
                this.setMarkerX(markerOwner.getRhsMarker(), -9999);
            }
            this.doResize();
        }
    },

    doResize: function() {
        var me = this,
            dragHd = me.dragHd,
            nextHd,
            offset;
            
        if (dragHd) {
            offset = me.tracker.getOffset('point');

            
            if (dragHd.flex) {
                delete dragHd.flex;
            }

            Ext.suspendLayouts();

            
            me.adjustColumnWidth(offset[0]);
 
            
            
            if (me.headerCt.forceFit) {
                nextHd = dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])');
                if (nextHd && !me.headerInSameGrid(nextHd)) {
                    nextHd = null;
                }
                if (nextHd) {
                    delete nextHd.flex;
                    nextHd.setWidth(nextHd.getWidth() - offset[0]);
                }
            }

            
            Ext.resumeLayouts(true);
        }
    },
    
    
    headerInSameGrid: function(header) {
        var grid = this.dragHd.up('tablepanel');
        
        return !!header.up(grid);
    },

    disable: function() {
        this.disabled = true;
        if (this.tracker) {
            this.tracker.disable();
        }
    },

    enable: function() {
        this.disabled = false;
        if (this.tracker) {
            this.tracker.enable();
        }
    },

    calculateDragX: function(markerOwner) {
        return this.tracker.getXY('point')[0] - markerOwner.getX() - markerOwner.el.getBorderWidth('l');
    },

    getLeftMarkerX: function(markerOwner) {
        return this.dragHd.getX() - markerOwner.getX() - markerOwner.el.getBorderWidth('l') - 1;
    },

    setMarkerX: function(marker, x) {
        marker.setLocalX(x);
    },

    adjustConstrainRegion: function(region, t, r, b, l) {
        return region.adjust(t, r, b, l);
    },

    adjustColumnWidth: function(offsetX) {
        this.dragHd.setWidth(this.origWidth + offsetX);
    }
});


Ext.define('Ext.grid.header.DragZone', {
    extend:  Ext.dd.DragZone ,
    colHeaderSelector: '.' + Ext.baseCSSPrefix + 'column-header',
    colInnerSelector: '.' + Ext.baseCSSPrefix + 'column-header-inner',
    maxProxyWidth: 120,

    constructor: function(headerCt) {
        this.headerCt = headerCt;
        this.ddGroup =  this.getDDGroup();
        this.callParent([headerCt.el]);
        this.proxy.el.addCls(Ext.baseCSSPrefix + 'grid-col-dd');
    },
    
    getDDGroup: function() {
        return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id;
    },

    getDragData: function(e) {
        if (e.getTarget(this.colInnerSelector)) {
            var header = e.getTarget(this.colHeaderSelector),
                headerCmp,
                ddel;

            if (header) {
                headerCmp = Ext.getCmp(header.id);
                if (!this.headerCt.dragging && headerCmp.draggable && !(headerCmp.isOnLeftEdge(e) || headerCmp.isOnRightEdge(e))) {
                    ddel = document.createElement('div');
                    ddel.innerHTML = Ext.getCmp(header.id).text;
                    return {
                        ddel: ddel,
                        header: headerCmp
                    };
                }
            }
        }
        return false;
    },

    onBeforeDrag: function() {
        return !(this.headerCt.dragging || this.disabled);
    },

    onInitDrag: function() {
        this.headerCt.dragging = true;
        this.callParent(arguments);
    },

    onDragDrop: function() {
        this.headerCt.dragging = false;
        this.callParent(arguments);
    },

    afterRepair: function() {
        this.callParent();
        this.headerCt.dragging = false;
    },

    getRepairXY: function() {
        return this.dragData.header.el.getXY();
    },
    
    disable: function() {
        this.disabled = true;
    },
    
    enable: function() {
        this.disabled = false;
    }
});


Ext.define('Ext.grid.header.DropZone', {
    extend:  Ext.dd.DropZone ,
    colHeaderCls: Ext.baseCSSPrefix + 'column-header',
    proxyOffsets: [-4, -9],

    constructor: function(headerCt){
        this.headerCt = headerCt;
        this.ddGroup = this.getDDGroup();
        this.callParent([headerCt.el]);
    },

    getDDGroup: function() {
        return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id;
    },

    getTargetFromEvent : function(e){
        return e.getTarget('.' + this.colHeaderCls);
    },

    getTopIndicator: function() {
        if (!this.topIndicator) {
            this.self.prototype.topIndicator = Ext.DomHelper.append(Ext.getBody(), {
                cls: "col-move-top",
                html: "&#160;"
            }, true);
            this.self.prototype.indicatorXOffset = Math.floor((this.topIndicator.dom.offsetWidth + 1) / 2);
        }
        return this.topIndicator;
    },

    getBottomIndicator: function() {
        if (!this.bottomIndicator) {
            this.self.prototype.bottomIndicator = Ext.DomHelper.append(Ext.getBody(), {
                cls: "col-move-bottom",
                html: "&#160;"
            }, true);
        }
        return this.bottomIndicator;
    },

    getLocation: function(e, t) {
        var x      = e.getXY()[0],
            region = Ext.fly(t).getRegion(),
            pos;

        if ((region.right - x) <= (region.right - region.left) / 2) {
            pos = "after";
        } else {
            pos = "before";
        }
        return {
            pos: pos,
            header: Ext.getCmp(t.id),
            node: t
        };
    },

    positionIndicator: function(data, node, e){
        var me = this,
            dragHeader   = data.header,
            dropLocation = me.getLocation(e, node),
            targetHeader = dropLocation.header,
            pos          = dropLocation.pos,
            nextHd,
            prevHd,
            topIndicator, bottomIndicator, topAnchor, bottomAnchor,
            topXY, bottomXY, headerCtEl, minX, maxX,
            allDropZones, ln, i, dropZone;

        
        if (targetHeader === me.lastTargetHeader && pos === me.lastDropPos) {
            return;
        }
        nextHd       = dragHeader.nextSibling('gridcolumn:not([hidden])');
        prevHd       = dragHeader.previousSibling('gridcolumn:not([hidden])');
        me.lastTargetHeader = targetHeader;
        me.lastDropPos = pos;

        
        if (!targetHeader.draggable && pos === 'before' && targetHeader.getIndex() === 0) {
            return false;
        }

        data.dropLocation = dropLocation;

        if ((dragHeader !== targetHeader) &&
            ((pos === "before" && nextHd !== targetHeader) ||
            (pos === "after" && prevHd !== targetHeader)) &&
            !targetHeader.isDescendantOf(dragHeader)) {

            
            
            
            allDropZones = Ext.dd.DragDropManager.getRelated(me);
            ln = allDropZones.length;
            i  = 0;

            for (; i < ln; i++) {
                dropZone = allDropZones[i];
                if (dropZone !== me && dropZone.invalidateDrop) {
                    dropZone.invalidateDrop();
                }
            }

            me.valid = true;
            topIndicator = me.getTopIndicator();
            bottomIndicator = me.getBottomIndicator();
            if (pos === 'before') {
                topAnchor = 'bc-tl';
                bottomAnchor = 'tc-bl';
            } else {
                topAnchor = 'bc-tr';
                bottomAnchor = 'tc-br';
            }
            
            
            topXY = topIndicator.getAlignToXY(targetHeader.el, topAnchor);
            bottomXY = bottomIndicator.getAlignToXY(targetHeader.el, bottomAnchor);

            
            headerCtEl = me.headerCt.el;
            minX = headerCtEl.getX() - me.indicatorXOffset;
            maxX = headerCtEl.getX() + headerCtEl.getWidth();

            topXY[0] = Ext.Number.constrain(topXY[0], minX, maxX);
            bottomXY[0] = Ext.Number.constrain(bottomXY[0], minX, maxX);

            
            topIndicator.setXY(topXY);
            bottomIndicator.setXY(bottomXY);
            topIndicator.show();
            bottomIndicator.show();

        
        } else {
            me.invalidateDrop();
        }
    },

    invalidateDrop: function() {
        this.valid = false;
        this.hideIndicators();
    },

    onNodeOver: function(node, dragZone, e, data) {
        var me = this,
            from = data.header,
            doPosition,
            to,
            fromPanel,
            toPanel;

        if (data.header.el.dom === node) {
            doPosition = false;
        } else {
            data.isLock = data.isUnlock = false;
            to = me.getLocation(e, node).header;
            
            
            doPosition = (from.ownerCt === to.ownerCt);

            
            if (!doPosition && (!from.ownerCt.sealed && !to.ownerCt.sealed)) {

                doPosition = true;
                fromPanel = from.up('tablepanel');
                toPanel = to.up('tablepanel');

                
                data.isLock   = toPanel.isLocked && !fromPanel.isLocked;
                data.isUnlock = !toPanel.isLocked && fromPanel.isLocked;
                if ((data.isUnlock && from.lockable === false) || (data.isLock && !from.isLockable())) {
                    doPosition = false;
                }
            }
        }

        if (doPosition) {
            me.positionIndicator(data, node, e);
        } else {
            me.valid = false;
        }
        return me.valid ? me.dropAllowed : me.dropNotAllowed;
    },

    hideIndicators: function() {
        var me = this;
        
        me.getTopIndicator().hide();
        me.getBottomIndicator().hide();
        me.lastTargetHeader = me.lastDropPos = null;

    },

    onNodeOut: function() {
        this.hideIndicators();
    },

    onNodeDrop: function(node, dragZone, e, data) {
        if (this.valid) {
            var dragHeader   = data.header,
                dropLocation = data.dropLocation,
                targetHeader = dropLocation.header,
                fromCt       = dragHeader.ownerCt,
                localFromIdx = fromCt.items.indexOf(dragHeader), 
                toCt         = targetHeader.ownerCt,
                localToIdx   = toCt.items.indexOf(targetHeader),
                headerCt     = this.headerCt,
                columnManager= headerCt.columnManager,
                fromIdx      = columnManager.getHeaderIndex(dragHeader),
                toIdx        = columnManager.getHeaderIndex(targetHeader),
                colsToMove   = dragHeader.isGroupHeader ? dragHeader.query(':not([isGroupHeader])').length : 1,
                sameCt       = fromCt === toCt,
                scrollerOwner, savedWidth;

            
            if (dropLocation.pos === 'after') {
                localToIdx++;
                toIdx += targetHeader.isGroupHeader ? targetHeader.query(':not([isGroupHeader])').length : 1;
            }

            
            
            
            if (data.isLock) {
                scrollerOwner = fromCt.up('[scrollerOwner]');
                scrollerOwner.lock(dragHeader, localToIdx);
                data.isLock = false;

                
                this.onNodeDrop(node, dragZone, e, data);
            } else if (data.isUnlock) {
                scrollerOwner = fromCt.up('[scrollerOwner]');
                scrollerOwner.unlock(dragHeader, localToIdx);
                data.isUnlock = false;

                
                this.onNodeDrop(node, dragZone, e, data);
            }
            
            
            else {
                this.invalidateDrop();
                
                savedWidth = dragHeader.getWidth();
                
                
                if (sameCt) {
                    
                    
                    
                    if (localToIdx === localFromIdx) {
                        
                        headerCt.onHeaderMoved(dragHeader, colsToMove, fromIdx, toIdx);
                        return;
                    }
                    
                    if (localToIdx > localFromIdx) {
                        localToIdx -= 1;
                    }
                }

                
                Ext.suspendLayouts();

                if (sameCt) {
                    toCt.move(localFromIdx, localToIdx);
                } else {
                    fromCt.remove(dragHeader, false);
                    toCt.insert(localToIdx, dragHeader);
                }

                
                
                
                if (toCt.isGroupHeader) {
                    
                    if (!sameCt) {
                        dragHeader.savedFlex = dragHeader.flex;
                        delete dragHeader.flex;
                        dragHeader.width = savedWidth;
                    }
                } else {
                    if (dragHeader.savedFlex) {
                        dragHeader.flex = dragHeader.savedFlex;
                        delete dragHeader.width;
                    }
                }

                
                headerCt.purgeCache();
                Ext.resumeLayouts(true);
                headerCt.onHeaderMoved(dragHeader, colsToMove, fromIdx, toIdx);

                
            }
        }
    }
});


Ext.define('Ext.grid.plugin.HeaderReorderer', {
    extend:  Ext.AbstractPlugin ,
                                                                       
    alias: 'plugin.gridheaderreorderer',

    init: function(headerCt) {
        this.headerCt = headerCt;
        headerCt.on({
            render: this.onHeaderCtRender,
            single: true,
            scope: this
        });
    },

    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },

    onHeaderCtRender: function() {
        var me = this;
        
        me.dragZone = new Ext.grid.header.DragZone(me.headerCt);
        me.dropZone = new Ext.grid.header.DropZone(me.headerCt);
        if (me.disabled) {
            me.dragZone.disable();
        }
    },
    
    enable: function() {
        this.disabled = false;
        if (this.dragZone) {
            this.dragZone.enable();
        }
    },
    
    disable: function() {
        this.disabled = true;
        if (this.dragZone) {
            this.dragZone.disable();
        }
    }
});


Ext.define('Ext.grid.header.Container', {
    extend:  Ext.container.Container ,
               
                                
                                        
                                         
      
           
                                 
                                 
                        
                             
                            
      
    border: true,

    alias: 'widget.headercontainer',

    baseCls: Ext.baseCSSPrefix + 'grid-header-ct',
    dock: 'top',

    
    weight: 100,

    defaultType: 'gridcolumn',

    detachOnRemove: false,

    
    defaultWidth: 100,

    

    
    sortAscText: 'Sort Ascending',
    
    
    sortDescText: 'Sort Descending',
    
    
    sortClearText: 'Clear Sort',
    
    
    columnsText: 'Columns',
    

    headerOpenCls: Ext.baseCSSPrefix + 'column-header-open',

    menuSortAscCls: Ext.baseCSSPrefix + 'hmenu-sort-asc',

    menuSortDescCls: Ext.baseCSSPrefix + 'hmenu-sort-desc',

    menuColsIcon: Ext.baseCSSPrefix + 'cols-icon',

    
    triStateSort: false,

    ddLock: false,

    dragging: false,

    

    
    sortable: true,

    
    enableColumnHide: true,

    initComponent: function() {
        var me = this;

        me.headerCounter = 0;
        me.plugins = me.plugins || [];

        
        

        
        
        if (!me.isColumn) {
            if (me.enableColumnResize) {
                me.resizer = new Ext.grid.plugin.HeaderResizer();
                me.plugins.push(me.resizer);
            }
            if (me.enableColumnMove) {
                me.reorderer = new Ext.grid.plugin.HeaderReorderer();
                me.plugins.push(me.reorderer);
            }
        }

        
        
        if (me.isColumn && (!me.items || me.items.length === 0)) {
            me.isContainer = false;
            me.layout = {
                type: 'container',
                calculate: Ext.emptyFn
            };
        }

        
        else {
            me.layout = Ext.apply({
                type: 'gridcolumn',
                align: 'stretch'
            }, me.initialConfig.layout);
        
            
            if (me.isRootHeader) {
                me.grid.columnManager = me.columnManager = new Ext.grid.ColumnManager(me);
            }
        }

        me.defaults = me.defaults || {};
        Ext.applyIf(me.defaults, {
            triStateSort: me.triStateSort,
            sortable: me.sortable
        });

        me.menuTask = new Ext.util.DelayedTask(me.updateMenuDisabledState, me);
        me.callParent();
        me.addEvents(
            
            'columnresize',

            
            'headerclick',

            
            'headercontextmenu',

            
            'headertriggerclick',

            
            'columnmove',
            
            'columnhide',
            
            'columnshow',
            
            'columnschanged',
            
            'sortchange',
            
            'menucreate'
        );
    },

    isLayoutRoot: function(){
        
        
        
        
        
        if (this.hiddenHeaders) {
            return false;
        }
        return this.callParent();
    },
    
    
    getOwnerHeaderCt: function() {
        var me = this;
        return me.isRootHeader ? me : me.up('[isRootHeader]');
    },

    onDestroy: function() {
        var me = this;

        if (me.menu) {
            me.menu.un('hide', me.onMenuHide, me);
        }
        me.menuTask.cancel();
        Ext.destroy(me.resizer, me.reorderer);
        me.callParent();
    },

    applyColumnsState: function(columns) {
        if (!columns || !columns.length) {
            return;
        }

        var me     = this,
            items  = me.items.items,
            count  = items.length,
            i      = 0,
            length = columns.length,
            c, col, columnState, index;

        for (c = 0; c < length; c++) {
            columnState = columns[c];

            for (index = count; index--; ) {
                col = items[index];
                if (col.getStateId && col.getStateId() == columnState.id) {
                    
                    
                    
                    
                    if (i !== index) {
                        me.moveHeader(index, i);
                    }

                    if (col.applyColumnState) {
                        col.applyColumnState(columnState);
                    }
                    ++i;
                    break;
                }
            }
        }
    },

    getColumnsState: function () {
        var me = this,
            columns = [],
            state;

        me.items.each(function (col) {
            state = col.getColumnState && col.getColumnState();
            if (state) {
                columns.push(state);
            }
        });

        return columns;
    },

    
    
    
    onAdd: function(c) {
        var me = this;

        if (!c.headerId) {
            c.headerId = c.initialConfig.id || Ext.id(null, 'header-');
        }

        
        if (!c.getStateId()) {
            
            
            
            
            c.stateId = c.initialConfig.id || ('h' + (++me.headerCounter));
        }

        me.callParent(arguments);
        me.onColumnsChanged();
    },

    onMove: function() {
        this.callParent(arguments);
        this.onColumnsChanged();
    },

    onShow: function() {
        this.callParent(arguments);
        this.onColumnsChanged();
    },

    
    
    
    onColumnsChanged: function() {
        var headerCt = this;

        
        while (headerCt) {
            headerCt.purgeCache();
            if (headerCt.isRootHeader) {
                break;
            }
            headerCt = headerCt.ownerCt;
        }

        if (headerCt && headerCt.rendered) {
            headerCt.fireEvent('columnschanged', headerCt);
        }
    },

    
    
    
    onRemove: function(c) {
        var me = this,
            ownerCt = me.ownerCt;

        me.callParent(arguments);


        if (!me.destroying) {
            me.onColumnsChanged();
            if (me.isGroupHeader && !me.items.getCount() && ownerCt) {
                
                
                
                me.detachComponent(c);
                
                
                Ext.suspendLayouts();
                ownerCt.remove(me);
                Ext.resumeLayouts(true);
            }
        }
    },

    
    applyDefaults: function(config) {
        var ret;
        
        if (config && !config.isComponent && config.xtype == 'rownumberer') {
            ret = config;
        } else {
            ret = this.callParent(arguments);

            
            if (!config.isGroupHeader && !('width' in ret) && !ret.flex) {
                ret.width = this.defaultWidth;
            }
        }
        return ret;
    },

    setSortState: function(){
        var store   = this.up('[store]').store,
            
            
            first = store.getFirstSorter(),
            hd;

        if (first) {
            hd = this.down('gridcolumn[dataIndex=' + first.property  +']');
            if (hd) {
                hd.setSortState(first.direction, false, true);
            }
        } else {
            this.clearOtherSortStates(null);
        }
    },

    getHeaderMenu: function(){
        var menu = this.getMenu(),
            item;

        if (menu) {
            item = menu.child('#columnItem');
            if (item) {
                return item.menu;
            }
        }
        return null;
    },

    onHeaderVisibilityChange: function(header, visible){
        var me = this,
            menu = me.getHeaderMenu(),
            item;

        
        me.purgeCache();

        if (menu) {
            
            item = me.getMenuItemForHeader(menu, header);
            if (item) {
                item.setChecked(visible, true);
            }
            
            if (menu.isVisible()) {
                me.menuTask.delay(50);
            }
        }
    },

    updateMenuDisabledState: function(menu) {
        var me = this,
            columns = me.query(':not([hidden])'),
            i,
            len = columns.length,
            item,
            checkItem,
            method;

        
        if (!menu) {
            menu = me.getMenu();
        }

        for (i = 0; i < len; ++i) {
            item = columns[i];
            checkItem = me.getMenuItemForHeader(menu, item);
            if (checkItem) {
                method = item.isHideable() ? 'enable' : 'disable';
                if (checkItem.menu) {
                    method += 'CheckChange';
                }
                checkItem[method]();
            }
        }
    },

    getMenuItemForHeader: function(menu, header) {
        return header ? menu.down('menucheckitem[headerId=' + header.id + ']') : null;
    },

    onHeaderShow: function(header) {
        
        var me = this,
            gridSection = me.ownerCt;

        if (me.forceFit) {
            delete me.flex;

        }

        me.onHeaderVisibilityChange(header, true);

        
        
        if (!header.isGroupHeader) {
            if (gridSection) {
                gridSection.onHeaderShow(me, header);
            }
        }
        me.fireEvent('columnshow', me, header);
        me.fireEvent('columnschanged', this);
    },

    onHeaderHide: function(header) {
        
        var me = this,
            gridSection = me.ownerCt;

        me.onHeaderVisibilityChange(header, false);

        
        if (!header.isGroupHeader) {
            if (gridSection) {
                gridSection.onHeaderHide(me, header);
            }
        }
        me.fireEvent('columnhide', me, header);
        me.fireEvent('columnschanged', this);
    },

    
    tempLock: function() {
        this.ddLock = true;
        Ext.Function.defer(function() {
            this.ddLock = false;
        }, 200, this);
    },

    onHeaderResize: function(header, w, suppressFocus) {
        var me = this,
            view = me.view,
            gridSection = me.ownerCt;

        
        if (view && view.body.dom) {
            me.tempLock();
            if (gridSection) {
                gridSection.onHeaderResize(me, header, w);
            }
        }
        me.fireEvent('columnresize', this, header, w);
    },

    onHeaderClick: function(header, e, t) {
        header.fireEvent('headerclick', this, header, e, t);
        this.fireEvent('headerclick', this, header, e, t);
    },

    onHeaderContextMenu: function(header, e, t) {
        header.fireEvent('headercontextmenu', this, header, e, t);
        this.fireEvent('headercontextmenu', this, header, e, t);
    },

    onHeaderTriggerClick: function(header, e, t) {
        
        var me = this;
        if (header.fireEvent('headertriggerclick', me, header, e, t) !== false && me.fireEvent('headertriggerclick', me, header, e, t) !== false) {
            me.showMenuBy(t, header);
        }
    },

    showMenuBy: function(t, header) {
        var menu = this.getMenu(),
            ascItem  = menu.down('#ascItem'),
            descItem = menu.down('#descItem'),
            sortableMth;

        
        
        menu.activeHeader = menu.ownerButton = header;
        header.setMenuActive(true);

        
        sortableMth = header.sortable ? 'enable' : 'disable';
        if (ascItem) {
            ascItem[sortableMth]();
        }
        if (descItem) {
            descItem[sortableMth]();
        }
        menu.showBy(t);
    },

    
    onMenuHide: function(menu) {
        menu.activeHeader.setMenuActive(false);
    },

    moveHeader: function(fromIdx, toIdx) {
        
        this.tempLock();
        this.onHeaderMoved(this.move(fromIdx, toIdx), 1, fromIdx, toIdx);
    },

    purgeCache: function() {
        var me = this,
            menu = me.menu;
            
        
        me.gridDataColumns = me.hideableColumns = null;

        
        if (me.columnManager) {
            me.columnManager.invalidate();
        }

        
        
        if (menu && menu.hidden) {
            
            menu.hide();
            menu.destroy();
            me.menu = null;
        }
    },

    onHeaderMoved: function(header, colsToMove, fromIdx, toIdx) {
        var me = this,
            gridSection = me.ownerCt;

        if (gridSection && gridSection.onHeaderMove) {
            gridSection.onHeaderMove(me, header, colsToMove, fromIdx, toIdx);
        }
        me.fireEvent("columnmove", me, header, fromIdx, toIdx);
    },

    
    getMenu: function() {
        var me = this;

        if (!me.menu) {
            me.menu = new Ext.menu.Menu({
                hideOnParentHide: false,  
                items: me.getMenuItems(),
                listeners: {
                    hide: me.onMenuHide,
                    scope: me
                }
            });
            me.fireEvent('menucreate', me, me.menu);
        }
        me.updateMenuDisabledState(me.menu);
        return me.menu;
    },

    
    getMenuItems: function() {
        var me = this,
            menuItems = [],
            hideableColumns = me.enableColumnHide ? me.getColumnMenu(me) : null;

        if (me.sortable) {
            menuItems = [{
                itemId: 'ascItem',
                text: me.sortAscText,
                cls: me.menuSortAscCls,
                handler: me.onSortAscClick,
                scope: me
            },{
                itemId: 'descItem',
                text: me.sortDescText,
                cls: me.menuSortDescCls,
                handler: me.onSortDescClick,
                scope: me
            }];
        }
        if (hideableColumns && hideableColumns.length) {
            if (me.sortable) {
                menuItems.push('-');
            }
            menuItems.push({
                itemId: 'columnItem',
                text: me.columnsText,
                cls: me.menuColsIcon,
                menu: hideableColumns,
                hideOnClick: false
            });
        }
        return menuItems;
    },

    
    onSortAscClick: function() {
        var menu = this.getMenu(),
            activeHeader = menu.activeHeader;

        activeHeader.setSortState('ASC');
    },

    
    onSortDescClick: function() {
        var menu = this.getMenu(),
            activeHeader = menu.activeHeader;

        activeHeader.setSortState('DESC');
    },

    
    getColumnMenu: function(headerContainer) {
        var menuItems = [],
            i = 0,
            item,
            items = headerContainer.query('>gridcolumn[hideable]'),
            itemsLn = items.length,
            menuItem;

        for (; i < itemsLn; i++) {
            item = items[i];
            menuItem = new Ext.menu.CheckItem({
                text: item.menuText || item.text,
                checked: !item.hidden,
                hideOnClick: false,
                headerId: item.id,
                menu: item.isGroupHeader ? this.getColumnMenu(item) : undefined,
                checkHandler: this.onColumnCheckChange,
                scope: this
            });
            menuItems.push(menuItem);

            
            
            item.on({
                destroy: Ext.Function.bind(menuItem.destroy, menuItem)
            });
        }
        return menuItems;
    },

    onColumnCheckChange: function(checkItem, checked) {
        var header = Ext.getCmp(checkItem.headerId);
        header[checked ? 'show' : 'hide']();
    },

    
    getColumnCount: function() {
        return this.getGridColumns().length;
    },

    
    getFullWidth: function() {
        var fullWidth = 0,
            headers = this.getVisibleGridColumns(),
            headersLn = headers.length,
            i = 0,
            header;
           

        for (; i < headersLn; i++) {
            header = headers[i];
            
            if (header.getDesiredWidth) {
                fullWidth += header.getDesiredWidth() || 0;
            
            } else {
                fullWidth += header.getWidth();
            }
        }
        return fullWidth;
    },

    
    clearOtherSortStates: function(activeHeader) {
        var headers   = this.getGridColumns(),
            headersLn = headers.length,
            i         = 0;

        for (; i < headersLn; i++) {
            if (headers[i] !== activeHeader) {
                
                headers[i].setSortState(null, true);
            }
        }
    },

    
    getVisibleGridColumns: function() {
        var allColumns = this.getGridColumns(),
            result = [],
            len = allColumns.length, i;

        
        
        for (i = 0; i < len; i++) {
            if (!allColumns[i].hidden) {
                result[result.length] = allColumns[i];
            }
        }
        return result;
    }, 

    
    getGridColumns: function(inResult, hiddenAncestor) {
        if (!inResult && this.gridDataColumns) {
            return this.gridDataColumns;
        }

        var me = this,
            result = inResult || [],
            items, i, len, item,
            lastVisibleColumn;

        hiddenAncestor = hiddenAncestor || me.hidden;
        if (me.items) {
            items = me.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                item = items[i];
                if (item.isGroupHeader) {
                    item.getGridColumns(result, hiddenAncestor);
                } else {
                    item.hiddenAncestor = hiddenAncestor;
                    result.push(item);
                }
            }
        }
        if (!inResult) {
            me.gridDataColumns = result;
        }

        
        if (!inResult && len) {
            
            for (i = 0, len = result.length; i < len; i++) {
                item = result[i];
                item.isFirstVisible = item.isLastVisible = false;
                if (!(item.hidden || item.hiddenAncestor)) {
                    if (!lastVisibleColumn) {
                        item.isFirstVisible = true;
                    }
                    lastVisibleColumn = item;
                }
            }
            
            if (lastVisibleColumn) {
                lastVisibleColumn.isLastVisible = true;
            }
        }

        return result;
    },

    
    getHideableColumns: function() {
        var me = this,
            result = me.hideableColumns;

        if (!result) {
            result = me.hideableColumns = me.query('[hideable]');
        }
        return result;
    },

    
    getHeaderIndex: function(header) {
        return this.columnManager.getHeaderIndex(header);
    },

    
    getHeaderAtIndex: function(index) {
        return this.columnManager.getHeaderAtIndex(index);
    },

    
    getVisibleHeaderClosestToIndex: function(index) {
        return this.columnManager.getVisibleHeaderClosestToIndex(index);
    },

    autoSizeColumn : function(header) {
        var view = this.view;
        if (view) {
            view.autoSizeColumn(header);
        }
    }
});


Ext.define('Ext.grid.column.Column', {
    extend:  Ext.grid.header.Container ,
    alias: 'widget.gridcolumn',
                                                                                             
    alternateClassName: 'Ext.grid.Column',

    baseCls: Ext.baseCSSPrefix + 'column-header',

    
    hoverCls: Ext.baseCSSPrefix + 'column-header-over',

    handleWidth: 4,

    sortState: null,

    possibleSortStates: ['ASC', 'DESC'],

    childEls: [
        'titleEl', 'triggerEl', 'textEl'
    ],
    
    
    noWrap: true,

    renderTpl:
        '<div id="{id}-titleEl" {tipMarkup}class="' + Ext.baseCSSPrefix + 'column-header-inner">' +
            '<span id="{id}-textEl" class="' + Ext.baseCSSPrefix + 'column-header-text' +
                '{childElCls}">' +
                '{text}' +
            '</span>' +
            '<tpl if="!menuDisabled">'+
                '<div id="{id}-triggerEl" class="' + Ext.baseCSSPrefix + 'column-header-trigger' +
                '{childElCls}"></div>' +
            '</tpl>' +
        '</div>' +
        '{%this.renderContainer(out,values)%}',

    

    

    
    dataIndex: null,

    
    text: '&#160;',

    

    
    menuText: null,

    
    emptyCellText: '&#160;',

    
    sortable: true,

    

    

    

    

    
    resizable: true,

    
    hideable: true,

    
    menuDisabled: false,

    
    renderer: false,

    

    

    
    editRenderer: false,

    
    align: 'left',

    
    draggable: true,

    

    
    tooltipType: 'qtip',

    
    
    initDraggable: Ext.emptyFn,

    
    tdCls: '',

    

    

    

    

    
    isHeader: true,

    
    isColumn: true,

    ascSortCls: Ext.baseCSSPrefix + 'column-header-sort-ASC',
    descSortCls: Ext.baseCSSPrefix + 'column-header-sort-DESC',

    componentLayout: 'columncomponent',

    groupSubHeaderCls: Ext.baseCSSPrefix + 'group-sub-header',

    groupHeaderCls: Ext.baseCSSPrefix + 'group-header',

    clickTargetName: 'titleEl',

    
    detachOnRemove : true,

    
    initResizable: Ext.emptyFn,

    initComponent: function() {
        var me = this,
            renderer,
            listeners;

        if (me.header != null) {
            me.text = me.header;
            me.header = null;
        }

        if (!me.triStateSort) {
            me.possibleSortStates.length = 2;
        }

        
        if (me.columns != null) {
            me.isGroupHeader = true;


            
            me.items = me.columns;
            me.columns = me.flex = me.width = null;
            me.cls = (me.cls||'') + ' ' + me.groupHeaderCls;

            
            me.sortable = me.resizable = false;
            me.align = 'center';
        } else {
            
            
            
            if (me.flex) {
                me.minWidth = me.minWidth || Ext.grid.plugin.HeaderResizer.prototype.minColWidth;
            }
        }
        me.addCls(Ext.baseCSSPrefix + 'column-header-align-' + me.align);

        renderer = me.renderer;
        if (renderer) {
            
            
            if (typeof renderer == 'string') {
                me.renderer = Ext.util.Format[renderer];
            }
            me.hasCustomRenderer = true;
        } else if (me.defaultRenderer) {
            me.scope = me;
            me.renderer = me.defaultRenderer;
        }

        
        me.callParent(arguments);

        listeners = {
            element:        me.clickTargetName,
            click:          me.onTitleElClick,
            contextmenu:    me.onTitleElContextMenu,
            mouseenter:     me.onTitleMouseOver,
            mouseleave:     me.onTitleMouseOut,
            scope:          me
        };
        if (me.resizable) {
            listeners.dblclick = me.onTitleElDblClick;
        }
        me.on(listeners);
    },

    onAdd: function(child) {
        if (child.isColumn) {
            child.isSubHeader = true;
            child.addCls(this.groupSubHeaderCls);
        }
        if (this.hidden) {
            child.hide();
        }
        this.callParent(arguments);
    },

    onRemove: function(child) {
        if (child.isSubHeader) {
            child.isSubHeader = false;
            child.removeCls(this.groupSubHeaderCls);
        }
        this.callParent(arguments);
    },

    initRenderData: function() {
        var me = this,
            tipMarkup = '',
            tip = me.tooltip,
            attr = me.tooltipType == 'qtip' ? 'data-qtip' : 'title';

        if (!Ext.isEmpty(tip)) {
            tipMarkup = attr + '="' + tip + '" ';
        }

        return Ext.applyIf(me.callParent(arguments), {
            text: me.text,
            menuDisabled: me.menuDisabled,
            tipMarkup: tipMarkup
        });
    },

    applyColumnState: function (state) {
        var me = this;
 
        
        me.applyColumnsState(state.columns);

        
        
        if (state.hidden != null) {
            me.hidden = state.hidden;
        }
        if (state.locked != null) {
            me.locked = state.locked;
        }
        if (state.sortable != null) {
            me.sortable = state.sortable;
        }
        if (state.width != null) {
            me.flex = null;
            me.width = state.width;
        } else if (state.flex != null) {
            me.width = null;
            me.flex = state.flex;
        }
    },

    getColumnState: function () {
        var me = this,
            items = me.items.items,
            
            iLen = items ? items.length : 0,
            i,
            columns = [],
            state = {
                id: me.getStateId()
            };

        me.savePropsToState(['hidden', 'sortable', 'locked', 'flex', 'width'], state);
 
        if (me.isGroupHeader) {
            for (i = 0; i < iLen; i++) {
                columns.push(items[i].getColumnState());
            }

            if (columns.length) {
                state.columns = columns;
            }
        } else if (me.isSubHeader && me.ownerCt.hidden) {
            
            delete me.hidden;
        }

        if ('width' in state) {
            delete state.flex; 
        }
        return state;
    },

    getStateId: function () {
        return this.stateId || this.headerId;
    },

    
    setText: function(text) {
        this.text = text;
        if (this.rendered) {
            this.textEl.update(text);
        }
    },

    
    getIndex: function() {
        return this.isGroupColumn ? false : this.getOwnerHeaderCt().getHeaderIndex(this);
    },

    
    getVisibleIndex: function() {
        return this.isGroupColumn ? false : Ext.Array.indexOf(this.getOwnerHeaderCt().getVisibleGridColumns(), this);
    },

    beforeRender: function() {
        var me = this,
            grid = me.up('tablepanel');

        me.callParent();

        
        
        if (grid && (!me.sortable || grid.sortableColumns === false) && !me.groupable &&
                     !me.lockable && (grid.enableColumnHide === false ||
                     !me.getOwnerHeaderCt().getHideableColumns().length)) {
            me.menuDisabled = true;
        }

        me.protoEl.unselectable();
    },

    afterRender: function() {
        var me = this,
            triggerEl = me.triggerEl,
            triggerWidth;

        me.callParent(arguments);

        
        
        if (!Ext.isIE8 || !Ext.isStrict) {
            me.mon(me.getFocusEl(), {
                focus: me.onTitleMouseOver,
                blur: me.onTitleMouseOut,
                scope: me
            });
        }
        
        if (triggerEl && me.self.triggerElWidth === undefined) {
            triggerEl.setStyle('display', 'block');
            me.self.triggerElWidth = triggerEl.getWidth();
            triggerEl.setStyle('display', '');
        }

        me.keyNav = new Ext.util.KeyNav(me.el, {
            enter: me.onEnterKey,
            down: me.onDownKey,
            scope: me
        });
    },

    
    
    afterComponentLayout: function(width, height, oldWidth, oldHeight) {
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt();

        me.callParent(arguments);

        if (ownerHeaderCt && (oldWidth != null || me.flex) && width !== oldWidth) {
            ownerHeaderCt.onHeaderResize(me, width, true);
        }
    },

    onDestroy: function() {
        var me = this;
        
        Ext.destroy(me.textEl, me.keyNav, me.field);
        me.keyNav = null;
        me.callParent(arguments);
    },

    onTitleMouseOver: function() {
        this.titleEl.addCls(this.hoverCls);
    },

    onTitleMouseOut: function() {
        this.titleEl.removeCls(this.hoverCls);
    },

    onDownKey: function(e) {
        if (this.triggerEl) {
            this.onTitleElClick(e, this.triggerEl.dom || this.el.dom);
        }
    },

    onEnterKey: function(e) {
        this.onTitleElClick(e, this.el.dom);
    },

    
    onTitleElDblClick: function(e, t) {
        var me = this,
            prev,
            leafColumns;

        
        if (me.isOnLeftEdge(e)) {

            
            
            prev = me.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])');

            
            if (prev && prev.getOwnerHeaderCt() === me.getOwnerHeaderCt()) {
                prev.autoSize();
            }
        }
        
        else if (me.isOnRightEdge(e)) {

            
            if (me.isGroupHeader && e.getPoint().isContainedBy(me.layout.innerCt)) {
                leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])');
                this.getOwnerHeaderCt().autoSizeColumn(leafColumns[leafColumns.length - 1]);
                return;
            }
            me.autoSize();
        }
    },

    
    autoSize: function() {
        var me = this,
            leafColumns,
            numLeaves, i,
            headerCt;

        
        if (me.isGroupHeader) {
            leafColumns = me.query('gridcolumn:not([hidden]):not([isGroupHeader])');
            numLeaves = leafColumns.length;
            headerCt = this.getOwnerHeaderCt();
            Ext.suspendLayouts();
            for (i = 0; i < numLeaves; i++) {
                headerCt.autoSizeColumn(leafColumns[i]);
            }
            Ext.resumeLayouts(true);
            return;
        }
        this.getOwnerHeaderCt().autoSizeColumn(this);
    },

    onTitleElClick: function(e, t) {

        
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt();

        if (ownerHeaderCt && !ownerHeaderCt.ddLock) {
            
            
            if (me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl.dom || e.within(me.triggerEl))) {
                ownerHeaderCt.onHeaderTriggerClick(me, e, t);
            
            } else if (e.getKey() || (!me.isOnLeftEdge(e) && !me.isOnRightEdge(e))) {
                me.toggleSortState();
                ownerHeaderCt.onHeaderClick(me, e, t);
            }
        }
    },

    onTitleElContextMenu: function(e, t) {
        
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt();

        if (ownerHeaderCt && !ownerHeaderCt.ddLock) {
            ownerHeaderCt.onHeaderContextMenu(me, e, t);
        }
    },

    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
        return this.fireEvent.apply(this, arguments);
    },

    toggleSortState: function() {
        var me = this,
            idx,
            nextIdx;

        if (me.sortable) {
            idx = Ext.Array.indexOf(me.possibleSortStates, me.sortState);

            nextIdx = (idx + 1) % me.possibleSortStates.length;
            me.setSortState(me.possibleSortStates[nextIdx]);
        }
    },

    doSort: function(state) {
        var tablePanel = this.up('tablepanel'),
            store = tablePanel.store;

        
        
        
        if (tablePanel.ownerLockable && store.isNodeStore) {
            store = tablePanel.ownerLockable.lockedGrid.store;
        }
        store.sort({
            property: this.getSortParam(),
            direction: state
        });
    },

    
    getSortParam: function() {
        return this.dataIndex;
    },

    setSortState: function(state, skipClear, initial) {
        var me = this,
            ascCls = me.ascSortCls,
            descCls = me.descSortCls,
            ownerHeaderCt = me.getOwnerHeaderCt(),
            oldSortState = me.sortState;

         state = state || null;

        if (!me.sorting && oldSortState !== state && (me.getSortParam() != null)) {
            
            if (state && !initial) {
                
                
                me.sorting = true;
                me.doSort(state);
                me.sorting = false;
            }
            switch (state) {
                case 'DESC':
                    me.addCls(descCls);
                    me.removeCls(ascCls);
                    break;
                case 'ASC':
                    me.addCls(ascCls);
                    me.removeCls(descCls);
                    break;
                default:
                    me.removeCls([ascCls, descCls]);
            }
            if (ownerHeaderCt && !me.triStateSort && !skipClear) {
                ownerHeaderCt.clearOtherSortStates(me);
            }
            me.sortState = state;
            
            if (me.triStateSort || state != null) {
                ownerHeaderCt.fireEvent('sortchange', ownerHeaderCt, me, state);
            }
        }
    },

    
    isHideable: function() {
        var result = {
                hideCandidate: this,
                result: this.hideable
            };

        if (result.result) {
            this.ownerCt.bubble(this.hasOtherMenuEnabledChildren, null, [result]);
        }
        return result.result;
    },

    
    
    hasOtherMenuEnabledChildren: function(result) {
        var visibleChildren,
            count;

        
        
        if (!this.isXType('headercontainer')) {
            result.result = false;
            return false;
        }
        
        
        
        
        visibleChildren = this.query('>:not([hidden]):not([menuDisabled])');
        count = visibleChildren.length;
        if (Ext.Array.contains(visibleChildren, result.hideCandidate)) {
            count--;
        }
        if (count) {
            return false;
        }
        
        result.hideCandidate = this;
    },

    
    isLockable: function() {
        var result = {
                result: this.lockable !== false
            };

        if (result.result) {
            this.ownerCt.bubble(this.hasMultipleVisibleChildren, null, [result]);
        }
        return result.result;
    },

    
    isLocked: function() {
        return this.locked || !!this.up('[isColumn][locked]', '[isRootHeader]');
    },

    
    
    hasMultipleVisibleChildren: function(result) {
        
        if (!this.isXType('headercontainer')) {
            result.result = false;
            return false;
        }
        
        if (this.query('>:not([hidden])').length > 1) {
            return false;
        }
    },

    hide: function(fromOwner) {
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt(),
            owner = me.ownerCt,
            ownerIsGroup,
            item, items, len, i;
            
        if (!me.isVisible()) {
            
            return me;
        }

        
        
        if (!ownerHeaderCt) {
            me.callParent();
            return me;
        }

        
        
        
        if (ownerHeaderCt.forceFit) {
            me.visibleSiblingCount = ownerHeaderCt.getVisibleGridColumns().length - 1;
            if (me.flex) {
                me.savedWidth = me.getWidth();
                me.flex = null;
            }
        }

        ownerIsGroup = owner.isGroupHeader;

        
        if (ownerIsGroup && !fromOwner) {
            items = owner.query('>:not([hidden])');
            
            if (items.length === 1 && items[0] == me) {
                me.ownerCt.hide();
                return;
            }
        }

        Ext.suspendLayouts();

        if (me.isGroupHeader) {
            items = me.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                item = items[i];
                if (!item.hidden) {
                    item.hide(true);
                }
            }
        }

        me.callParent();
        
        ownerHeaderCt.onHeaderHide(me);

        Ext.resumeLayouts(true);
        return me;
    },

    show: function(fromOwner, fromChild) {
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt(),
            ownerCt = me.ownerCt,
            items,
            len, i,
            item,
            myWidth,
            availFlex,
            totalFlex,
            oldLen,
            defaultWidth = Ext.grid.header.Container.prototype.defaultWidth;
            
        if (me.isVisible()) {
            return me;
        }

        if (!me.rendered) {
            me.hidden = false;
            return;
        }

        availFlex = ownerHeaderCt.el.getViewSize().width - (ownerHeaderCt.view.el.dom.scrollHeight > ownerHeaderCt.view.el.dom.clientHeight ? Ext.getScrollbarSize().width : 0);

        
        if (ownerHeaderCt.forceFit) {
            
            items = Ext.ComponentQuery.query(':not([flex])', ownerHeaderCt.getVisibleGridColumns());

            
            
            
            
            if (items.length) {
                me.width = me.savedWidth || me.width || defaultWidth;
            }
            
            else {
                items = ownerHeaderCt.getVisibleGridColumns();
                len = items.length;
                oldLen = me.visibleSiblingCount;

                
                
                
                
                myWidth = (me.savedWidth || me.width || defaultWidth);

                
                
                
                
                
                
                
                
                myWidth = Math.min(myWidth * (oldLen / len), defaultWidth,
                    Math.max(availFlex - (len * defaultWidth), defaultWidth));

                me.width = null;
                me.flex = myWidth;
                availFlex -= myWidth;
                totalFlex = 0;
                for (i = 0; i < len; i++) {
                    item = items[i];
                    item.flex = (item.width || item.getWidth());
                    totalFlex += item.flex;
                    item.width = null;
                }

                
                
                
                for (i = 0; i < len; i++) {
                    item = items[i];
                    item.flex = item.flex / totalFlex * availFlex;
                }
            }
        }

        Ext.suspendLayouts();

        
        if (me.isSubHeader && ownerCt.hidden) {
            ownerCt.show(false, true);
        }

        me.callParent(arguments);

        
        if (me.isGroupHeader && fromChild !== true && !me.query(':not([hidden])').length) {
            items = me.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                item = items[i];
                if (item.hidden) {
                    item.show(true);
                }
            }
        }

        Ext.resumeLayouts(true);

        
        ownerCt = me.getOwnerHeaderCt();
        if (ownerCt) {
            ownerCt.onHeaderShow(me);
        }
    },

    getDesiredWidth: function() {
        var me = this;
        if (me.rendered && me.componentLayout && me.componentLayout.lastComponentSize) {
            
            
            
            
            
            
            return me.componentLayout.lastComponentSize.width;
        
        
        
        }
        else if (me.flex) {
            
            return me.width;
        }
        else {
            return me.width;
        }
    },

    getCellSelector: function() {
        return '.' + Ext.baseCSSPrefix + 'grid-cell-' + this.getItemId();
    },

    getCellInnerSelector: function() {
        return this.getCellSelector() + ' .' + Ext.baseCSSPrefix + 'grid-cell-inner';
    },

    isOnLeftEdge: function(e) {
        return (e.getXY()[0] - this.getX() <= this.handleWidth);
    },

    isOnRightEdge: function(e) {
        return (this.getX() + this.getWidth() - e.getXY()[0] <= this.handleWidth);
    },

    
    
    setMenuActive: function(isMenuOpen) {
        this.titleEl[isMenuOpen ? 'addCls' : 'removeCls'](this.headerOpenCls);
    }

    
    

    
    
});


Ext.define('Ext.grid.column.Action', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.actioncolumn'],
    alternateClassName: 'Ext.grid.ActionColumn',

    
    
    
    
    
    
    
    
    
    
    
    

    actionIdRe: new RegExp(Ext.baseCSSPrefix + 'action-col-(\\d+)'),

    
    altText: '',

    
    menuText: '<i>Actions</i>',

    sortable: false,

    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-action-col',

    constructor: function(config) {
        var me = this,
            cfg = Ext.apply({}, config),
            
            items = cfg.items || me.items || [me],
            hasGetClass,
            i,
            len;


        me.origRenderer = cfg.renderer || me.renderer;
        me.origScope = cfg.scope || me.scope;
        
        me.renderer = me.scope = cfg.renderer = cfg.scope = null;
        
        
        cfg.items = null;
        me.callParent([cfg]);

        
        me.items = items;
        
        for (i = 0, len = items.length; i < len; ++i) {
            if (items[i].getClass) {
                hasGetClass = true;
                break;
            }
        }
        
        
        if (me.origRenderer || hasGetClass) {
            me.hasCustomRenderer = true;
        }
    },
    
    
    
    defaultRenderer: function(v, meta, record, rowIdx, colIdx, store, view){
        var me = this,
            prefix = Ext.baseCSSPrefix,
            scope = me.origScope || me,
            items = me.items,
            len = items.length,
            i = 0,
            item, ret, disabled, tooltip;
 
        
        
        
        ret = Ext.isFunction(me.origRenderer) ? me.origRenderer.apply(scope, arguments) || '' : '';

        meta.tdCls += ' ' + Ext.baseCSSPrefix + 'action-col-cell';
        for (; i < len; i++) {
            item = items[i];

            disabled = item.disabled || (item.isDisabled ? item.isDisabled.call(item.scope || scope, view, rowIdx, colIdx, item, record) : false);
            tooltip = disabled ? null : (item.tooltip || (item.getTip ? item.getTip.apply(item.scope || scope, arguments) : null));

            
            if (!item.hasActionConfiguration) {

                
                item.stopSelection = me.stopSelection;
                item.disable = Ext.Function.bind(me.disableAction, me, [i], 0);
                item.enable = Ext.Function.bind(me.enableAction, me, [i], 0);
                item.hasActionConfiguration = true;
            }

            ret += '<img role="button" alt="' + (item.altText || me.altText) + '" src="' + (item.icon || Ext.BLANK_IMAGE_URL) +
                '" class="' + prefix + 'action-col-icon ' + prefix + 'action-col-' + String(i) + ' ' + (disabled ? prefix + 'item-disabled' : ' ') +
                ' ' + (Ext.isFunction(item.getClass) ? item.getClass.apply(item.scope || scope, arguments) : (item.iconCls || me.iconCls || '')) + '"' +
                (tooltip ? ' data-qtip="' + tooltip + '"' : '') + ' />';
        }
        return ret;    
    },

    
    enableAction: function(index, silent) {
        var me = this;

        if (!index) {
            index = 0;
        } else if (!Ext.isNumber(index)) {
            index = Ext.Array.indexOf(me.items, index);
        }
        me.items[index].disabled = false;
        me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).removeCls(me.disabledCls);
        if (!silent) {
            me.fireEvent('enable', me);
        }
    },

    
    disableAction: function(index, silent) {
        var me = this;

        if (!index) {
            index = 0;
        } else if (!Ext.isNumber(index)) {
            index = Ext.Array.indexOf(me.items, index);
        }
        me.items[index].disabled = true;
        me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'action-col-' + index).addCls(me.disabledCls);
        if (!silent) {
            me.fireEvent('disable', me);
        }
    },

    destroy: function() {
        delete this.items;
        delete this.renderer;
        return this.callParent(arguments);
    },

    
    processEvent : function(type, view, cell, recordIndex, cellIndex, e, record, row){
        var me = this,
            target = e.getTarget(),
            match,
            item, fn,
            key = type == 'keydown' && e.getKey(),
            disabled;

        
        
        
        if (key && !Ext.fly(target).findParent(view.getCellSelector())) {
            target = Ext.fly(cell).down('.' + Ext.baseCSSPrefix + 'action-col-icon', true);
        }

        
        if (target && (match = target.className.match(me.actionIdRe))) {
            item = me.items[parseInt(match[1], 10)];
            disabled = item.disabled || (item.isDisabled ? item.isDisabled.call(item.scope || me.origScope || me, view, recordIndex, cellIndex, item, record) : false);
            if (item && !disabled) {
                if (type == 'click' || (key == e.ENTER || key == e.SPACE)) {
                    fn = item.handler || me.handler;
                    if (fn) {
                        fn.call(item.scope || me.origScope || me, view, recordIndex, cellIndex, item, e, record, row);
                    }
                } else if (type == 'mousedown' && item.stopSelection !== false) {
                    return false;
                }
            }
        }
        return me.callParent(arguments);
    },

    cascade: function(fn, scope) {
        fn.call(scope||this, this);
    },

    
    getRefItems: function() {
        return [];
    }
});


Ext.define('Ext.grid.column.Boolean', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.booleancolumn'],
    alternateClassName: 'Ext.grid.BooleanColumn',

    
    
    trueText: 'true',
    

    
    
    falseText: 'false',
    

    
    undefinedText: '&#160;',

    
    
    

    defaultRenderer: function(value){
        if (value === undefined) {
            return this.undefinedText;
        }
        
        if (!value || value === 'false') {
            return this.falseText;
        }
        return this.trueText;
    }
});


Ext.define('Ext.grid.column.CheckColumn', {
    extend:  Ext.grid.column.Column ,
    alternateClassName: 'Ext.ux.CheckColumn',
    alias: 'widget.checkcolumn',

    
    align: 'center',

    
    stopSelection: true,

    tdCls: Ext.baseCSSPrefix + 'grid-cell-checkcolumn',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-checkcolumn',

    clickTargetName: 'el',

    constructor: function() {
        this.addEvents(
            
            'beforecheckchange',
            
            'checkchange'
        );
        this.scope = this;
        this.callParent(arguments);
    },

    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {
        var me = this,
            key = type === 'keydown' && e.getKey(),
            mousedown = type == 'mousedown';

        if (!me.disabled && (mousedown || (key == e.ENTER || key == e.SPACE))) {
            var dataIndex = me.dataIndex,
                checked = !record.get(dataIndex);

            
            if (me.fireEvent('beforecheckchange', me, recordIndex, checked) !== false) {
                record.set(dataIndex, checked);
                me.fireEvent('checkchange', me, recordIndex, checked);

                
                if (mousedown) {
                    e.stopEvent();
                }

                
                
                if (!me.stopSelection) {
                    view.selModel.selectByPosition({
                        row: recordIndex,
                        column: cellIndex
                    });
                }

                
                return false;
            } else {
                
                return !me.stopSelection;
            }
        } else {
            return me.callParent(arguments);
        }
    },

    
    onEnable: function(silent) {
        var me = this;

        me.callParent(arguments);
        me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'grid-cell-' + me.id).removeCls(me.disabledCls);
        if (!silent) {
            me.fireEvent('enable', me);
        }
    },

    
    onDisable: function(silent) {
        var me = this;

        me.callParent(arguments);
        me.up('tablepanel').el.select('.' + Ext.baseCSSPrefix + 'grid-cell-' + me.id).addCls(me.disabledCls);
        if (!silent) {
            me.fireEvent('disable', me);
        }
    },

    
    
    renderer : function(value, meta) {
        var cssPrefix = Ext.baseCSSPrefix,
            cls = [cssPrefix + 'grid-checkcolumn'];

        if (this.disabled) {
            meta.tdCls += ' ' + this.disabledCls;
        }
        if (value) {
            cls.push(cssPrefix + 'grid-checkcolumn-checked');
        }
        return '<img class="' + cls.join(' ') + '" src="' + Ext.BLANK_IMAGE_URL + '"/>';
    }
});


Ext.define('Ext.grid.column.Date', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.datecolumn'],
                           
    alternateClassName: 'Ext.grid.DateColumn',

    
    
    
    
    

    initComponent: function(){
        if (!this.format) {
            this.format = Ext.Date.defaultFormat;
        }
        
        this.callParent(arguments);
    },
    
    defaultRenderer: function(value){
        return Ext.util.Format.date(value, this.format);
    }
});


Ext.define('Ext.grid.column.Number', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.numbercolumn'],
                                  
    alternateClassName: 'Ext.grid.NumberColumn',

    
    
    format : '0,000.00',
    

    
    
    

    defaultRenderer: function(value){
        return Ext.util.Format.number(value, this.format);
    }
});


Ext.define('Ext.grid.column.RowNumberer', {
    extend:  Ext.grid.column.Column ,
    alternateClassName: 'Ext.grid.RowNumberer',
    alias: 'widget.rownumberer',

    
    text: "&#160",

    
    width: 23,

    
    sortable: false,
    
    
    draggable: false,

    
    autoLock: true,

    
    lockable: false,

    align: 'right',

    constructor : function(config){
        var me = this;

        
        
        me.width = me.width;

        me.callParent(arguments);
        me.scope = me;
    },

    
    resizable: false,
    hideable: false,
    menuDisabled: true,
    dataIndex: '',
    cls: Ext.baseCSSPrefix + 'row-numberer',
    tdCls: Ext.baseCSSPrefix + 'grid-cell-row-numberer ' + Ext.baseCSSPrefix + 'grid-cell-special',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-row-numberer',
    rowspan: undefined,

    
    renderer: function(value, metaData, record, rowIdx, colIdx, store) {
        var rowspan = this.rowspan,
            page = store.currentPage,
            result = record.index;
            
        if (rowspan) {
            metaData.tdAttr = 'rowspan="' + rowspan + '"';
        }

        if (result == null) {
            result = rowIdx;
            if (page > 1) {
                result += (page - 1) * store.pageSize; 
            }
        }
        return result + 1;
    }
});


Ext.define('Ext.grid.column.Template', {
    extend:  Ext.grid.column.Column ,
    alias: ['widget.templatecolumn'],
                                
    alternateClassName: 'Ext.grid.TemplateColumn',

    
    
    
    
    

    initComponent: function(){
        var me = this;
        me.tpl = (!Ext.isPrimitive(me.tpl) && me.tpl.compile) ? me.tpl : new Ext.XTemplate(me.tpl);
        
        
        me.hasCustomRenderer = true;
        me.callParent(arguments);
    },
    
    defaultRenderer: function(value, meta, record) {
        var data = Ext.apply({}, record.data, record.getAssociatedData());
        return this.tpl.apply(data);
    }
});


Ext.define('Ext.grid.feature.Feature', {
    extend:  Ext.util.Observable ,
    alias: 'feature.feature',
    
    wrapsItem: false,

    
    isFeature: true,

    
    disabled: false,

    
    hasFeatureEvent: true,

    
    eventPrefix: null,

    
    eventSelector: null,

    
    view: null,

    
    grid: null,
    
    constructor: function(config) {
        this.initialConfig = config;
        this.callParent(arguments);
    },

    clone: function() {
        return new this.self(this.initialConfig);
    },

    init: Ext.emptyFn,
    
    destroy: function(){
        this.clearListeners();
    },

    
    getFireEventArgs: function(eventName, view, featureTarget, e) {
        return [eventName, view, featureTarget, e];
    },
    
    vetoEvent: Ext.emptyFn,

    
    enable: function() {
        this.disabled = false;
    },

    
    disable: function() {
        this.disabled = true;
    }

});


Ext.define('Ext.grid.feature.AbstractSummary', {

    extend:  Ext.grid.feature.Feature ,

    alias: 'feature.abstractsummary',

    summaryRowCls: Ext.baseCSSPrefix + 'grid-row-summary',
    summaryTableCls: Ext.plainTableCls + ' ' + Ext.baseCSSPrefix + 'grid-table',
    summaryRowSelector: '.' + Ext.baseCSSPrefix + 'grid-row-summary',

    
    
    summaryRowTpl: {
        before: function(values, out) {
            
            
            if (values.record.isSummary) {
                this.summaryFeature.outputSummaryRecord(values.record, values, out);
                return false;
            }
        },
        priority: 1000
    },

   
    showSummaryRow: true,

    
    init: function() {
        var me = this;
        me.view.summaryFeature = me;
        me.rowTpl = me.view.self.prototype.rowTpl;

        
        
        me.view.addRowTpl(me.summaryRowTpl).summaryFeature = me;
    },

    
    toggleSummaryRow: function(visible) {
        this.showSummaryRow = !!visible;
    },

    outputSummaryRecord: function(summaryRecord, contextValues, out) {
        var view = contextValues.view,
            savedRowValues = view.rowValues,
            columns = contextValues.columns || view.headerCt.getVisibleGridColumns(),
            colCount = columns.length, i, column,
            
            
            values = {
                view: view,
                record: summaryRecord,
                rowStyle: '',
                rowClasses: [ this.summaryRowCls ],
                itemClasses: [],
                recordIndex: -1,
                rowId: view.getRowId(summaryRecord),
                columns: columns
            };

        
        for (i = 0; i < colCount; i++) {
            column = columns[i];
            column.savedRenderer = column.renderer;
            if (column.summaryRenderer) {
                column.renderer = column.summaryRenderer;
            } else if (!column.summaryType) {
                column.renderer = Ext.emptyFn;
            }

            
            if (!column.dataIndex) {
                column.dataIndex = column.id;
            }
        }

        
        view.rowValues = values;
        view.self.prototype.rowTpl.applyOut(values, out);
        view.rowValues = savedRowValues;

        
        for (i = 0; i < colCount; i++) {
            column = columns[i];
            column.renderer = column.savedRenderer;
            column.savedRenderer = null;
        }
    },

    
    getSummary: function(store, type, field, group){
        var records = group.records;

        if (type) {
            if (Ext.isFunction(type)) {
                return store.getAggregate(type, null, records, [field]);
            }

            switch (type) {
                case 'count':
                    return records.length;
                case 'min':
                    return store.getMin(records, field);
                case 'max':
                    return store.getMax(records, field);
                case 'sum':
                    return store.getSum(records, field);
                case 'average':
                    return store.getAverage(records, field);
                default:
                    return '';

            }
        }
    },

    
    generateSummaryData: function(){
        var me = this,
            store = me.view.store,
            groups = store.groups.items,
            reader = store.proxy.reader,
            len = groups.length,
            groupField = me.getGroupField(),
            data = {},
            lockingPartner = me.lockingPartner,
            i, group, record,
            root, summaryRows, hasRemote,
            convertedSummaryRow, remoteData;

        
        if (me.remoteRoot && reader.rawData) {
            hasRemote = true;
            remoteData = {};
            
            root = reader.root;
            reader.root = me.remoteRoot;
            reader.buildExtractors(true);
            summaryRows = reader.getRoot(reader.rawData)||[];
            len = summaryRows.length;

            
            if (!reader.convertRecordData) {
                reader.buildExtractors();
            }

            for (i = 0; i < len; ++i) {
                convertedSummaryRow = {};

                
                reader.convertRecordData(convertedSummaryRow, summaryRows[i]);
                remoteData[convertedSummaryRow[groupField]] = convertedSummaryRow;
            }

            
            reader.root = root;
            reader.buildExtractors(true);
        }

        for (i = 0; i < len; ++i) {
            group = groups[i];
            
            if (hasRemote || group.isDirty() || !group.hasAggregate()) {
                if (hasRemote) {
                    record = me.populateRemoteRecord(group, remoteData);
                } else {
                    record = me.populateRecord(group);
                }
                
                if (!lockingPartner || (me.view.ownerCt === me.view.ownerCt.ownerLockable.normalGrid)) {
                    group.commit();
                }
            } else {
                record = group.getAggregateRecord();
            }
            data[group.key] = record;
        }

        return data;
    },

    populateRemoteRecord: function(group, data) {
        var record = group.getAggregateRecord(true),
            groupData = data[group.key],
            field;

        record.beginEdit();
        for (field in groupData) {
            if (groupData.hasOwnProperty(field)) {
                if (field !== record.idProperty) {
                    record.set(field, groupData[field]);
                }
            }
        }
        record.endEdit(true);
        record.commit(true);

        return record;
    },

    populateRecord: function(group){
        var me = this,
            view = me.grid.ownerLockable ? me.grid.ownerLockable.view : me.view,
            store = me.view.store,
            record = group.getAggregateRecord(),
            
            columns = view.headerCt.getGridColumns(),
            len = columns.length,
            i, column, fieldName;

        record.beginEdit();
        for (i = 0; i < len; ++i) {
            column = columns[i];
            
            fieldName = column.dataIndex || column.id;
            record.set(fieldName, me.getSummary(store, column.summaryType, fieldName, group));
        }
        record.endEdit(true);
        record.commit();

        return record;
    }
});


Ext.define('Ext.grid.feature.GroupStore', {
    extend:  Ext.util.Observable ,

    isStore: true,

    constructor: function(groupingFeature, store) {
        var me = this;

        me.superclass.constructor.apply(me, arguments);
        me.groupingFeature = groupingFeature;
        me.bindStore(store);
        me.processStore(store);
        me.view.dataSource = me;
    },

    bindStore: function(store) {
        var me = this;

        if (me.store) {
            Ext.destroy(me.storeListeners);
            me.store = null;
        }
        if (store) {
            me.storeListeners = store.on({
                bulkremove: me.onBulkRemove,
                add: me.onAdd,
                update: me.onUpdate,
                refresh: me.onRefresh,
                clear: me.onClear,
                scope: me,
                destroyable: true
            });
            me.store = store;
        }
    },

    processStore: function(store) {
        var me = this,
            groups = store.getGroups(),
            groupCount = groups.length,
            i,
            group,
            groupPlaceholder,
            data = me.data,
            oldGroupCache = me.groupingFeature.groupCache,
            groupCache = me.groupingFeature.clearGroupCache(),
            collapseAll = me.groupingFeature.startCollapsed;

        if (data) {
            data.clear();
        } else {
            data = me.data = new Ext.util.MixedCollection(false, Ext.data.Store.recordIdFn);
        }

        if (store.getCount()) {

            
            me.groupingFeature.startCollapsed = false;

            for (i = 0; i < groupCount; i++) {

                
                
                group = groups[i];

                
                groupCache[group.name] = group;
                group.isCollapsed = collapseAll || (oldGroupCache[group.name] && oldGroupCache[group.name].isCollapsed);

                
                
                if (group.isCollapsed) {
                    group.placeholder = groupPlaceholder = new store.model(null, 'group-' + group.name + '-placeholder');
                    groupPlaceholder.set(me.getGroupField(), group.name);
                    groupPlaceholder.rows = groupPlaceholder.children = group.children;
                    groupPlaceholder.isCollapsedPlaceholder = true;
                    data.add(groupPlaceholder);
                }

                
                else {
                    data.insert(me.data.length, group.children);
                }
            }
        }
    },

    isCollapsed: function(name) {
        return this.groupingFeature.groupCache[name].isCollapsed; 
    },

    isInCollapsedGroup: function(record) {
        var groupData;

        if (this.store.isGrouped() && (groupData = this.groupingFeature.groupCache[record.get(this.getGroupField())])) {
            return groupData.isCollapsed || false;
        }
        return false;
    },

    getCount: function() {
        return this.data.getCount();
    },

    getTotalCount: function() {
        return this.data.getCount();
    },

    
    rangeCached: function(start, end) {
        return end < this.getCount();
    },

    getRange: function(start, end, options) {
        var result = this.data.getRange(start, end);

        if (options && options.callback) {
            options.callback.call(options.scope || this, result, start, end, options);
        }
        return result;
    },

    getAt: function(index) {
        return this.getRange(index, index)[0];
    },

    getById: function(id) {
        return this.store.getById(id);
    },

    expandGroup: function(group) {
        var me = this,
            startIdx;

        if (typeof group === 'string') {
            group = me.groupingFeature.groupCache[group];
        }

        if (group && group.children.length && (startIdx = me.indexOf(group.children[0], true, true)) !== -1) {

            
            group.isCollapsed = false;
            me.isExpandingOrCollapsing = 1;
            
            
            me.data.removeAt(startIdx);
            me.fireEvent('bulkremove', me, [me.getGroupPlaceholder(group)], [startIdx]);

            
            me.data.insert(startIdx, group.children);
            me.fireEvent('add', me, group.children, startIdx);

            me.fireEvent('groupexpand', me, group);
            me.isExpandingOrCollapsing = 0;
        }
    },

    collapseGroup: function(group) {
        var me = this,
            startIdx,
            placeholder,
            i, j, len,
            removeIndices;

        if (typeof group === 'string') {
            group = me.groupingFeature.groupCache[group];
        }

        if (group && (len = group.children.length) && (startIdx = me.indexOf(group.children[0], true)) !== -1) {

            
            group.isCollapsed = true;
            me.isExpandingOrCollapsing = 2;

            
            me.data.removeRange(startIdx, len);

            
            removeIndices = new Array(len);
            for (i = 0, j = startIdx; i < len; i++, j++) {
                removeIndices[i] = j;
            }
            me.fireEvent('bulkremove', me, group.children, removeIndices);

            
            me.data.insert(startIdx, placeholder = me.getGroupPlaceholder(group));
            me.fireEvent('add', me, [placeholder], startIdx);

            me.fireEvent('groupcollapse', me, group);
            me.isExpandingOrCollapsing = 0;
        }
    },

    getGroupPlaceholder: function(group) {
        if (!group.placeholder) {
            var groupPlaceholder = group.placeholder = new this.store.model(null, 'group-' + group.name + '-placeholder');
            groupPlaceholder.set(this.getGroupField(), group.name);
            groupPlaceholder.rows = groupPlaceholder.children = group.children;
            groupPlaceholder.isCollapsedPlaceholder = true;
        }
        return group.placeholder;
    },

    
    
    
    indexOf: function(record, viewOnly, includeCollapsed) {
        var me = this,
            groups,
            groupCount,
            i,
            group,
            groupIndex,
            result = 0;

        if (record && (includeCollapsed || !me.isInCollapsedGroup(record))) {
            groups = me.store.getGroups();
            groupCount = groups.length;
            for (i = 0; i < groupCount; i++) {

                
                
                group = groups[i];
                if (group.name === this.store.getGroupString(record)) {
                    groupIndex = Ext.Array.indexOf(group.children, record);
                    return result + groupIndex;
                }

                result += (viewOnly && me.isCollapsed(group.name)) ? 1 : group.children.length;
            }
        }
        return -1;
    },

    
    indexOfTotal: function(record) {
        var index = record.index;
        if (index || index === 0) {
            return index;
        }
        return this.istore.ndexOf(record);
    },

    onRefresh: function(store) {
        this.processStore(this.store);
        this.fireEvent('refresh', this);
    },

    onBulkRemove: function(store, records, indices) {
        this.processStore(this.store);
        this.fireEvent('refresh', this);
    },

    onClear: function(store, records, startIndex) {
        this.processStore(this.store);
        this.fireEvent('clear', this);
    },

    onAdd: function(store, records, startIndex) {
        this.processStore(this.store);
        this.fireEvent('refresh', this);
    },

    onUpdate: function(store, record, operation, modifiedFieldNames) {
        var me = this,
            groupInfo = me.groupingFeature.getRecordGroup(record),
            firstRec, lastRec;

        
        
        
        if (store.isGrouped()) {
            if (modifiedFieldNames && Ext.Array.contains(modifiedFieldNames, me.groupingFeature.getGroupField())) {
                return me.onRefresh(me.store);
            }

            
            if (groupInfo.isCollapsed) {
                me.fireEvent('update', me, groupInfo.placeholder);
            }

            
            
            else {
                Ext.suspendLayouts();

                
                me.fireEvent('update', me, record, operation, modifiedFieldNames);

                
                
                firstRec = groupInfo.children[0];
                lastRec = groupInfo.children[groupInfo.children.length - 1];

                
                if (firstRec !== record) {
                    me.fireEvent('update', me, firstRec, 'edit');
                }
                if (lastRec !== record && lastRec !== firstRec) {
                    me.fireEvent('update', me, lastRec, 'edit');
                }
                Ext.resumeLayouts(true);
            }
        } else {
            
            me.fireEvent('update', me, record, operation, modifiedFieldNames);
        }
    }
});


Ext.define('Ext.grid.feature.Grouping', {
    extend:  Ext.grid.feature.Feature ,
    mixins: {
        summary:  Ext.grid.feature.AbstractSummary 
    },
                                              

    alias: 'feature.grouping',

    eventPrefix: 'group',
    groupCls: Ext.baseCSSPrefix + 'grid-group-hd',
    eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd',

    refreshData: {},
    groupInfo: {},
    wrapsItem: true,

    

    

    

    

    

    
    groupHeaderTpl: '{columnName}: {name}',

    
    depthToIndent: 17,

    collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed',
    hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed',
    hdNotCollapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-not-collapsible',
    collapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsible',
    ctCls: Ext.baseCSSPrefix  + 'group-hd-container',

    
    
    groupByText : 'Group by this field',
    
    
    
    showGroupsText : 'Show in groups',
    

    
    hideGroupedHeader : false,

    
    startCollapsed : false,

    
    enableGroupingMenu : true,

    
    enableNoGroups : true,

    
    collapsible: true,

    
    expandTip: 'Click to expand. CTRL key collapses all others',
    

    
    collapseTip: 'Click to collapse. CTRL/click collapses all others',
    

    showSummaryRow: false,

    tableTpl: {
        before: function(values) {
            
            if (this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary) {
                return;
            }
            this.groupingFeature.setup(values.rows, values.view.rowValues);
        },
        after: function(values) {
            
            if (this.groupingFeature.disabled || values.rows.length === 1 && values.rows[0].isSummary) {
                return;
            }
            this.groupingFeature.cleanup(values.rows, values.view.rowValues);
        },
        priority: 200
    },

    groupTpl: [
        '{%',
            'var me = this.groupingFeature;',
            
            'if (me.disabled) {',
                'values.needsWrap = false;',
            '} else {',
                'me.setupRowData(values.record, values.recordIndex, values);',
                'values.needsWrap = !me.disabled && (values.isFirstRow || values.summaryRecord);',
            '}',
        '%}',
        '<tpl if="needsWrap">',
            '<tr data-boundView="{view.id}" data-recordId="{record.internalId}" data-recordIndex="{[values.isCollapsedGroup ? -1 : values.recordIndex]}"',
                'class="{[values.itemClasses.join(" ")]} ' + Ext.baseCSSPrefix + 'grid-wrap-row<tpl if="!summaryRecord"> ' + Ext.baseCSSPrefix + 'grid-group-row</tpl>">',
                '<td class="' + Ext.baseCSSPrefix + 'group-hd-container" colspan="{columns.length}">',
                    '<tpl if="isFirstRow">',
                        '{%',
                            
                            
                            'var groupTitleStyle = (!values.view.lockingPartner || (values.view.ownerCt === values.view.ownerCt.ownerLockable.lockedGrid) || (values.view.lockingPartner.headerCt.getVisibleGridColumns().length === 0)) ? "" : "visibility:hidden";',
                        '%}',
                        '<div id="{groupId}" class="' + Ext.baseCSSPrefix + 'grid-group-hd {collapsibleCls}" tabIndex="0">',
                            '<div class="' + Ext.baseCSSPrefix + 'grid-group-title" style="{[groupTitleStyle]}">',
                                '{[values.groupHeaderTpl.apply(values.groupInfo, parent) || "&#160;"]}',
                            '</div>',
                        '</div>',
                    '</tpl>',

                    
                    '<tpl if="summaryRecord || !isCollapsedGroup">',
                        '<table class="', Ext.baseCSSPrefix, '{view.id}-table ', Ext.baseCSSPrefix, 'grid-table',
                            '<tpl if="summaryRecord"> ', Ext.baseCSSPrefix, 'grid-table-summary</tpl>"',
                            'border="0" cellspacing="0" cellpadding="0" style="width:100%">',
                            '{[values.view.renderColumnSizer(out)]}',
                            
                            '<tpl if="!isCollapsedGroup">',
                                '{%',
                                    'values.itemClasses.length = 0;',
                                    'this.nextTpl.applyOut(values, out, parent);',
                                '%}',
                            '</tpl>',
                            '<tpl if="summaryRecord">',
                                '{%me.outputSummaryRecord(values.summaryRecord, values, out);%}',
                            '</tpl>',
                        '</table>',
                    '</tpl>',
                '</td>',
            '</tr>',
        '<tpl else>',
            '{%this.nextTpl.applyOut(values, out, parent);%}',
        '</tpl>', {
            priority: 200,

            syncRowHeights: function(firstRow, secondRow) {
                firstRow = Ext.fly(firstRow, 'syncDest');
                secondRow = Ext.fly(secondRow, 'sycSrc');
                var owner = this.owner,
                    firstHd = firstRow.down(owner.eventSelector, true),
                    secondHd,
                    firstSummaryRow = firstRow.down(owner.summaryRowSelector, true),
                    secondSummaryRow,
                    firstHeight, secondHeight;

                
                if (firstHd && (secondHd = secondRow.down(owner.eventSelector, true))) {
                    firstHd.style.height = secondHd.style.height = '';
                    if ((firstHeight = firstHd.offsetHeight) > (secondHeight = secondHd.offsetHeight)) {
                        Ext.fly(secondHd).setHeight(firstHeight);
                    }
                    else if (secondHeight > firstHeight) {
                        Ext.fly(firstHd).setHeight(secondHeight);
                    }
                }

                
                if (firstSummaryRow && (secondSummaryRow = secondRow.down(owner.summaryRowSelector, true))) {
                    firstSummaryRow.style.height = secondSummaryRow.style.height = '';
                    if ((firstHeight = firstSummaryRow.offsetHeight) > (secondHeight = secondSummaryRow.offsetHeight)) {
                        Ext.fly(secondSummaryRow).setHeight(firstHeight);
                    }
                    else if (secondHeight > firstHeight) {
                        Ext.fly(firstSummaryRow).setHeight(secondHeight);
                    }
                }
            },

            syncContent: function(destRow, sourceRow) {
                destRow = Ext.fly(destRow, 'syncDest');
                sourceRow = Ext.fly(sourceRow, 'sycSrc');
                var owner = this.owner,
                    destHd = destRow.down(owner.eventSelector, true),
                    sourceHd = sourceRow.down(owner.eventSelector, true),
                    destSummaryRow = destRow.down(owner.summaryRowSelector, true),
                    sourceSummaryRow = sourceRow.down(owner.summaryRowSelector, true);

                
                if (destHd && sourceHd) {
                    Ext.fly(destHd).syncContent(sourceHd);
                }

                
                if (destSummaryRow && sourceSummaryRow) {
                    Ext.fly(destSummaryRow).syncContent(sourceSummaryRow);
                }
            }
        }
    ],

    constructor: function() {
        this.groupCache = {};
        this.callParent(arguments);
    },

    init: function(grid) {
        var me = this,
            view = me.view;

        view.isGrouping = true;

        
        if (me.lockingPartner && me.lockingPartner.groupCache) {
            me.groupCache = me.lockingPartner.groupCache;
        }

        me.mixins.summary.init.call(me);

        me.callParent(arguments);
        view.headerCt.on({
            columnhide: me.onColumnHideShow,
            columnshow: me.onColumnHideShow,
            columnmove: me.onColumnMove,
            scope: me
        });

        
        view.addTableTpl(me.tableTpl).groupingFeature = me;

        
        view.addRowTpl(Ext.XTemplate.getTpl(me, 'groupTpl')).groupingFeature = me;

        view.preserveScrollOnRefresh = true;

        
        if (view.store.buffered) {
            me.collapsible = false;
        }
        
        else {

            
            if (this.lockingPartner && this.lockingPartner.dataSource) {
                me.dataSource = view.dataSource = this.lockingPartner.dataSource;
            } else {
                me.dataSource = view.dataSource = new Ext.grid.feature.GroupStore(me, view.store);
            }
        }

        me.grid.on({
            reconfigure: me.onReconfigure
        });
        view.on({
            afterrender: me.afterViewRender,
            scope: me,
            single: true
        });
    },

    clearGroupCache: function() {
        var me = this,
            groupCache = me.groupCache = {};

        if (me.lockingPartner) {
            me.lockingPartner.groupCache = groupCache;
        }
        return groupCache;
    },

    vetoEvent: function(record, row, rowIndex, e) {
        
        if (e.type !== 'mouseover' && e.type !== 'mouseout'  && e.type !== 'mouseenter' && e.type !== 'mouseleave' && e.getTarget(this.eventSelector)) {
            return false;
        }
    },

    enable: function() {
        var me    = this,
            view  = me.view,
            store = view.store,
            groupToggleMenuItem;

        me.lastGroupField = me.getGroupField();

        view.isGrouping = true;
        if (me.lastGroupIndex) {
            me.block();
            store.group(me.lastGroupIndex);
            me.unblock();
        }
        me.callParent();
        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
        if (groupToggleMenuItem) {
            groupToggleMenuItem.setChecked(true, true);
        }
        me.refreshIf();
    },

    disable: function() {
        var me    = this,
            view  = me.view,
            store = view.store,
            groupToggleMenuItem,
            lastGroup;

        view.isGrouping = false;
        lastGroup = store.groupers.first();
        if (lastGroup) {
            me.lastGroupIndex = lastGroup.property;
            me.block();
            store.clearGrouping();
            me.unblock();
        }

        me.callParent();
        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
        if (groupToggleMenuItem) {
            groupToggleMenuItem.setChecked(false, true);
        }
        me.refreshIf();
    },

    refreshIf: function() {
        var ownerCt = this.grid.ownerCt,
            view = this.view;

        if (!view.store.remoteGroup && !this.blockRefresh) {

            
            if (ownerCt && ownerCt.lockable) {
                ownerCt.view.refresh();
            } else {
                view.refresh();
            }
        }
    },

    
    afterViewRender: function() {
        var me = this,
            view = me.view;

        view.on({
            scope: me,
            groupclick: me.onGroupClick
        });

        if (me.enableGroupingMenu) {
            me.injectGroupingMenu();
        }

        me.pruneGroupedHeader();

        me.lastGroupField = me.getGroupField();
        me.block();
        me.onGroupChange();
        me.unblock();
    },

    injectGroupingMenu: function() {
        var me       = this,
            headerCt = me.view.headerCt;

        headerCt.showMenuBy = me.showMenuBy;
        headerCt.getMenuItems = me.getMenuItems();
    },

    onColumnHideShow: function(headerOwnerCt, header) {
        var view = this.view,
            headerCt = view.headerCt,
            menu = headerCt.getMenu(),
            groupToggleMenuItem  = menu.down('#groupMenuItem'),
            colCount = headerCt.getGridColumns().length,
            items,
            len,
            i;

        
        if (groupToggleMenuItem) {
            if (headerCt.getVisibleGridColumns().length > 1) {
                groupToggleMenuItem.enable();
            } else {
                groupToggleMenuItem.disable();
            }
        }

        
        if (view.rendered) {
            items = view.el.query('.' + this.ctCls);
            for (i = 0, len = items.length; i < len; ++i) {
                items[i].colSpan = colCount;
            }
        }
    },

    
    
    onColumnMove: function() {
        var me = this,
            store = me.view.store,
            groups,
            i, len,
            groupInfo, firstRec, lastRec;

        if (store.isGrouped()) {
            groups = store.getGroups();
            len = groups.length;

            
            for (i = 0; i < len; i++) {
                groupInfo = groups[i];
                firstRec = groupInfo.children[0];
                lastRec = groupInfo.children[groupInfo.children.length - 1];

                
                
                store.fireEvent('update', store, firstRec, 'edit', null);
                if (lastRec !== firstRec) {
                    store.fireEvent('update', store, lastRec, 'edit', null);
                }
            }
        }
    },

    showMenuBy: function(t, header) {
        var menu = this.getMenu(),
            groupMenuItem  = menu.down('#groupMenuItem'),
            groupMenuMeth = header.groupable === false || this.view.headerCt.getVisibleGridColumns().length < 2 ?  'disable' : 'enable',
            groupToggleMenuItem  = menu.down('#groupToggleMenuItem'),
            isGrouped = this.view.store.isGrouped();

        groupMenuItem[groupMenuMeth]();
        if (groupToggleMenuItem) {
            groupToggleMenuItem.setChecked(isGrouped, true);
            groupToggleMenuItem[isGrouped ?  'enable' : 'disable']();
        }
        Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);
    },

    getMenuItems: function() {
        var me                 = this,
            groupByText        = me.groupByText,
            disabled           = me.disabled || !me.getGroupField(),
            showGroupsText     = me.showGroupsText,
            enableNoGroups     = me.enableNoGroups,
            getMenuItems       = me.view.headerCt.getMenuItems;

        
        return function() {

            
            
            var o = getMenuItems.call(this);
            o.push('-', {
                iconCls: Ext.baseCSSPrefix + 'group-by-icon',
                itemId: 'groupMenuItem',
                text: groupByText,
                handler: me.onGroupMenuItemClick,
                scope: me
            });
            if (enableNoGroups) {
                o.push({
                    itemId: 'groupToggleMenuItem',
                    text: showGroupsText,
                    checked: !disabled,
                    checkHandler: me.onGroupToggleMenuItemClick,
                    scope: me
                });
            }
            return o;
        };
    },

    
    onGroupMenuItemClick: function(menuItem, e) {
        var me = this,
            menu = menuItem.parentMenu,
            hdr  = menu.activeHeader,
            view = me.view,
            store = view.store;

        me.lastGroupIndex = null;
        me.block();
        me.enable();
        store.group(hdr.dataIndex);
        me.pruneGroupedHeader();
        me.unblock();
        me.refreshIf();
    },

    block: function(fromPartner) {
        this.blockRefresh = this.view.blockRefresh = true;
        if (this.lockingPartner && !fromPartner) {
            this.lockingPartner.block(true);
        }
    },

    unblock: function(fromPartner) {
        this.blockRefresh = this.view.blockRefresh = false;
        if (this.lockingPartner && !fromPartner) {
            this.lockingPartner.unblock(true);
        }
    },

    
    onGroupToggleMenuItemClick: function(menuItem, checked) {
        this[checked ? 'enable' : 'disable']();
    },

    
    pruneGroupedHeader: function() {
        var me = this,
            header = me.getGroupedHeader();

        if (me.hideGroupedHeader && header) {
            Ext.suspendLayouts();
            if (me.prunedHeader && me.prunedHeader !== header) {
                me.prunedHeader.show();
            }
            me.prunedHeader = header;
            header.hide();
            Ext.resumeLayouts(true);
        }
    },

    getHeaderNode: function(groupName) {
        return Ext.get(this.createGroupId(groupName));
    },

    getGroup: function(name) {
        var cache = this.groupCache,
            item = cache[name];

        if (!item) {
            item = cache[name] = {
                isCollapsed: false
            };
        }    
        return item;
    },

    
    isExpanded: function(groupName) {
        return !this.getGroup(groupName).isCollapsed;
    },

    
    expand: function(groupName, focus) {
        this.doCollapseExpand(false, groupName, focus);
    },

    
    expandAll: function() {
        var me = this,
            view = me.view,
            groupCache = me.groupCache,
            groupName,
            lockingPartner = me.lockingPartner,
            partnerView;

        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                groupCache[groupName].isCollapsed = false;
            }
        }
        Ext.suspendLayouts();
        view.suspendEvent('beforerefresh', 'refresh');
        if (lockingPartner) {
            partnerView = lockingPartner.view
            partnerView.suspendEvent('beforerefresh', 'refresh');
        }
        me.dataSource.onRefresh();
        view.resumeEvent('beforerefresh', 'refresh');
        if (lockingPartner) {
            partnerView.resumeEvent('beforerefresh', 'refresh');
        }
        Ext.resumeLayouts(true);

        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                me.afterCollapseExpand(false, groupName);
                if (lockingPartner) {
                    lockingPartner.afterCollapseExpand(false, groupName);
                }
            }
        }
    },

    
    collapse: function(groupName, focus) {
        this.doCollapseExpand(true, groupName, focus);
    },

    
    
    isAllCollapsed: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName;

        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                if (!groupCache[groupName].isCollapsed) {
                    return false;
                }
            }
        }
        return true;
    },

    
    
    isAllExpanded: function() {
        var me = this,
            groupCache = me.groupCache,
            groupName;

        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                if (groupCache[groupName].isCollapsed) {
                    return false;
                }
            }
        }
        return true;
    },

    
    collapseAll: function() {
        var me = this,
            view = me.view,
            groupCache = me.groupCache,
            groupName,
            lockingPartner = me.lockingPartner,
            partnerView;

        
        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                groupCache[groupName].isCollapsed = true;
            }
        }
        Ext.suspendLayouts();
        view.suspendEvent('beforerefresh', 'refresh');
        if (lockingPartner) {
            partnerView = lockingPartner.view
            partnerView.suspendEvent('beforerefresh', 'refresh');
        }
        me.dataSource.onRefresh();
        view.resumeEvent('beforerefresh', 'refresh');
        if (lockingPartner) {
            partnerView.resumeEvent('beforerefresh', 'refresh');
        }

        if (lockingPartner && !lockingPartner.isAllCollapsed()) {
            lockingPartner.collapseAll();
        }
        Ext.resumeLayouts(true);

        
        for (groupName in groupCache) {
            if (groupCache.hasOwnProperty(groupName)) {
                me.afterCollapseExpand(true, groupName);
                if (lockingPartner) {
                    lockingPartner.afterCollapseExpand(true, groupName);
                }
            }
        }

    },

    doCollapseExpand: function(collapsed, groupName, focus) {
        var me = this,
            lockingPartner = me.lockingPartner,
            group = me.groupCache[groupName];

        
        if (group.isCollapsed != collapsed) {

            
            
            Ext.suspendLayouts();
            if (collapsed) {
                me.dataSource.collapseGroup(group);
            } else {
                me.dataSource.expandGroup(group);
            }
            Ext.resumeLayouts(true);

            
            me.afterCollapseExpand(collapsed, groupName, focus);

            
            
            if (lockingPartner) {
                lockingPartner.afterCollapseExpand(collapsed, groupName, false);
            }
        }
    },

    afterCollapseExpand: function(collapsed, groupName, focus) {
        var me = this,
            view = me.view,
            header;

        header = Ext.get(this.getHeaderNode(groupName));
        view.fireEvent(collapsed ? 'groupcollapse' : 'groupexpand', view, header, groupName);
        if (focus) {
            header.up(view.getItemSelector()).scrollIntoView(view.el, null, true);
        }
    },

    onGroupChange: function() {
        var me = this,
            field = me.getGroupField(),
            menuItem,
            visibleGridColumns,
            groupingByLastVisibleColumn;

        if (me.hideGroupedHeader) {
            if (me.lastGroupField) {
                menuItem = me.getMenuItem(me.lastGroupField);
                if (menuItem) {
                    menuItem.setChecked(true);
                }
            }
            if (field) {
                visibleGridColumns = me.view.headerCt.getVisibleGridColumns();

                
                
                groupingByLastVisibleColumn = ((visibleGridColumns.length === 1) && (visibleGridColumns[0].dataIndex == field));
                menuItem = me.getMenuItem(field);
                if (menuItem && !groupingByLastVisibleColumn) {
                    menuItem.setChecked(false);
                }
            }
        }
        me.refreshIf();
        me.lastGroupField = field;
    },

    
    getMenuItem: function(dataIndex){
        var view = this.view,
            header = view.headerCt.down('gridcolumn[dataIndex=' + dataIndex + ']'),
            menu = view.headerCt.getMenu();

        return header ? menu.down('menuitem[headerId='+ header.id +']') : null;
    },

    onGroupKey: function(keyCode, event) {
        var me = this,
            groupName = me.getGroupName(event.target);

        if (groupName) {
            me.onGroupClick(me.view, event.target, groupName, event);
        }
    },

    
    onGroupClick: function(view, rowElement, groupName, e) {
        var me = this,
            groupCache = me.groupCache,
            groupIsCollapsed = !me.isExpanded(groupName),
            g;

        if (me.collapsible) {

            
            if (e.ctrlKey) {
                Ext.suspendLayouts();
                for (g in groupCache) {
                    if (g === groupName) {
                        if (groupIsCollapsed) {
                            me.expand(groupName);
                        }
                    } else {
                        me.doCollapseExpand(true, g, false);
                    }
                }
                Ext.resumeLayouts(true);
                return;
            }

            if (groupIsCollapsed) {
               me.expand(groupName);
            } else {
                me.collapse(groupName);
            }
        }
    },

    setupRowData: function(record, idx, rowValues) {
        var me = this,
            data = me.refreshData,
            groupInfo = me.groupInfo,
            header = data.header,
            groupField = data.groupField,
            store = me.view.dataSource,
            grouper, groupName, prev, next;

        rowValues.isCollapsedGroup = false;
        rowValues.summaryRecord = null;

        if (data.doGrouping) {
            grouper = me.view.store.groupers.first();

            
            
            if (record.children) {
                groupName = grouper.getGroupString(record.children[0]);

                rowValues.isFirstRow = rowValues.isLastRow = true;
                rowValues.itemClasses.push(me.hdCollapsedCls);
                rowValues.isCollapsedGroup = true;
                rowValues.groupInfo = groupInfo;
                groupInfo.groupField = groupField;
                groupInfo.name = groupName;
                groupInfo.groupValue = record.children[0].get(groupField);
                groupInfo.columnName = header ? header.text : groupField;
                rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls;
                rowValues.groupId = me.createGroupId(groupName);
                groupInfo.rows = groupInfo.children = record.children;
                if (me.showSummaryRow) {
                    rowValues.summaryRecord = data.summaryData[groupName];
                }
                return;
            }

            groupName = grouper.getGroupString(record);

            
            rowValues.isFirstRow = idx === 0;
            if (!rowValues.isFirstRow) {
                prev = store.getAt(idx - 1);
                
                if (prev) {
                    
                    rowValues.isFirstRow = !prev.isEqual(grouper.getGroupString(prev), groupName);
                }
            }

            
            rowValues.isLastRow = idx == store.getTotalCount() - 1;
            if (!rowValues.isLastRow) {
                next = store.getAt(idx + 1);
                if (next) {
                    
                    rowValues.isLastRow = !next.isEqual(grouper.getGroupString(next), groupName);
                }
            }

            if (rowValues.isFirstRow) {
                groupInfo.groupField = groupField;
                groupInfo.name = groupName;
                groupInfo.groupValue = record.get(groupField);
                groupInfo.columnName = header ? header.text : groupField;
                rowValues.collapsibleCls = me.collapsible ? me.collapsibleCls : me.hdNotCollapsibleCls;
                rowValues.groupId = me.createGroupId(groupName);

                if (!me.isExpanded(groupName)) {
                    rowValues.itemClasses.push(me.hdCollapsedCls);
                    rowValues.isCollapsedGroup = true;
                }

                
                if (store.buffered) {
                    groupInfo.rows = groupInfo.children = [];
                } else {
                    groupInfo.rows = groupInfo.children = me.getRecordGroup(record).children;
                }
                rowValues.groupInfo = groupInfo;
            }

            if (rowValues.isLastRow) {
                
                if (me.showSummaryRow) {
                    rowValues.summaryRecord = data.summaryData[groupName];
                }
            }
        }
    },

    setup: function(rows, rowValues) {
        var me = this,
            data = me.refreshData,
            isGrouping = !me.disabled && me.view.store.isGrouped();
            
        me.skippedRows = 0;
        if (rowValues.view.bufferedRenderer) {
            rowValues.view.bufferedRenderer.variableRowHeight = true;
        }
        data.groupField = me.getGroupField();
        data.header = me.getGroupedHeader(data.groupField);
        data.doGrouping = isGrouping;
        rowValues.groupHeaderTpl = Ext.XTemplate.getTpl(me, 'groupHeaderTpl');

        if (isGrouping && me.showSummaryRow) {
            data.summaryData = me.generateSummaryData();
        }
    },

    cleanup: function(rows, rowValues) {
        var data = this.refreshData;

        rowValues.groupInfo = rowValues.groupHeaderTpl = rowValues.isFirstRow = null;
        data.groupField = data.header = null;
    },

    getGroupName: function(element) {
        var me = this,
            view = me.view,
            eventSelector = me.eventSelector,
            parts,
            targetEl,
            row;

        
        targetEl = Ext.fly(element).findParent(eventSelector);

        if (!targetEl) {
            
            row = Ext.fly(element).findParent(view.itemSelector);
            if (row) {
                targetEl = row.down(eventSelector, true);
            }
        }

        if (targetEl) {
            parts = targetEl.id.split(view.id + '-hd-');
            if (parts.length === 2) {
                return Ext.htmlDecode(parts[1]);
            }
        }
    },

    
    getRecordGroup: function(record) {
        var grouper = this.view.store.groupers.first();
        if (grouper) {
            return this.groupCache[grouper.getGroupString(record)];
        }
    },

    createGroupId: function(group) {
        return this.view.id + '-hd-' + Ext.htmlEncode(group);
    },

    createGroupCls: function(group) {
        return this.view.id + '-' + Ext.htmlEncode(group) + '-item';    
    },

    getGroupField: function(){
        return this.view.store.getGroupField();
    },

    getGroupedHeader: function(groupField) {
        var me = this,
            headerCt = me.view.headerCt,
            partner = me.lockingPartner,
            selector, header;

        groupField = groupField || this.getGroupField();

        if (groupField) {
            selector = '[dataIndex=' + groupField + ']';
            header = headerCt.down(selector);
            
            if (!header && partner) {
                header = partner.view.headerCt.down(selector);
            }
        }
        return header || null;
    },

    getFireEventArgs: function(type, view, targetEl, e) {
        return [type, view, targetEl, this.getGroupName(targetEl), e];
    },

    destroy: function(){
        var me = this,
            dataSource = me.dataSource;

        me.view = me.prunedHeader = me.grid = me.groupCache = me.dataSource = null;
        me.callParent();
        if (dataSource) {
            dataSource.bindStore(null);
        }
    },

    onReconfigure: function(grid, store, columns, oldStore, oldColumns) {
        var me = grid;

        if (store && store !== oldStore) {
            
            if (store.buffered !== oldStore.buffered) {
                Ext.Error.raise('Cannot reconfigure grouping switching between buffered and non-buffered stores');
            }
            if (store.buffered) {
                me.bindStore(store);
                me.dataSource.processStore(store);
            }
        }
    }
});


Ext.define('Ext.grid.feature.GroupingSummary', {

    extend:  Ext.grid.feature.Grouping ,

    alias: 'feature.groupingsummary',

    showSummaryRow: true,
    
    vetoEvent: function(record, row, rowIndex, e){
        var result = this.callParent(arguments);
        if (result !== false) {
            if (e.getTarget(this.summaryRowSelector)) {
                result = false;
            }
        }
        return result;
    }
});


Ext.define('Ext.grid.feature.RowBody', {
    extend:  Ext.grid.feature.Feature ,
    alias: 'feature.rowbody',

    rowBodyCls: Ext.baseCSSPrefix + 'grid-row-body',
    rowBodyHiddenCls: Ext.baseCSSPrefix + 'grid-row-body-hidden',
    rowBodyTdSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell-rowbody',
    eventPrefix: 'rowbody',
    eventSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-rowbody-tr',

    tableTpl: {
        before: function(values, out) {
            var view = values.view,
                rowValues = view.rowValues;

            this.rowBody.setup(values.rows, rowValues);
        },
        after: function(values, out) {
            var view = values.view,
                rowValues = view.rowValues;

            this.rowBody.cleanup(values.rows, rowValues);
        },
        priority: 100
    },

    extraRowTpl: [
        '{%',
            'values.view.rowBodyFeature.setupRowData(values.record, values.recordIndex, values);',
            'this.nextTpl.applyOut(values, out, parent);',
        '%}',
        '<tr class="' + Ext.baseCSSPrefix + 'grid-rowbody-tr {rowBodyCls}">',
            '<td class="' + Ext.baseCSSPrefix + 'grid-cell-rowbody' + '" colspan="{rowBodyColspan}">',
                '<div class="' + Ext.baseCSSPrefix + 'grid-rowbody' + ' {rowBodyDivCls}">{rowBody}</div>',
            '</td>',
        '</tr>', {
            priority: 100,

            syncRowHeights: function(firstRow, secondRow) {
                var owner = this.owner,
                    firstRowBody = Ext.fly(firstRow).down(owner.eventSelector, true),
                    secondRowBody,
                    firstHeight, secondHeight;

                
                if (firstRowBody && (secondRowBody = Ext.fly(secondRow).down(owner.eventSelector, true))) {
                    if ((firstHeight = firstRowBody.offsetHeight) > (secondHeight = secondRowBody.offsetHeight)) {
                        Ext.fly(secondRowBody).setHeight(firstHeight);
                    }
                    else if (secondHeight > firstHeight) {
                        Ext.fly(firstRowBody).setHeight(secondHeight);
                    }
                }
            },

            syncContent: function(destRow, sourceRow) {
                var owner = this.owner,
                    destRowBody = Ext.fly(destRow).down(owner.eventSelector, true),
                    sourceRowBody;

                
                if (destRowBody && (sourceRowBody = Ext.fly(sourceRow).down(owner.eventSelector, true))) {
                    Ext.fly(destRowBody).syncContent(sourceRowBody);
                }
            }
        }
    ],

    init: function(grid) {
        var me = this,
            view = me.view;

        view.rowBodyFeature = me;

        
        
        if (!view.findFeature('rowwrap')) {
            grid.mon(view, {
                element: 'el',
                mousedown: me.onMouseDown,
                scope: me
            });
            
            me.mon(grid.getStore(), 'remove', me.onStoreRemove, me);
        }

        view.headerCt.on({
            columnschanged: me.onColumnsChanged,
            scope: me
        });
        view.addTableTpl(me.tableTpl).rowBody = me;
        view.addRowTpl(Ext.XTemplate.getTpl(this, 'extraRowTpl'));
        me.callParent(arguments);
    },
    
    onStoreRemove: function(store, model, index){
        var view = this.view,
            node;
            
        if (view.rendered) {
            node = view.getNode(index);
            if (node) {
                node = Ext.fly(node).next(this.eventSelector);
                if (node) {
                    node.remove();
                }
            }
        }
    },

    
    onMouseDown: function(e) {
        var me = this,
            tableRow = e.getTarget(me.eventSelector);

        
        if (tableRow && Ext.fly(tableRow = tableRow.previousSibling).is(me.view.getItemSelector())) {
            e.target = tableRow;
            me.view.handleEvent(e);
        }
    },

    getSelectedRow: function(view, rowIndex) {
        var selectedRow = view.getNode(rowIndex, false);
        if (selectedRow) {
            return Ext.fly(selectedRow).down(this.eventSelector);
        }
        return null;
    },

    
    onColumnsChanged: function(headerCt) {
        var items = this.view.el.query(this.rowBodyTdSelector),
            colspan = headerCt.getVisibleGridColumns().length,
            len = items.length,
            i;

        for (i = 0; i < len; ++i) {
            items[i].colSpan = colspan;
        }
    },
    
    
    setupRowData: function(record, rowIndex, rowValues) {
        if (this.getAdditionalData) {
            Ext.apply(rowValues, this.getAdditionalData(record.data, rowIndex, record, rowValues));
        }
    },

    setup: function(rows, rowValues) {
        rowValues.rowBodyCls = this.rowBodyCls;
        rowValues.rowBodyColspan = rowValues.view.getGridColumns().length;
    },

    cleanup: function(rows, rowValues) {
        rowValues.rowBodyCls = rowValues.rowBodyColspan = rowValues.rowBody = null;    
    }
});


Ext.define('Ext.grid.feature.RowWrap', {
    extend:  Ext.grid.feature.Feature ,
    alias: 'feature.rowwrap',
    
    rowWrapTd: 'td.' + Ext.baseCSSPrefix + 'grid-rowwrap',
    
    
    hasFeatureEvent: false,
    
    tableTpl: {
        before: function(values, out) {
            if (values.view.bufferedRenderer) {
                values.view.bufferedRenderer.variableRowHeight = true;
            }
        },
        priority: 200
    },

    wrapTpl: [
        '<tr data-boundView="{view.id}" data-recordId="{record.internalId}" data-recordIndex="{recordIndex}" class="{[values.itemClasses.join(" ")]} ' + Ext.baseCSSPrefix + 'grid-wrap-row">',
            '<td class="' + Ext.baseCSSPrefix + 'grid-rowwrap ' + Ext.baseCSSPrefix + 'grid-td" colSpan="{columns.length}">',
                '<table class="' + Ext.baseCSSPrefix + '{view.id}-table ' + Ext.baseCSSPrefix + 'grid-table" border="0" cellspacing="0" cellpadding="0">',
                    '{[values.view.renderColumnSizer(out)]}',
                    '{%',
                        'values.itemClasses.length = 0;',
                        'this.nextTpl.applyOut(values, out, parent)',
                    '%}',
                '</table>',
            '</td>',
        '</tr>', {
            priority: 200
        }
    ],

    init: function(grid) {
        var me = this;
        me.view.addTableTpl(me.tableTpl);
        me.view.addRowTpl(Ext.XTemplate.getTpl(me, 'wrapTpl'));
        me.view.headerCt.on({
            columnhide: me.onColumnHideShow,
            columnshow: me.onColumnHideShow,
            scope: me
        });
    },

    
    onColumnHideShow: function() {
        var view = this.view,
            items = view.el.query(this.rowWrapTd),
            colspan = view.headerCt.getVisibleGridColumns().length,
            len = items.length,
            i;
            
        for (i = 0; i < len; ++i) {
            items[i].colSpan = colspan;
        }
    }
});


Ext.define('Ext.grid.feature.Summary', {

    

    extend:  Ext.grid.feature.AbstractSummary ,

    alias: 'feature.summary',

    
    dock: undefined,

    dockedSummaryCls: Ext.baseCSSPrefix + 'docked-summary',

    panelBodyCls: Ext.baseCSSPrefix + 'summary-',

    init: function(grid) {
        var me = this,
            view = me.view;

        me.callParent(arguments);

        if (me.dock) {
            grid.headerCt.on({
                afterlayout: me.onStoreUpdate,
                scope: me
            });
            grid.on({
                beforerender: function() {
                    var tableCls = [me.summaryTableCls];
                    if (view.columnLines) {
                        tableCls[tableCls.length] = view.ownerCt.colLinesCls;
                    }
                    me.summaryBar = grid.addDocked({
                        childEls: ['innerCt'],
                        renderTpl: [
                            '<div id="{id}-innerCt">',
                                '<table cellPadding="0" cellSpacing="0" class="' + tableCls.join(' ') + '">',
                                    '<tr class="' + me.summaryRowCls + '"></tr>',
                                '</table>',
                            '</div>'
                        ],
                        style: 'overflow:hidden',
                        itemId: 'summaryBar',
                        cls: [ me.dockedSummaryCls, me.dockedSummaryCls + '-' + me.dock ],
                        xtype: 'component',
                        dock: me.dock,
                        weight: 10000000
                    })[0];
                },
                afterrender: function() {
                    grid.body.addCls(me.panelBodyCls + me.dock);
                    view.mon(view.el, {
                        scroll: me.onViewScroll,
                        scope: me
                    });
                    me.onStoreUpdate();
                },
                single: true
            });

            
            grid.headerCt.afterComponentLayout = Ext.Function.createSequence(grid.headerCt.afterComponentLayout, function() {
                me.summaryBar.innerCt.setWidth(this.getFullWidth() + Ext.getScrollbarSize().width);
            });
        } else {
            me.view.addFooterFn(me.renderTFoot);
        }

        grid.on({
            columnmove: me.onStoreUpdate,
            scope: me
        });

        
        view.mon(view.store, {
            update: me.onStoreUpdate,
            datachanged: me.onStoreUpdate,
            scope: me
        });
    },

    renderTFoot: function(values, out) {
        var view = values.view,
            me = view.findFeature('summary');

        if (me.showSummaryRow) {
            out.push('<tfoot>');
            me.outputSummaryRecord(me.createSummaryRecord(view), values, out);
            out.push('</tfoot>');
        }
    },
    
    vetoEvent: function(record, row, rowIndex, e) {
        return !e.getTarget(this.summaryRowSelector);
    },

    onViewScroll: function() {
        this.summaryBar.el.dom.scrollLeft = this.view.el.dom.scrollLeft;
    },

    createSummaryRecord: function(view) {
        var columns = view.headerCt.getVisibleGridColumns(),
            info = {
                records: view.store.getRange()
            },
            colCount = columns.length, i, column,
            summaryRecord = this.summaryRecord || (this.summaryRecord = new view.store.model(null, view.id + '-summary-record'));

        
        summaryRecord.beginEdit();
        for (i = 0; i < colCount; i++) {
            column = columns[i];

            
            
            if (!column.dataIndex) {
                column.dataIndex = column.id;
            }

            summaryRecord.set(column.dataIndex, this.getSummary(view.store, column.summaryType, column.dataIndex, info));
        }
        summaryRecord.endEdit(true);
        
        summaryRecord.commit(true);
        summaryRecord.isSummary = true;

        return summaryRecord;
    },

    onStoreUpdate: function() {
        var me = this,
            view = me.view,
            record = me.createSummaryRecord(view),
            newRowDom = view.createRowElement(record, -1),
            oldRowDom, partner,
            p;

        if (!view.rendered) {
            return;
        }
        
        
        if (me.dock) {
            oldRowDom = me.summaryBar.el.down('.' + me.summaryRowCls, true);
        }
        
        
        else {
            oldRowDom = me.view.getNode(record);
        }
        
        if (oldRowDom) {
            p = oldRowDom.parentNode;
            p.insertBefore(newRowDom, oldRowDom);
            p.removeChild(oldRowDom);

            partner = me.lockingPartner;
            
            
            if (partner && partner.grid.rendered && !me.calledFromLockingPartner) {
                partner.calledFromLockingPartner = true;
                partner.onStoreUpdate();
                partner.calledFromLockingPartner = false;
            }
        }
        
        if (me.dock) {
            me.onColumnHeaderLayout();
        }
    },

    
    onColumnHeaderLayout: function() {
        var view = this.view,
            columns = view.headerCt.getVisibleGridColumns(),
            column,
            len = columns.length, i,
            summaryEl = this.summaryBar.el,
            el;

        for (i = 0; i < len; i++) {
            column = columns[i];
            el = summaryEl.down(view.getCellSelector(column));
            if (el) {
                if (column.hidden) {
                    el.setDisplayed(false);
                } else {
                    el.setDisplayed(true);
                    el.setWidth(column.width || (column.lastBox ? column.lastBox.width : 100));
                }
            }
        }
    }
});


Ext.define('Ext.grid.locking.HeaderContainer', {
    extend:  Ext.grid.header.Container ,
               
                                
      

    constructor: function(lockable) {
        var me = this,
            events,
            event,
            eventNames = [],
            lockedGrid = lockable.lockedGrid,
            normalGrid = lockable.normalGrid;

        me.lockable = lockable;
        me.callParent();

        
        lockedGrid.columnManager.rootColumns =
            normalGrid.columnManager.rootColumns =
            lockable.columnManager =
            me.columnManager = new Ext.grid.ColumnManager(lockedGrid.headerCt, normalGrid.headerCt);

        
        events = lockedGrid.headerCt.events;
        for (event in events) {
            if (events.hasOwnProperty(event)) {
                eventNames.push(event);
            }
        }
        me.relayEvents(lockedGrid.headerCt, eventNames);
        me.relayEvents(normalGrid.headerCt, eventNames);
    },

    getRefItems: function() {
        return this.lockable.lockedGrid.headerCt.getRefItems().concat(this.lockable.normalGrid.headerCt.getRefItems());
    },

    
    
    getGridColumns: function() {
        return this.lockable.lockedGrid.headerCt.getGridColumns().concat(this.lockable.normalGrid.headerCt.getGridColumns());
    },

    
    getColumnsState: function () {
        var me = this,
            locked = me.lockable.lockedGrid.headerCt.getColumnsState(),
            normal = me.lockable.normalGrid.headerCt.getColumnsState();

        return locked.concat(normal);
    },

    
    applyColumnsState: function (columns) {
        var me             = this,
            lockedGrid     = me.lockable.lockedGrid,
            lockedHeaderCt = lockedGrid.headerCt,
            normalHeaderCt = me.lockable.normalGrid.headerCt,
            lockedCols     = Ext.Array.toValueMap(lockedHeaderCt.items.items, 'headerId'),
            normalCols     = Ext.Array.toValueMap(normalHeaderCt.items.items, 'headerId'),
            locked         = [],
            normal         = [],
            lockedWidth    = 1,
            length         = columns.length,
            i, existing,
            lockedDefault,
            col;

        for (i = 0; i < length; i++) {
            col = columns[i];

            lockedDefault = lockedCols[col.id];
            existing = lockedDefault || normalCols[col.id];

            if (existing) {
                if (existing.applyColumnState) {
                    existing.applyColumnState(col);
                }
                if (existing.locked === undefined) {
                    existing.locked = !!lockedDefault;
                }
                if (existing.locked) {
                    locked.push(existing);
                    if (!existing.hidden && typeof existing.width == 'number') {
                        lockedWidth += existing.width;
                    }
                } else {
                    normal.push(existing);
                }
            }
        }

        
        if (locked.length + normal.length == lockedHeaderCt.items.getCount() + normalHeaderCt.items.getCount()) {
            lockedHeaderCt.removeAll(false);
            normalHeaderCt.removeAll(false);

            lockedHeaderCt.add(locked);
            normalHeaderCt.add(normal);

            lockedGrid.setWidth(lockedWidth);
        }
    }
});


Ext.define('Ext.grid.locking.View', {
    alternateClassName: 'Ext.grid.LockingView',

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    isLockingView: true,

    eventRelayRe: /^(beforeitem|beforecontainer|item|container|cell|refresh)/,

    constructor: function(config){
        var me = this,
            eventNames = [],
            eventRe = me.eventRelayRe,
            locked = config.locked.getView(),
            normal = config.normal.getView(),
            events,
            event;

        Ext.apply(me, {
            lockedView: locked,
            normalView: normal,
            lockedGrid: config.locked,
            normalGrid: config.normal,
            panel: config.panel
        });
        me.mixins.observable.constructor.call(me, config);

        
        events = locked.events;
        for (event in events) {
            if (events.hasOwnProperty(event) && eventRe.test(event)) {
                eventNames.push(event);
            }
        }
        me.relayEvents(locked, eventNames);
        me.relayEvents(normal, eventNames);

        normal.on({
            scope: me,
            itemmouseleave: me.onItemMouseLeave,
            itemmouseenter: me.onItemMouseEnter
        });

        locked.on({
            scope: me,
            itemmouseleave: me.onItemMouseLeave,
            itemmouseenter: me.onItemMouseEnter
        });
        
        me.panel.on({
            render: me.onPanelRender,
            scope: me
        });
    },

    onPanelRender: function() {
        var me = this,
            mask = me.loadMask,
            cfg = {
                target: me.panel,
                msg: me.loadingText,
                msgCls: me.loadingCls,
                useMsg: me.loadingUseMsg,
                store: me.panel.store
            };

        
        
        me.el = me.panel.body;
        me.fireEvent('render', me);

        if (mask) {
            
            if (Ext.isObject(mask)) {
                cfg = Ext.apply(cfg, mask);
            }
            
            
            
            
            me.loadMask = new Ext.LoadMask(cfg);
        }
    },

    getGridColumns: function() {
        var cols = this.lockedGrid.headerCt.getVisibleGridColumns();
        return cols.concat(this.normalGrid.headerCt.getVisibleGridColumns());
    },

    getEl: function(column){
        return this.getViewForColumn(column).getEl();
    },

    getViewForColumn: function(column) {
        var view = this.lockedView,
            inLocked;

        view.headerCt.cascade(function(col){
            if (col === column) {
                inLocked = true;
                return false;
            }
        });

        return inLocked ? view : this.normalView;
    },

    onItemMouseEnter: function(view, record){
        var me = this,
            locked = me.lockedView,
            other = me.normalView,
            item;

        if (view.trackOver) {
            if (view !== locked) {
                other = locked;
            }
            item = other.getNode(record, false);
            other.highlightItem(item);
        }
    },

    onItemMouseLeave: function(view, record){
        var me = this,
            locked = me.lockedView,
            other = me.normalView;

        if (view.trackOver) {
            if (view !== locked) {
                other = locked;
            }
            other.clearHighlight();
        }
    },

    relayFn: function(name, args){
        args = args || [];

        var view = this.lockedView;
        view[name].apply(view, args);
        view = this.normalView;
        view[name].apply(view, args);
    },

    getSelectionModel: function(){
        return this.panel.getSelectionModel();
    },

    getStore: function(){
        return this.panel.store;
    },

    getNode: function(nodeInfo, dataRow) {
        
        return this.normalView.getNode(nodeInfo, dataRow);
    },

    getCell: function(record, column) {
        var view = this.getViewForColumn(column),
            row = view.getNode(record, true);
            
        return Ext.fly(row).down(column.getCellSelector());
    },

    indexOf: function(record) {
        var result = this.lockedView.indexOf(record);
        if (!result) {
            result = this.normalView.indexOf(record);
        }
        return result;
    },

    focus: function() {
        var p = this.getSelectionModel().getCurrentPosition(),
            v = p ? p.view : this.normalView;

        v.focus();
    },
    
    focusRow: function(row) {
        this.normalView.focusRow(row);
    },

    focusCell: function(position) {
        position.view.focusCell(position);
    },

    isVisible: function(deep) {
        return this.panel.isVisible(deep);
    },

    getRecord: function(node) {
        var result = this.lockedView.getRecord(node);
        if (!result) {
            result = this.normalView.getRecord(node);
        }
        return result;
    },
    
    scrollBy: function(){
        var normal = this.normalView;
        normal.scrollBy.apply(normal, arguments);
    },

    addElListener: function(eventName, fn, scope){
        this.relayFn('addElListener', arguments);
    },

    refreshNode: function(){
        this.relayFn('refreshNode', arguments);
    },

    refresh: function(){
        this.relayFn('refresh', arguments);
    },

    bindStore: function(){
        this.relayFn('bindStore', arguments);
    },

    addRowCls: function(){
        this.relayFn('addRowCls', arguments);
    },

    removeRowCls: function(){
        this.relayFn('removeRowCls', arguments);
    },
    
    destroy: function(){
        var me = this,
            mask = me.loadMask;
            
        
        
        me.clearListeners();
        if (mask && mask.bindStore) {
            mask.bindStore(null);
        }
    }

});


Ext.define('Ext.grid.locking.Lockable', {
    alternateClassName: 'Ext.grid.Lockable',

               
                                
                                    
                                           
                        
      

    
    syncRowHeight: true,

    

    

    

    headerCounter: 0,

    
    scrollDelta: 40,
    
    
    
    
    
    lockedGridCls: Ext.baseCSSPrefix + 'grid-inner-locked',

    
    
    unlockText: 'Unlock',
    
    
    lockText: 'Lock',
    

    
    
    bothCfgCopy: [
        'invalidateScrollerOnRefresh',
        'hideHeaders',
        'enableColumnHide',
        'enableColumnMove',
        'enableColumnResize',
        'sortableColumns',
        'columnLines',
        'rowLines'
    ],
    normalCfgCopy: [ 
        'verticalScroller', 
        'verticalScrollDock', 
        'verticalScrollerType', 
        'scroll'
    ],
    lockedCfgCopy: [],

    determineXTypeToCreate: function(lockedSide) {
        var me = this,
            typeToCreate,
            xtypes, xtypesLn, xtype, superxtype;

        if (me.subGridXType) {
            typeToCreate = me.subGridXType;
        } else {
            
            
            if (!lockedSide) {
                return 'gridpanel';
            }
            xtypes     = this.getXTypes().split('/');
            xtypesLn   = xtypes.length;
            xtype      = xtypes[xtypesLn - 1];
            superxtype = xtypes[xtypesLn - 2];

            if (superxtype !== 'tablepanel') {
                typeToCreate = superxtype;
            } else {
                typeToCreate = xtype;
            }
        }

        return typeToCreate;
    },

    
    
    injectLockable: function() {
        
        this.lockable = true;
        
        
        this.hasView = true;

        var me = this,
            scrollbarHeight = Ext.getScrollbarSize().height,
            store = me.store = Ext.StoreManager.lookup(me.store),

            
            selModel = me.getSelectionModel(),

            
            allFeatures,

            
            allPlugins,

            lockedGrid,
            normalGrid,
            i,
            columns,
            lockedHeaderCt,
            normalHeaderCt,
            lockedView,
            normalView,
            listeners,
            bufferedRenderer = me.findPlugin('bufferedrenderer');

        allFeatures = me.constructLockableFeatures();

        
        if (me.features) {
            me.features = null;
        }

        
        allPlugins = me.constructLockablePlugins();
        me.plugins = allPlugins.topPlugins;

        lockedGrid = Ext.apply({
            id: me.id + '-locked',
            isLocked: true,
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(true),
            store: store,
            scrollerOwner: false,
            
            
            animate: false,
            
            scroll: scrollbarHeight ? false : 'vertical',
            selModel: selModel,
            border: false,
            cls: me.lockedGridCls,
            isLayoutRoot: function() {
                return false;
            },
            features: allFeatures.lockedFeatures,
            plugins: allPlugins.lockedPlugins
        }, me.lockedGridConfig);

        normalGrid = Ext.apply({
            id: me.id + '-normal',
            isLocked: false,
            ownerLockable: me,
            xtype: me.determineXTypeToCreate(),
            store: store,
            scrollerOwner: false,
            selModel: selModel,
            border: false,
            isLayoutRoot: function() {
                return false;
            },
            features: allFeatures.normalFeatures,
            plugins: allPlugins.normalPlugins
        }, me.normalGridConfig);

        me.addCls(Ext.baseCSSPrefix + 'grid-locked');

        
        
        
        Ext.copyTo(normalGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.bothCfgCopy, true);
        Ext.copyTo(normalGrid, me, me.normalCfgCopy, true);
        Ext.copyTo(lockedGrid, me, me.lockedCfgCopy, true);
        for (i = 0; i < me.normalCfgCopy.length; i++) {
            delete me[me.normalCfgCopy[i]];
        }
        for (i = 0; i < me.lockedCfgCopy.length; i++) {
            delete me[me.lockedCfgCopy[i]];
        }

        me.addEvents(
            
            'processcolumns',

            
            'lockcolumn',

            
            'unlockcolumn'
        );

        me.addStateEvents(['lockcolumn', 'unlockcolumn']);

        columns = me.processColumns(me.columns);

        
        
        lockedGrid.width = columns.lockedWidth + Ext.num(selModel.headerWidth, 0) + (columns.locked.items.length ? 1 : 0);
        lockedGrid.columns = columns.locked;
        normalGrid.columns = columns.normal;

        
        normalGrid.flex = 1;
        lockedGrid.viewConfig = me.lockedViewConfig || {};
        lockedGrid.viewConfig.loadingUseMsg = false;
        lockedGrid.viewConfig.loadMask = false;

        
        
        
        
        if (scrollbarHeight) {
            lockedGrid.viewConfig.style = 'border-bottom:' + scrollbarHeight +
                'px solid #f6f6f6;' + (lockedGrid.viewConfig.style || '');
        }

        normalGrid.viewConfig = me.normalViewConfig || {};
        normalGrid.viewConfig.loadMask = false;


        Ext.applyIf(lockedGrid.viewConfig, me.viewConfig);
        Ext.applyIf(normalGrid.viewConfig, me.viewConfig);

        me.lockedGrid = Ext.ComponentManager.create(lockedGrid);

        if (me.isTree) {
            
            me.lockedGrid.getView().animate = false;

            
            normalGrid.store = me.lockedGrid.view.store;

            
            normalGrid.deferRowRender = false;

            
            normalGrid.viewConfig.stripeRows = me.lockedGrid.view.stripeRows;
            normalGrid.rowLines = me.lockedGrid.rowLines;
        }

        
        lockedView = me.lockedGrid.getView();
        normalGrid.viewConfig.lockingPartner = lockedView;
        me.normalGrid = Ext.ComponentManager.create(normalGrid);
        lockedView.lockingPartner = normalView = me.normalGrid.getView();

        me.view = new Ext.grid.locking.View({
            loadingText: normalView.loadingText,
            loadingCls: normalView.loadingCls,
            loadingUseMsg: normalView.loadingUseMsg,
            loadMask: me.loadMask !== false,
            locked: me.lockedGrid,
            normal: me.normalGrid,
            panel: me
        });

        
        listeners = bufferedRenderer ? {} : {
            scroll: {
                fn: me.onLockedViewScroll,
                element: 'el',
                scope: me
            }
        };

        
        
        
        
        if (scrollbarHeight) {
            me.lockedGrid.on({
                afterlayout: me.afterLockedViewLayout,
                scope: me
            });

            
            lockedView.getOverflowStyle();

            
            if (lockedView.scrollFlags.y) {
                me.lockedGrid.headerCt.forceFit = true;
            }
            
            else {
                listeners.mousewheel = {
                    fn: me.onLockedViewMouseWheel,
                    element: 'el',
                    scope: me
                };
            }
        }
        lockedView.on(listeners);

        
        listeners = bufferedRenderer ? {} : {
            scroll: {
                fn: me.onNormalViewScroll,
                element: 'el',
                scope: me
            },
            scope: me
        };
        normalView.on(listeners);

        lockedHeaderCt = me.lockedGrid.headerCt;
        normalHeaderCt = me.normalGrid.headerCt;

        
        
        me.headerCt = me.view.headerCt = new Ext.grid.locking.HeaderContainer(me);

        lockedHeaderCt.lockedCt = true;
        lockedHeaderCt.lockableInjected = true;
        normalHeaderCt.lockableInjected = true;

        lockedHeaderCt.on({
            
            add: {
                buffer: 1,
                scope: me,
                fn: me.onLockedHeaderAdd
            },
            columnshow: me.onLockedHeaderShow,
            columnhide: me.onLockedHeaderHide,
            sortchange: me.onLockedHeaderSortChange,
            columnresize: me.onLockedHeaderResize,
            scope: me
        });

        normalHeaderCt.on({
            sortchange: me.onNormalHeaderSortChange,
            scope: me
        });

        me.modifyHeaderCt();
        me.items = [me.lockedGrid, me.normalGrid];

        me.relayHeaderCtEvents(lockedHeaderCt);
        me.relayHeaderCtEvents(normalHeaderCt);

        
        
        me.storeRelayers = me.relayEvents(store, [
            
            'filterchange'
        ]);

        me.layout = {
            type: 'hbox',
            align: 'stretch'
        };
    },

    getLockingViewConfig: function(){
        return {
            xclass: 'Ext.grid.locking.View',
            locked: this.lockedGrid,
            normal: this.normalGrid,
            panel: this
        };
    },

    processColumns: function(columns) {
        
        var i,
            len,
            column,
            cp = this.dummyHdrCtr || (this.self.prototype.dummyHdrCtr = new Ext.grid.header.Container()),
            lockedHeaders = [],
            normalHeaders = [],
            lockedHeaderCt = {
                itemId: 'lockedHeaderCt',
                stretchMaxPartner: '^^>>#normalHeaderCt',
                items: lockedHeaders
            },
            normalHeaderCt = {
                itemId: 'normalHeaderCt',
                stretchMaxPartner: '^^>>#lockedHeaderCt',
                items: normalHeaders
            },
            result = {
                lockedWidth: 0,
                locked: lockedHeaderCt,
                normal: normalHeaderCt
            };

        
        if (Ext.isObject(columns)) {
            Ext.applyIf(lockedHeaderCt, columns);
            Ext.applyIf(normalHeaderCt, columns);
            Ext.apply(cp, columns);
            columns = columns.items;
        }

        for (i = 0, len = columns.length; i < len; ++i) {
            column = columns[i];

            
            
            
            if (!column.isComponent) {
                column = cp.lookupComponent(cp.applyDefaults(column));
            }

            
            
            column.processed = true;
            if (column.locked || column.autoLock) {
                if (!column.hidden) {
                    result.lockedWidth += this.getColumnWidth(column) || cp.defaultWidth;
                }
                lockedHeaders.push(column);
            } else {
                normalHeaders.push(column);
            }
            if (!column.headerId) {
                column.headerId = (column.initialConfig || column).id || ('h' + (++this.headerCounter));
            }
        }
        this.fireEvent('processcolumns', this, lockedHeaders, normalHeaders);
        return result;
    },

    
    
    getColumnWidth: function(column) {
        var result = column.width || 0,
            subcols, len, i;

        if (!result && column.isGroupHeader) {
            subcols = column.items.items;
            len = subcols.length;
            for (i = 0; i < len; i++) {
                result += this.getColumnWidth(subcols[i]);
            }
        }
        return result;
    },

    
    
    
    afterLockedViewLayout: function() {
        var me = this,
            lockedView = me.lockedGrid.getView(),
            lockedViewEl = lockedView.el.dom,
            spacerHeight = (me.normalGrid.headerCt.tooNarrow ? Ext.getScrollbarSize().height : 0);

        
        if (lockedView.scrollFlags.x && lockedViewEl.scrollWidth > lockedViewEl.clientWidth) {
            spacerHeight = 0;
        }

        lockedView.el.dom.style.borderBottomWidth = spacerHeight + 'px';

        
        
        if (!Ext.isBorderBox) {
            lockedView.el.setHeight(lockedView.lastBox.height);
        }
    },

    
    onLockedViewMouseWheel: function(e) {
        var me = this,
            scrollDelta = -me.scrollDelta,
            deltaY = scrollDelta * e.getWheelDeltas().y,
            vertScrollerEl = me.lockedGrid.getView().el.dom,
            verticalCanScrollDown, verticalCanScrollUp;

        if (!me.ignoreMousewheel) {
            if (vertScrollerEl) {
                verticalCanScrollDown = vertScrollerEl.scrollTop !== vertScrollerEl.scrollHeight - vertScrollerEl.clientHeight;
                verticalCanScrollUp   = vertScrollerEl.scrollTop !== 0;
            }

            if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) {
                e.stopEvent();

                
                
                
                vertScrollerEl.scrollTop += deltaY;
                me.normalGrid.getView().el.dom.scrollTop = vertScrollerEl.scrollTop;

                
                me.onNormalViewScroll();
            }
        }
    },

    onLockedViewScroll: function() {
        var me = this,
            lockedView = me.lockedGrid.getView(),
            normalView = me.normalGrid.getView(),
            normalDom = normalView.el.dom,
            lockedDom = lockedView.el.dom,
            normalTable,
            lockedTable;

        
        if (normalDom.scrollTop !== lockedDom.scrollTop) {
            normalDom.scrollTop = lockedDom.scrollTop;
    
            
            if (me.store.buffered) {
                lockedTable = lockedView.el.child('table', true);
                normalTable = normalView.el.child('table', true);
                normalTable.style.position = 'absolute';
                normalTable.style.top = lockedTable.style.top;
            }
        }
    },
    
    onNormalViewScroll: function() {
        var me = this,
            lockedView = me.lockedGrid.getView(),
            normalView = me.normalGrid.getView(),
            normalDom = normalView.el.dom,
            lockedDom = lockedView.el.dom,
            normalTable,
            lockedTable;

        
        
        
        
        
        
        
        
        
        
        if (normalDom.scrollTop !== lockedDom.scrollTop) {
            lockedDom.scrollTop = normalDom.scrollTop;
    
            
            if (me.store.buffered) {
                lockedTable = lockedView.el.child('table', true);
                normalTable = normalView.el.child('table', true);
                lockedTable.style.position = 'absolute';
                lockedTable.style.top = normalTable.style.top;
            }
        }
    },

    
    syncRowHeights: function() {
        var me = this,
            i,
            lockedView = me.lockedGrid.getView(),
            normalView = me.normalGrid.getView(),
            lockedRowEls = lockedView.all.slice(),
            normalRowEls = normalView.all.slice(),
            ln = lockedRowEls.length,
            scrollTop;

        
        if (normalRowEls.length === ln) {

            
            for (i = 0; i < ln; i++) {
                normalView.syncRowHeights(lockedRowEls[i], normalRowEls[i]);
            }

            
            scrollTop = normalView.el.dom.scrollTop;
            normalView.el.dom.scrollTop = scrollTop;
            lockedView.el.dom.scrollTop = scrollTop;
        }
    },

    
    
    modifyHeaderCt: function() {
        var me = this;
        me.lockedGrid.headerCt.getMenuItems = me.getMenuItems(me.lockedGrid.headerCt.getMenuItems, true);
        me.normalGrid.headerCt.getMenuItems = me.getMenuItems(me.normalGrid.headerCt.getMenuItems, false);
        me.lockedGrid.headerCt.showMenuBy = Ext.Function.createInterceptor(me.lockedGrid.headerCt.showMenuBy, me.showMenuBy);
        me.normalGrid.headerCt.showMenuBy = Ext.Function.createInterceptor(me.normalGrid.headerCt.showMenuBy, me.showMenuBy);
    },

    onUnlockMenuClick: function() {
        this.unlock();
    },

    onLockMenuClick: function() {
        this.lock();
    },

    showMenuBy: function(t, header) {
        var menu = this.getMenu(),
            unlockItem  = menu.down('#unlockItem'),
            lockItem = menu.down('#lockItem'),
            sep = unlockItem.prev();

        if (header.lockable === false) {
            sep.hide();
            unlockItem.hide();
            lockItem.hide();
        } else {
            sep.show();
            unlockItem.show();
            lockItem.show();
            if (!unlockItem.initialConfig.disabled) {
                unlockItem.setDisabled(header.lockable === false);
            }
            if (!lockItem.initialConfig.disabled) {
                lockItem.setDisabled(!header.isLockable());
            }
        }
    },

    getMenuItems: function(getMenuItems, locked) {
        var me            = this,
            unlockText    = me.unlockText,
            lockText      = me.lockText,
            unlockCls     = Ext.baseCSSPrefix + 'hmenu-unlock',
            lockCls       = Ext.baseCSSPrefix + 'hmenu-lock',
            unlockHandler = Ext.Function.bind(me.onUnlockMenuClick, me),
            lockHandler   = Ext.Function.bind(me.onLockMenuClick, me);

        
        return function() {

            
            
            var o = getMenuItems.call(this);
            o.push('-', {
                itemId: 'unlockItem',
                cls: unlockCls,
                text: unlockText,
                handler: unlockHandler,
                disabled: !locked
            });
            o.push({
                itemId: 'lockItem',
                cls: lockCls,
                text: lockText,
                handler: lockHandler,
                disabled: locked
            });
            return o;
        };
    },

    
    syncLockedWidth: function() {
        var me = this,
            locked = me.lockedGrid,
            lockedView = locked.view,
            lockedViewEl = lockedView.el.dom,
            normal = me.normalGrid,
            lockedColCount = locked.headerCt.getVisibleGridColumns().length,
            normalColCount = normal.headerCt.getVisibleGridColumns().length;

        Ext.suspendLayouts();

        
        
        if (normalColCount) {
            normal.show();
            if (lockedColCount) {

                
                
                if (!locked.headerCt.forceFit) {
                    delete locked.flex;
                    
                    locked.setWidth(locked.headerCt.getFullWidth());
                }
                locked.addCls(me.lockedGridCls);
                locked.show();
            } else {
                
                
                
                locked.getView().refresh();
                locked.hide();
            }

            
            lockedView.el.setStyle(lockedView.getOverflowStyle());

            
            me.ignoreMousewheel = lockedView.scrollFlags.y;
        }

        
        
        else {
            normal.hide();
            
            
            lockedViewEl.style.borderBottomWidth = '0';

            
            locked.flex = 1;
            delete locked.width;
            locked.removeCls(me.lockedGridCls);
            locked.show();

            
            
            lockedView.el.setStyle(normal.view.getOverflowStyle());
            me.ignoreMousewheel = true;
        }
        Ext.resumeLayouts(true);
        return [lockedColCount, normalColCount];
    },

    onLockedHeaderAdd: function() {
        
        
        if (!this.ignoreAddLockedColumn) {
            this.syncLockedWidth();
        }
    },

    onLockedHeaderResize: function() {
        this.syncLockedWidth();
    },

    onLockedHeaderHide: function() {
        this.syncLockedWidth();
    },

    onLockedHeaderShow: function() {
        this.syncLockedWidth();
    },

    onLockedHeaderSortChange: function(headerCt, header, sortState) {
        if (sortState) {
            
            
            this.normalGrid.headerCt.clearOtherSortStates(null, true);
        }
    },

    onNormalHeaderSortChange: function(headerCt, header, sortState) {
        if (sortState) {
            
            
            this.lockedGrid.headerCt.clearOtherSortStates(null, true);
        }
    },

    
    
    lock: function(activeHd, toIdx) {
        var me         = this,
            normalGrid = me.normalGrid,
            lockedGrid = me.lockedGrid,
            normalHCt  = normalGrid.headerCt,
            lockedHCt  = lockedGrid.headerCt,
            refreshFlags,
            ownerCt;

        activeHd = activeHd || normalHCt.getMenu().activeHeader;
        ownerCt = activeHd.ownerCt;

        
        if (!activeHd.isLockable()) {
            return;
        }

        
        
        if (activeHd.flex) {
            activeHd.width = activeHd.getWidth();
            activeHd.flex = null;
        }

        Ext.suspendLayouts();
        ownerCt.remove(activeHd, false);
        activeHd.locked = true;

        
        me.ignoreAddLockedColumn = true;
        if (Ext.isDefined(toIdx)) {
            lockedHCt.insert(toIdx, activeHd);
        } else {
            lockedHCt.add(activeHd);
        }
        me.ignoreAddLockedColumn = false;

        refreshFlags = me.syncLockedWidth();
        if (refreshFlags[0]) {
            lockedGrid.getView().refresh();
        }
        if (refreshFlags[1]) {
            normalGrid.getView().refresh();
        }
        Ext.resumeLayouts(true);

        me.fireEvent('lockcolumn', me, activeHd);
    },

    
    
    unlock: function(activeHd, toIdx) {
        var me         = this,
            normalGrid = me.normalGrid,
            lockedGrid = me.lockedGrid,
            normalHCt  = normalGrid.headerCt,
            lockedHCt  = lockedGrid.headerCt,
            refreshFlags;

        
        if (!Ext.isDefined(toIdx)) {
            toIdx = 0;
        }
        activeHd = activeHd || lockedHCt.getMenu().activeHeader;

        Ext.suspendLayouts();
        activeHd.ownerCt.remove(activeHd, false);
        activeHd.locked = false;
        normalHCt.insert(toIdx, activeHd);

        
        
        refreshFlags = me.syncLockedWidth();

        if (refreshFlags[0]) {
            lockedGrid.getView().refresh();
        }
        if (refreshFlags[1]) {
            normalGrid.getView().refresh();
        }
        Ext.resumeLayouts(true);

        me.fireEvent('unlockcolumn', me, activeHd);
    },

    
    reconfigureLockable: function(store, columns) {
        var me = this,
            oldStore = me.store,
            lockedGrid = me.lockedGrid,
            normalGrid = me.normalGrid;

        Ext.suspendLayouts();
        if (columns) {
            lockedGrid.headerCt.removeAll();
            normalGrid.headerCt.removeAll();

            columns = me.processColumns(columns);

            
            me.ignoreAddLockedColumn = true;
            lockedGrid.headerCt.add(columns.locked.items);
            me.ignoreAddLockedColumn = false;
            normalGrid.headerCt.add(columns.normal.items);

            
            
            me.syncLockedWidth();
        }

        if (store && store !== oldStore) {
            store = Ext.data.StoreManager.lookup(store);
            me.store = store;
            lockedGrid.bindStore(store);
            normalGrid.bindStore(store);
        } else {
            lockedGrid.getView().refresh();
            normalGrid.getView().refresh();
        }
        Ext.resumeLayouts(true);
    },

    constructLockableFeatures: function() {
        var features = this.features,
            feature,
            featureClone,
            lockedFeatures,
            normalFeatures,
            i = 0, len;
        
        if (features) {
            lockedFeatures = [];
            normalFeatures = [];
            len = features.length;
            for (; i < len; i++) {
                feature = features[i];
                if (!feature.isFeature) {
                    feature = Ext.create('feature.' + feature.ftype, feature);
                }
                switch (feature.lockableScope) {
                    case 'locked':
                        lockedFeatures.push(feature);
                        break;
                    case 'normal':
                        normalFeatures.push(feature);
                        break;
                    default:
                        feature.lockableScope = 'both';
                        lockedFeatures.push(feature);
                        normalFeatures.push(featureClone = feature.clone());

                        
                        featureClone.lockingPartner = feature;
                        feature.lockingPartner = featureClone;
                }
            }
        }
        return {
            normalFeatures: normalFeatures,
            lockedFeatures: lockedFeatures
        };
    },

    constructLockablePlugins: function() {
        var plugins = this.plugins,
            plugin,
            normalPlugin,
            lockedPlugin,
            topPlugins,
            lockedPlugins,
            normalPlugins,
            i = 0, len;

        if (plugins) {
            topPlugins = [];
            lockedPlugins = [];
            normalPlugins = [];
            len = plugins.length;
            for (; i < len; i++) {
                
                plugin = plugins[i];

                switch (plugin.lockableScope) {
                    case 'both':
                        lockedPlugins.push(lockedPlugin = plugin.clonePlugin());
                        normalPlugins.push(normalPlugin = plugin.clonePlugin());

                        
                        lockedPlugin.lockingPartner = normalPlugin;
                        normalPlugin.lockingPartner = lockedPlugin;
                        
                        
                        
                        Ext.destroy(plugin);
                        break;
                    case 'locked':
                        lockedPlugins.push(plugin);
                        break;
                    case 'normal':
                        normalPlugins.push(plugin);
                        break;
                    default:
                        topPlugins.push(plugin);
                }
            }
        }
        return {
            topPlugins:    topPlugins,
            normalPlugins: normalPlugins,
            lockedPlugins: lockedPlugins
        };
    },
    
    destroyLockable: function(){
        
        Ext.destroy(this.view);
    }
}, function() {
    this.borrow(Ext.AbstractComponent, ['constructPlugin']);
});


Ext.define('Ext.tree.View', {
    extend:  Ext.view.Table ,
    alias: 'widget.treeview',

               
                            
      

    
    isTreeView: true,

    loadingCls: Ext.baseCSSPrefix + 'grid-tree-loading',
    expandedCls: Ext.baseCSSPrefix + 'grid-tree-node-expanded',
    leafCls: Ext.baseCSSPrefix + 'grid-tree-node-leaf',

    expanderSelector: '.' + Ext.baseCSSPrefix + 'tree-expander',
    checkboxSelector: '.' + Ext.baseCSSPrefix + 'tree-checkbox',
    expanderIconOverCls: Ext.baseCSSPrefix + 'tree-expander-over',

    
    
    
    nodeAnimWrapCls: Ext.baseCSSPrefix + 'tree-animator-wrap',

    blockRefresh: true,

    
    loadMask: false,

    
    rootVisible: true,

    
    deferInitialRefresh: false,

    

    expandDuration: 250,
    collapseDuration: 250,

    toggleOnDblClick: true,

    stripeRows: false,

    
    uiFields: ['expanded', 'loaded', 'checked', 'expandable', 'leaf', 'icon', 'iconCls', 'loading', 'qtip', 'qtitle'],

    
    treeRowTpl: [
        '{%',
            'this.processRowValues(values);',
            'this.nextTpl.applyOut(values, out, parent);',
            'delete values.rowAttr["data-qtip"];',
            'delete values.rowAttr["data-qtitle"];',
        '%}', {
            priority: 10,
            processRowValues: function(rowValues) {
                var record = rowValues.record,
                    view = rowValues.view,
                    qtip = record.get('qtip'),
                    qtitle = record.get('qttle');

                rowValues.rowAttr = {};
                if (qtip) {
                    rowValues.rowAttr['data-qtip'] = qtip;
                }
                if (qtitle) {
                    rowValues.rowAttr['data-qtitle'] = qtitle;
                }
                if (record.isExpanded()) {
                    rowValues.rowClasses.push(view.expandedCls);
                }
                if (record.isLeaf()) {
                    rowValues.rowClasses.push(view.leafCls);
                }
                if (record.isLoading()) {
                    rowValues.rowClasses.push(view.loadingCls);
                }
            }
        }
    ],

    initComponent: function() {
        var me = this,
            treeStore = me.panel.getStore(),
            store = me.store;

        if (me.initialConfig.animate === undefined) {
            me.animate = Ext.enableFx;
        }

        if (!store || store === treeStore) {
            me.store = store = new Ext.data.NodeStore({
                treeStore: treeStore,
                recursive: true,
                rootVisible: me.rootVisible
            });
        }

        if (me.node) {
            me.setRootNode(me.node);
        }
        me.animQueue = {};
        me.animWraps = {};
        me.addEvents(
            
            'afteritemexpand',
            
            'afteritemcollapse',
            
            'nodedragover'
        );
        me.callParent(arguments);
        me.addRowTpl(Ext.XTemplate.getTpl(me, 'treeRowTpl'));
    },

    onBeforeFill: function(treeStore, fillRoot) {
        this.store.suspendEvents();
    },

    onFillComplete: function(treeStore, fillRoot, newNodes) {
        var me = this,
            store = me.store,
            start = store.indexOf(newNodes[0]);

        store.resumeEvents();

        
        
        fillRoot.triggerUIUpdate();

        
        
        if (!newNodes.length || start === -1) {
            return;
        }

        
        me.onAdd(me.store, newNodes, start);

        me.refreshPartner();
    },

    onBeforeSort: function() {
        this.store.suspendEvents(); 
    },

    onSort: function(o) {
        
        
        if (o.isStore) {
            this.store.resumeEvents();
            this.refresh();
            this.refreshPartner();
        }
    },

    refreshPartner: function() {
        var partner = this.lockingPartner;
        if (partner) {
            partner.refresh();
        }
    },

    getMaskStore: function() {
        return this.panel.getStore();
    },

    afterRender: function() {
        var me = this;
        me.callParent(arguments);

        me.el.on({
            scope: me,
            delegate: me.expanderSelector,
            mouseover: me.onExpanderMouseOver,
            mouseout: me.onExpanderMouseOut,
            click: {
                delegate: me.checkboxSelector,
                fn: me.onCheckboxChange,
                scope: me
            }
        });
    },

    afterComponentLayout: function() {
        this.callParent(arguments);
        var stretcher = this.stretcher;
        if (stretcher) {
            stretcher.setWidth((this.getWidth() - Ext.getScrollbarSize().width));
        }
    },

    processUIEvent: function(e) {
        
        
        
        if (e.getTarget('.' + this.nodeAnimWrapCls, this.el)) {
            return false;
        }
        return this.callParent(arguments);
    },

    onClear: function() {
        this.store.removeAll();
    },

    setRootNode: function(node) {
        var me = this;
        me.store.setNode(node);
        me.node = node;
    },

    onCheckboxChange: function(e, t) {
        var me = this,
            item = e.getTarget(me.getItemSelector(), me.getTargetEl());

        if (item) {
            me.onCheckChange(me.getRecord(item));
        }
    },

    onCheckChange: function(record) {
        var checked = record.get('checked');
        if (Ext.isBoolean(checked)) {
            checked = !checked;
            record.set('checked', checked);
            this.fireEvent('checkchange', record, checked);
        }
    },

    getChecked: function() {
        var checked = [];
        this.node.cascadeBy(function(rec){
            if (rec.get('checked')) {
                checked.push(rec);
            }
        });
        return checked;
    },

    isItemChecked: function(rec) {
        return rec.get('checked');
    },

    
    createAnimWrap: function(record, index) {
        var me = this,
            node = me.getNode(record),
            tmpEl, nodeEl,
            columnSizer = [];

        me.renderColumnSizer(columnSizer);
        nodeEl = Ext.get(node);
        tmpEl = nodeEl.insertSibling({
            tag: 'tr',
            html: [
                '<td colspan="' + me.panel.headerCt.getColumnCount() + '">',
                    '<div class="' + me.nodeAnimWrapCls + '">',
                        
                        '<table class="' + Ext.baseCSSPrefix + me.id + '-table ' + Ext.baseCSSPrefix + 'grid-table" style="border:0" cellspacing="0" cellpadding="0">',
                        columnSizer.join(''),
                        '<tbody></tbody></table>',
                    '</div>',
                '</td>'
            ].join('')
        }, 'after');

        return {
            record: record,
            node: node,
            el: tmpEl,
            expanding: false,
            collapsing: false,
            animating: false,
            animateEl: tmpEl.down('div'),
            targetEl: tmpEl.down('tbody')
        };
    },

    
    getAnimWrap: function(parent, bubble) {
        if (!this.animate) {
            return null;
        }

        var wraps = this.animWraps,
            wrap = wraps[parent.internalId];

        if (bubble !== false) {
            while (!wrap && parent) {
                parent = parent.parentNode;
                if (parent) {
                    wrap = wraps[parent.internalId];
                }
            }
        }
        return wrap;
    },

    doAdd: function(records, index) {
        
        
        var me = this,
            nodes = me.bufferRender(records, index, true),
            record = records[0],
            parent = record.parentNode,
            all = me.all,
            relativeIndex,
            animWrap = me.getAnimWrap(parent),
            targetEl, children, len;

        if (!animWrap || !animWrap.expanding) {
            return me.callParent(arguments);
        }

        
        parent = animWrap.record;

        
        targetEl = animWrap.targetEl;
        children = targetEl.dom.childNodes;
        len = children.length;

        
        relativeIndex = index - me.indexInStore(parent) - 1;

        
        
        if (!len || relativeIndex >= len) {
            targetEl.appendChild(nodes);
        }
        
        
        else {
            Ext.fly(children[relativeIndex]).insertSibling(nodes, 'before', true);
        }

        
        all.insert(index, nodes);

        
        
        if (animWrap.isAnimating) {
            me.onExpand(parent);
        }
    },

    onRemove : function(ds, records, indexes) {
        var me = this,
            empty, i;

        if (me.viewReady) {
            empty = me.store.getCount() === 0;

            
            if (empty) {
                me.refresh();
            }
            else {
                
                for (i = indexes.length - 1; i >= 0; --i) {
                    me.doRemove(records[i], indexes[i]);
                }
            }

            
            if (me.hasListeners.itemremove) {
                for (i = indexes.length - 1; i >= 0; --i) {
                    me.fireEvent('itemremove', records[i], indexes[i]);
                }
            }
        }
    },

    doRemove: function(record, index) {
        
        
        var me = this,
            all = me.all,
            animWrap = me.getAnimWrap(record),
            item = all.item(index),
            node = item ? item.dom : null;

        if (!node || !animWrap || !animWrap.collapsing) {
            return me.callParent(arguments);
        }

        
        
        animWrap.targetEl.dom.insertBefore(node, animWrap.targetEl.dom.firstChild);
        all.removeElement(index);
    },

    onBeforeExpand: function(parent, records, index) {
        var me = this,
            animWrap;

        if (me.rendered && me.all.getCount() && me.animate) {
            if (me.getNode(parent)) {
                animWrap = me.getAnimWrap(parent, false);
                if (!animWrap) {
                    animWrap = me.animWraps[parent.internalId] = me.createAnimWrap(parent);
                    animWrap.animateEl.setHeight(0);
                }
                else if (animWrap.collapsing) {
                    
                    
                    animWrap.targetEl.select(me.itemSelector).remove();
                }
                animWrap.expanding = true;
                animWrap.collapsing = false;
            }
        }
    },

    onExpand: function(parent) {
        var me = this,
            queue = me.animQueue,
            id = parent.getId(),
            node = me.getNode(parent),
            index = node ? me.indexOf(node) : -1,
            animWrap,
            animateEl,
            targetEl,
            fromHeight = Ext.isIEQuirks ? 1 : 0 

        if (me.singleExpand) {
            me.ensureSingleExpand(parent);
        }

        
        if (index === -1) {
            return;
        }

        animWrap = me.getAnimWrap(parent, false);

        if (!animWrap) {
            parent.isExpandingOrCollapsing = false;
            me.fireEvent('afteritemexpand', parent, index, node);
            me.refreshSize();
            return;
        }

        animateEl = animWrap.animateEl;
        targetEl = animWrap.targetEl;

        animateEl.stopAnimation();
        queue[id] = true;

        
        animateEl.dom.style.height = fromHeight + 'px';
        animateEl.animate({
            from: {
                height: fromHeight
            },
            to: {
                height: targetEl.getHeight()
            },
            duration: me.expandDuration,
            listeners: {
                afteranimate: function() {
                    
                    
                    
                    var items = targetEl.query(me.itemSelector);
                    if (items.length) {
                        animWrap.el.insertSibling(items, 'before', true);
                    }
                    animWrap.el.remove();
                    me.refreshSize();
                    delete me.animWraps[animWrap.record.internalId];
                    delete queue[id];
                }
            },
            callback: function() {
                parent.isExpandingOrCollapsing = false;
                me.fireEvent('afteritemexpand', parent, index, node);
            }
        });

        animWrap.isAnimating = true;
    },

    
    onBeforeCollapse: function(parent, records, index, callback, scope) {
        var me = this,
            animWrap;

        if (me.rendered && me.all.getCount()) {
            if (me.animate) {
                
                
                
                if (Ext.Array.contains(parent.stores, me.store)) {
                    animWrap = me.getAnimWrap(parent);
                    if (!animWrap) {
                        animWrap = me.animWraps[parent.internalId] = me.createAnimWrap(parent, index);
                    }
                    else if (animWrap.expanding) {
                        
                        
                        animWrap.targetEl.select(this.itemSelector).remove();
                    }
                    animWrap.expanding = false;
                    animWrap.collapsing = true;
                    animWrap.callback = callback;
                    animWrap.scope = scope;
                }
            } else {
                
                me.onCollapseCallback = callback;
                me.onCollapseScope = scope;
            }
        }
    },

    onCollapse: function(parent) {
        var me = this,
            queue = me.animQueue,
            id = parent.getId(),
            node = me.getNode(parent),
            index = node ? me.indexOf(node) : -1,
            animWrap = me.getAnimWrap(parent),
            animateEl;

        
        
        
        if (!me.all.getCount() || !Ext.Array.contains(parent.stores, me.store)) {
            return;
        }

        
        if (!animWrap) {
            parent.isExpandingOrCollapsing = false;
            me.fireEvent('afteritemcollapse', parent, index, node);
            me.refreshSize();

            
            Ext.callback(me.onCollapseCallback, me.onCollapseScope);
            me.onCollapseCallback = me.onCollapseScope = null;
            return;
        }

        animateEl = animWrap.animateEl;

        queue[id] = true;

        animateEl.stopAnimation();
        animateEl.animate({
            to: {
                height: Ext.isIEQuirks ? 1 : 0 
            },
            duration: me.collapseDuration,
            listeners: {
                afteranimate: function() {
                    
                    animWrap.el.remove();
                    me.refreshSize();
                    delete me.animWraps[animWrap.record.internalId];
                    delete queue[id];
                }
            },
            callback: function() {
                parent.isExpandingOrCollapsing = false;
                me.fireEvent('afteritemcollapse', parent, index, node);

                
                Ext.callback(animWrap.callback, animWrap.scope);
                animWrap.callback = animWrap.scope = null;
            }
        });
        animWrap.isAnimating = true;
    },

    
    isAnimating: function(node) {
        return !!this.animQueue[node.getId()];
    },

    
    expand: function(record, deep, callback, scope) {
        var me = this,
            doAnimate = !!me.animate,
            result;

        
        if (!doAnimate || !record.isExpandingOrCollapsing) {
            if (!record.isLeaf()) {
                record.isExpandingOrCollapsing = doAnimate;
            }

            
            
            
            Ext.suspendLayouts();
            result = record.expand(deep, callback, scope);
            Ext.resumeLayouts(true);
            return result;
        }
    },

    
    collapse: function(record, deep, callback, scope) {
        var me = this,
            doAnimate = !!me.animate;

        
        if (!doAnimate || !record.isExpandingOrCollapsing) {
            if (!record.isLeaf()) {
                record.isExpandingOrCollapsing = doAnimate;
            }
            return record.collapse(deep, callback, scope);
        }
    },

    
    toggle: function(record, deep, callback, scope) {
        if (record.isExpanded()) {
            this.collapse(record, deep, callback, scope);
        } else {
            this.expand(record, deep, callback, scope);
        }
    },

    onItemDblClick: function(record, item, index) {
        var me = this,
            editingPlugin = me.editingPlugin;
            
        me.callParent(arguments);
        if (me.toggleOnDblClick && record.isExpandable() && !(editingPlugin && editingPlugin.clicksToEdit === 2)) {
            me.toggle(record);
        }
    },

    onBeforeItemMouseDown: function(record, item, index, e) {
        if (e.getTarget(this.expanderSelector, item)) {
            return false;
        }
        return this.callParent(arguments);
    },

    onItemClick: function(record, item, index, e) {
        if (e.getTarget(this.expanderSelector, item) && record.isExpandable()) {
            this.toggle(record, e.ctrlKey);
            return false;
        }
        return this.callParent(arguments);
    },

    onExpanderMouseOver: function(e, t) {
        e.getTarget(this.cellSelector, 10, true).addCls(this.expanderIconOverCls);
    },

    onExpanderMouseOut: function(e, t) {
        e.getTarget(this.cellSelector, 10, true).removeCls(this.expanderIconOverCls);
    },
    
    getStoreListeners: function(){
        var me = this,
            listeners = me.callParent(arguments);
            
        return Ext.apply(listeners, {
            beforeexpand: me.onBeforeExpand,
            expand: me.onExpand,
            beforecollapse: me.onBeforeCollapse,
            collapse: me.onCollapse,
            write: me.onStoreWrite,
            datachanged: me.onStoreDataChanged
        });    
    },
    
    onBindStore: function(){
        var me = this,
            treeStore = me.getTreeStore();
        
        me.callParent(arguments);
        
        me.mon(treeStore, {
            scope: me,
            beforefill: me.onBeforeFill,
            fillcomplete: me.onFillComplete
        });

        if (!treeStore.remoteSort) {
            
            
            me.mon(treeStore, {
                scope: me,
                beforesort: me.onBeforeSort,
                sort: me.onSort
            });
        }
    },
    
    onUnbindStore: function(){
        var me = this,
            treeStore = me.getTreeStore();
            
        me.callParent(arguments);
        
        me.mun(treeStore, {
            scope: me,
            beforefill: me.onBeforeFill,
            fillcomplete: me.onFillComplete
        });

        if (!treeStore.remoteSort) {
            me.mun(treeStore, {
                scope: me,
                beforesort: me.onBeforeSort,
                sort: me.onSort
            });
        }
    },

    
    getTreeStore: function() {
        return this.panel.store;
    },

    ensureSingleExpand: function(node) {
        var parent = node.parentNode;
        if (parent) {
            parent.eachChild(function(child) {
                if (child !== node && child.isExpanded()) {
                    child.collapse();
                }
            });
        }
    },

    shouldUpdateCell: function(record, column, changedFieldNames){
        if (changedFieldNames) {
            var i = 0,
                len = changedFieldNames.length;

            for (; i < len; ++i) {
                if (Ext.Array.contains(this.uiFields, changedFieldNames[i])) {
                    return true;
                }
            }
        }
        return this.callParent(arguments);
    },

    
    onStoreWrite: function(store, operation) {
        var treeStore = this.panel.store;
        treeStore.fireEvent('write', treeStore, operation);
    },

    
    onStoreDataChanged: function(store, operation) {
        var treeStore = this.panel.store;
        treeStore.fireEvent('datachanged', treeStore);
    }
});


Ext.define('Ext.grid.plugin.BufferedRendererTreeView', {
    override: 'Ext.tree.View',

    onRemove: function(store, records, indices) {
        var me = this;
        
        
        if (me.rendered && me.bufferedRenderer) {
            me.refreshView();
        }
        
        else {
            me.callParent([store, records, indices]);
        }
    }    
});


Ext.define('Ext.grid.plugin.BufferedRenderer', {
    extend:  Ext.AbstractPlugin ,
               
                                                    
                                                  
      
    alias: 'plugin.bufferedrenderer',
    lockableScope: 'both',

    
    percentageFromEdge: 0.35,

    
    variableRowHeight: false,

    
    numFromEdge: 8,

    
    trailingBufferZone: 10,

    
    leadingBufferZone: 20,

    
    synchronousRender: true,

    
    scrollToLoadBuffer: 200,

    
    viewSize: 0,
    
    rowHeight: 21,
    
    position: 0,
    lastScrollDirection: 1,
    bodyTop: 0,

    
    init: function(grid) {
        var me = this,
            view = grid.view,
            viewListeners = {
                scroll: {
                    fn: me.onViewScroll,
                    element: 'el',
                    scope: me
                },
                boxready: me.onViewResize,
                resize: me.onViewResize,
                refresh: me.onViewRefresh,
                scope: me,
                destroyable: true
            };

        
        if (!me.variableRowHeight && grid.ownerLockable) {
            grid.ownerLockable.syncRowHeight = false;
        }

        
        
        if (grid.isTree || grid.ownerLockable && grid.ownerLockable.isTree) {
            view.blockRefresh = false;
            view.loadMask = true;
        }
        if (view.positionBody) {
            viewListeners.refresh = me.onViewRefresh;
        }
        me.grid = grid;
        me.view = view;
        view.bufferedRenderer = me;
        view.preserveScrollOnRefresh = true;

        me.bindStore(view.dataSource);
        view.getViewRange = function() {
            return me.getViewRange();
        };

        me.position = 0;

        me.gridListeners = grid.on('reconfigure', me.onReconfigure, me);
        me.viewListeners = view.on(viewListeners);
    },

    bindStore: function(store) {
        var me = this;
        if (me.store) {
            me.unbindStore();
        }
        me.storeListeners = store.on({
            scope: me,
            clear: me.onStoreClear,
            destroyable: true
        });
        me.store = store;

        
        if (me.view.componentLayout.layoutCount) {
            me.onViewResize(me.view, 0, me.view.getHeight());
        }
    },

    onReconfigure: function(grid, store){
        if (store && store !== this.store) {
            this.bindStore(store);
        }
    },

    unbindStore: function() {
        this.storeListeners.destroy();
        this.store = null;
    },

    onStoreClear: function() {
        var me = this;

        
        if (me.view.rendered && !me.store.isDestroyed) {
            
            
            if (me.scrollTop !== 0) {
                me.ignoreNextScrollEvent = true;
                me.view.el.dom.scrollTop = me.bodyTop = me.scrollTop = 0;
            }

            me.position = me.scrollHeight = 0;
            me.lastScrollDirection = me.scrollOffset = null;

            
            delete me.rowHeight;
        }
    },

    onViewRefresh: function() {
        var me = this,
            view = me.view,
            oldScrollHeight = me.scrollHeight,
            scrollHeight;

        
        if (view.all.getCount()) {
            
            
            delete me.rowHeight;
        }

        
        
        scrollHeight = me.getScrollHeight();

        if (!oldScrollHeight || scrollHeight != oldScrollHeight) {
            me.stretchView(view, scrollHeight);
        }

        if (me.scrollTop !== view.el.dom.scrollTop) {
            
            
            
            me.onViewScroll();    
        } else {
            me.setBodyTop(me.bodyTop);

            
            if (view.all.getCount()) {
                me.viewSize = 0;
                me.onViewResize(view, null, view.getHeight());
            }
        }
    },

    onViewResize: function(view, width, height, oldWidth, oldHeight) {
        
        if (!oldHeight || height !== oldHeight) {
            var me = this,
                newViewSize;

            
            newViewSize = Math.ceil(height / me.rowHeight) + me.trailingBufferZone + me.leadingBufferZone;
            me.viewSize = me.setViewSize(newViewSize);
        }
    },
 
    stretchView: function(view, scrollRange) {
        var me = this,
            recordCount = (me.store.buffered ? me.store.getTotalCount() : me.store.getCount());

        if (me.stretcher) {
            me.stretcher.dom.style.marginTop = (scrollRange - 1) + 'px';
        } else {
            var el = view.el;

            
            
            if (view.refreshCounter) {
                view.fixedNodes++;
            }

            
            if (recordCount && (me.view.all.endIndex === recordCount - 1)) {
                scrollRange = me.bodyTop + view.body.dom.offsetHeight;
            }
            this.stretcher = el.createChild({
                style: {
                    width: '1px',
                    height: '1px',
                    'marginTop': (scrollRange - 1) + 'px',
                    left: 0,
                    position: 'absolute'
                }
            }, el.dom.firstChild);
        }
    },

    setViewSize: function(viewSize) {
        if (viewSize !== this.viewSize) {

            
            this.scrollTop = this.view.el.dom.scrollTop;

            var me = this,
                store = me.store,
                elCount = me.view.all.getCount(),
                start, end,
                lockingPartner = me.lockingPartner;

                me.viewSize = store.viewSize = viewSize;

            
            
            if (elCount) {
                start = me.view.all.startIndex;
                    end = Math.min(start + viewSize - 1, (store.buffered ? store.getTotalCount() : store.getCount()) - 1);

                
                if (lockingPartner) {
                    lockingPartner.disable();
                }
                me.renderRange(start, end);
                if (lockingPartner) {
                    lockingPartner.enable();
                }
            }
        }
        return viewSize;
    },

    getViewRange: function() {
        var me = this,
            rows = me.view.all,
            store = me.store;

        if (store.data.getCount()) {
            return store.getRange(rows.startIndex, rows.startIndex + (me.viewSize || me.store.defaultViewSize) - 1);
        } else {
            return [];
        }
    },

    
    scrollTo: function(recordIdx, doSelect, callback, scope) {
        var me = this,
            view = me.view,
            viewDom = view.el.dom,
            store = me.store,
            total = store.buffered ? store.getTotalCount() : store.getCount(),
            startIdx, endIdx,
            targetRec,
            targetRow,
            tableTop;

        
        recordIdx = Math.min(Math.max(recordIdx, 0), total - 1);

        
        startIdx = Math.max(Math.min(recordIdx - ((me.leadingBufferZone + me.trailingBufferZone) / 2), total - me.viewSize + 1), 0);
        tableTop = startIdx * me.rowHeight;
        endIdx = Math.min(startIdx + me.viewSize - 1, total - 1);

        store.getRange(startIdx, endIdx, {
            callback: function(range, start, end) {

                me.renderRange(start, end, true);

                targetRec = store.data.getRange(recordIdx, recordIdx)[0];
                targetRow = view.getNode(targetRec, false);
                view.body.dom.style.top = tableTop + 'px';
                me.position = me.scrollTop = viewDom.scrollTop = tableTop = Math.min(Math.max(0, tableTop - view.body.getOffsetsTo(targetRow)[1]), viewDom.scrollHeight - viewDom.clientHeight);

                
                if (Ext.isIE) {
                    viewDom.scrollTop = tableTop;
                }
                if (doSelect) {
                    view.selModel.select(targetRec);
                }
                if (callback) {
                    callback.call(scope||me, recordIdx, targetRec);
                }
            }
        });
    },

    onViewScroll: function(e, t) {
        var me = this,
            store = me.store,
            totalCount = (store.buffered ? store.getTotalCount() : store.getCount()),
            vscrollDistance,
            scrollDirection,
            scrollTop = me.scrollTop = me.view.el.dom.scrollTop,
            scrollHandled = false;

        
        
        if (me.ignoreNextScrollEvent) {
            me.ignoreNextScrollEvent = false;
            return;
        }

        
        
        if (!(me.disabled || totalCount < me.viewSize)) {

            vscrollDistance = scrollTop - me.position;
            scrollDirection = vscrollDistance > 0 ? 1 : -1;

            
            if (Math.abs(vscrollDistance) >= 20 || (scrollDirection !== me.lastScrollDirection)) {
                me.lastScrollDirection = scrollDirection;
                me.handleViewScroll(me.lastScrollDirection);
                scrollHandled = true;
            }
        }

        
        if (!scrollHandled) {
            if (me.lockingPartner && me.lockingPartner.scrollTop !== scrollTop) {
                me.lockingPartner.view.el.dom.scrollTop = scrollTop;
            }
        }
    },

    handleViewScroll: function(direction) {
        var me                = this,
            rows              = me.view.all,
            store             = me.store,
            viewSize          = me.viewSize,
            totalCount        = (store.buffered ? store.getTotalCount() : store.getCount()),
            requestStart,
            requestEnd;

        
        if (direction == -1) {

            
            if (rows.startIndex) {
                if ((me.getFirstVisibleRowIndex() - rows.startIndex) < me.numFromEdge) {
                    requestStart = Math.max(0, me.getLastVisibleRowIndex() + me.trailingBufferZone - viewSize);
                }
            }
        }
        
        else {

            
            if (rows.endIndex < totalCount - 1) {
                if ((rows.endIndex - me.getLastVisibleRowIndex()) < me.numFromEdge) {
                    requestStart = Math.max(0, me.getFirstVisibleRowIndex() - me.trailingBufferZone);
                }
            }
        }

        
        if (requestStart != null) {
            requestEnd = Math.min(requestStart + viewSize - 1, totalCount - 1);

            
            if (requestStart !== rows.startIndex || requestEnd !== rows.endIndex) {
                me.renderRange(requestStart, requestEnd);
                return;
            }
        }

        
        if (me.lockingPartner && me.lockingPartner.view.el && me.lockingPartner.scrollTop !== me.scrollTop) {
            me.lockingPartner.view.el.dom.scrollTop = me.scrollTop;
        }
    },

    renderRange: function(start, end, forceSynchronous) {
        var me = this,
            store = me.store;

        
        if (store.rangeCached(start, end)) {
            me.cancelLoad();

            if (me.synchronousRender || forceSynchronous) {
                me.onRangeFetched(null, start, end);
            } else {
                if (!me.renderTask) {
                    me.renderTask = new Ext.util.DelayedTask(me.onRangeFetched, me, null, false);
                }
                
                
                
                
                me.renderTask.delay(1, null, null, [null, start, end]);
            }
        }

        
        else {
            me.attemptLoad(start, end);
        }
    },

    onRangeFetched: function(range, start, end, fromLockingPartner) {
        var me = this,
            view = me.view,
            oldStart,
            rows = view.all,
            removeCount,
            increment = 0,
            calculatedTop = start * me.rowHeight,
            top,
            lockingPartner = me.lockingPartner;

        
        if (view.isDestroyed) {
            return;
        }

        
        if (!range) {
            range = me.store.getRange(start, end);

            
            if (!range) {
                return;
            }
        }

        
        if (start > rows.endIndex || end < rows.startIndex) {
            rows.clear(true);
            top = calculatedTop;
        }

        if (!rows.getCount()) {
            view.doAdd(range, start);
        }
        
        else if (end > rows.endIndex) {
            removeCount = Math.max(start - rows.startIndex, 0);

            
            if (me.variableRowHeight) {
                increment = rows.item(rows.startIndex + removeCount, true).offsetTop;
            }
            rows.scroll(Ext.Array.slice(range, rows.endIndex + 1 - start), 1, removeCount, start, end);

            
            if (me.variableRowHeight) {
                
                top = me.bodyTop + increment;
            } else {
                top = calculatedTop;
            }
        }
        
        else {
            removeCount = Math.max(rows.endIndex - end, 0);
            oldStart = rows.startIndex;
            rows.scroll(Ext.Array.slice(range, 0, rows.startIndex - start), -1, removeCount, start, end);

            
            if (me.variableRowHeight) {
                
                top = me.bodyTop - rows.item(oldStart, true).offsetTop;
            } else {
                top = calculatedTop;
            }
        }
        
        
        me.position = me.scrollTop;

        
        
        if (view.positionBody) {
            me.setBodyTop(top, calculatedTop);
        }

        
        
        if (lockingPartner && !lockingPartner.disabled && !fromLockingPartner) {
            lockingPartner.onRangeFetched(range, start, end, true);
            if (lockingPartner.scrollTop !== me.scrollTop) {
                lockingPartner.view.el.dom.scrollTop = me.scrollTop;
            }
        }
    },

    setBodyTop: function(bodyTop, calculatedTop) {
        var me = this,
            view = me.view,
            store = me.store,
            body = view.body.dom,
            delta;

        bodyTop = Math.floor(bodyTop);

        
        
        
        if (calculatedTop !== undefined) {
            delta = bodyTop - calculatedTop;
            bodyTop = calculatedTop;
        }
        body.style.position = 'absolute';
        body.style.top = (me.bodyTop = bodyTop) + 'px';

        
        
        if (delta) {
            me.scrollTop = me.position = view.el.dom.scrollTop -= delta;
        }

        
        if (view.all.endIndex === (store.buffered ? store.getTotalCount() : store.getCount()) - 1) {
            me.stretchView(view, me.bodyTop + body.offsetHeight);
        }
    },

    getFirstVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) {
        var me = this,
            view = me.view,
            rows = view.all,
            elements = rows.elements,
            clientHeight = view.el.dom.clientHeight,
            target,
            targetTop;

        
        if (rows.getCount() && me.variableRowHeight) {
            if (!arguments.length) {
                startRow = rows.startIndex;
                endRow = rows.endIndex;
                viewportTop = me.scrollTop;
                viewportBottom = viewportTop + clientHeight;

                
                if (me.bodyTop > viewportBottom || me.bodyTop + view.body.getHeight() < viewportTop) {
                    return Math.floor(me.scrollTop / me.rowHeight);
                }

                
                target = startRow + Math.min(me.numFromEdge + ((me.lastScrollDirection == -1) ? me.leadingBufferZone : me.trailingBufferZone), Math.floor((endRow - startRow) / 2));
            } else {
                target = startRow + Math.floor((endRow - startRow) / 2);
            }
            targetTop = me.bodyTop + elements[target].offsetTop;

            
            if (targetTop + elements[target].offsetHeight < viewportTop) {
                return me.getFirstVisibleRowIndex(target + 1, endRow, viewportTop, viewportBottom);
            }
            
            
            if (targetTop <= viewportTop) {
                return target;
            }
            
            else if (target !== startRow) {
                return me.getFirstVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom);
            }
        }
        return Math.floor(me.scrollTop / me.rowHeight);
    },

    getLastVisibleRowIndex: function(startRow, endRow, viewportTop, viewportBottom) {
        var me = this,
            view = me.view,
            rows = view.all,
            elements = rows.elements,
            clientHeight = view.el.dom.clientHeight,
            target,
            targetTop, targetBottom;

        
        if (rows.getCount() && me.variableRowHeight) {
            if (!arguments.length) {
                startRow = rows.startIndex;
                endRow = rows.endIndex;
                viewportTop = me.scrollTop;
                viewportBottom = viewportTop + clientHeight;

                
                if (me.bodyTop > viewportBottom || me.bodyTop + view.body.getHeight() < viewportTop) {
                    return Math.floor(me.scrollTop / me.rowHeight) + Math.ceil(clientHeight / me.rowHeight);
                }

                
                target = endRow - Math.min(me.numFromEdge + ((me.lastScrollDirection == 1) ? me.leadingBufferZone : me.trailingBufferZone), Math.floor((endRow - startRow) / 2));
            } else {
                target = startRow + Math.floor((endRow - startRow) / 2);
            }
            targetTop = me.bodyTop + elements[target].offsetTop;

            
            if (targetTop > viewportBottom) {
                return me.getLastVisibleRowIndex(startRow, target - 1, viewportTop, viewportBottom);
            }
            targetBottom = targetTop + elements[target].offsetHeight;

            
            if (targetBottom >= viewportBottom) {
                return target;
            }
            
            else if (target !== endRow) {
                return me.getLastVisibleRowIndex(target + 1, endRow, viewportTop, viewportBottom);
            }
        }
        return me.getFirstVisibleRowIndex() + Math.ceil(clientHeight / me.rowHeight);
    },

    getScrollHeight: function() {
        var me = this,
            view   = me.view,
            store  = me.store,
            doCalcHeight = !me.hasOwnProperty('rowHeight'),
            storeCount = me.store.getCount();

        if (!storeCount) {
            return 0;
        }
        if (doCalcHeight) {
            if (view.all.getCount()) {
                me.rowHeight = Math.floor(view.body.getHeight() / view.all.getCount());
            }
        }
        return this.scrollHeight = Math.floor((store.buffered ? store.getTotalCount() : store.getCount()) * me.rowHeight);
    },

    attemptLoad: function(start, end) {
        var me = this;
        if (me.scrollToLoadBuffer) {
            if (!me.loadTask) {
                me.loadTask = new Ext.util.DelayedTask(me.doAttemptLoad, me, []);
            }
            me.loadTask.delay(me.scrollToLoadBuffer, me.doAttemptLoad, me, [start, end]);
        } else {
            me.store.getRange(start, end, {
                callback: me.onRangeFetched,
                scope: me,
                fireEvent: false
            });
        }
    },

    cancelLoad: function() {
        if (this.loadTask) {
            this.loadTask.cancel();
        }
    },

    doAttemptLoad:  function(start, end) {
        this.store.getRange(start, end, {
            callback: this.onRangeFetched,
            scope: this,
            fireEvent: false
        });
    },

    destroy: function() {
        var me = this,
            view = me.view;

        if (view && view.el) {
            view.el.un('scroll', me.onViewScroll, me); 
        }

        
        Ext.destroy(me.viewListeners, me.storeListeners, me.gridListeners);
    }
});


Ext.define('Ext.grid.plugin.Editing', {
    alias: 'editing.editing',
    extend:  Ext.AbstractPlugin ,

               
                                 
                         
      

    mixins: {
        observable:  Ext.util.Observable 
    },

    
    clicksToEdit: 2,

    
    triggerEvent: undefined,

    relayedEvents: [
        'beforeedit',
        'edit',
        'validateedit',
        'canceledit'
    ],

    
    defaultFieldXType: 'textfield',

    
    editStyle: '',

    constructor: function(config) {
        var me = this;

        me.addEvents(
            
            'beforeedit',

            
            'edit',

            
            'validateedit',
            
            'canceledit'

        );
        me.callParent(arguments);
        me.mixins.observable.constructor.call(me);
        
        me.on("edit", function(editor, e) {
            me.fireEvent("afteredit", editor, e);
        });
    },

    
    init: function(grid) {
        var me = this;

        me.grid = grid;
        me.view = grid.view;
        me.initEvents();

        
        me.mon(grid, {
            reconfigure: me.onReconfigure,
            scope: me,
            beforerender: {
                fn: me.onReconfigure,
                single: true,
                scope: me
            }
        });

        grid.relayEvents(me, me.relayedEvents);

        
        if (me.grid.ownerLockable) {
            me.grid.ownerLockable.relayEvents(me, me.relayedEvents);
        }
        
        
        grid.isEditable = true;
        grid.editingPlugin = grid.view.editingPlugin = me;
    },

    
    onReconfigure: function() {
        var grid = this.grid;

        
        
        grid = grid.ownerLockable ? grid.ownerLockable : grid;
        this.initFieldAccessors(grid.getView().getGridColumns());
    },

    
    destroy: function() {
        var me = this,
            grid = me.grid;

        Ext.destroy(me.keyNav);
        
        me.clearListeners();

        if (grid) {
            me.removeFieldAccessors(grid.columnManager.getColumns());
            grid.editingPlugin = grid.view.editingPlugin = me.grid = me.view = me.editor = me.keyNav = null;
        }
    },

    
    getEditStyle: function() {
        return this.editStyle;
    },

    
    initFieldAccessors: function(columns) {
        
        if (columns.isGroupHeader) {
            columns = columns.getGridColumns();
        }

        
        else if (!Ext.isArray(columns)) {
            columns = [columns];
        }

        var me   = this,
            c,
            cLen = columns.length,
            column;

        for (c = 0; c < cLen; c++) {
            column = columns[c];

            if (!column.getEditor) {
                column.getEditor = function(record, defaultField) {
                    return me.getColumnField(this, defaultField);
                };
            }
            if (!column.hasEditor) {
                column.hasEditor = function() {
                    return me.hasColumnField(this);
                };
            }
            if (!column.setEditor) {
                column.setEditor = function(field) {
                    me.setColumnField(this, field);
                };
            }
        }
    },

    
    removeFieldAccessors: function(columns) {
        
        if (columns.isGroupHeader) {
            columns = columns.getGridColumns();
        }

        
        else if (!Ext.isArray(columns)) {
            columns = [columns];
        }

        var c,
            cLen = columns.length,
            column;

        for (c = 0; c < cLen; c++) {
            column = columns[c];

            column.getEditor = column.hasEditor = column.setEditor = null;
        }
    },

    
    
    getColumnField: function(columnHeader, defaultField) {
        var field = columnHeader.field;
        if (!(field && field.isFormField)) {
            field = columnHeader.field = this.createColumnField(columnHeader, defaultField);
        }
        return field;
    },

    
    
    hasColumnField: function(columnHeader) {
        return !!columnHeader.field;
    },

    
    
    setColumnField: function(columnHeader, field) {
        columnHeader.field = field;
        columnHeader.field = this.createColumnField(columnHeader);
    },

    createColumnField:  function(columnHeader, defaultField) {
        var field = columnHeader.field;

        if (!field && columnHeader.editor) {
            field = columnHeader.editor;
            columnHeader.editor = null;
        }

        if (!field && defaultField) {
            field = defaultField;
        }

        if (field) {
            if (field.isFormField) {
                field.column = columnHeader;
            } else {
                if (Ext.isString(field)) {
                    field = {
                        name: columnHeader.dataIndex,
                        xtype: field,
                        column: columnHeader
                    };
                } else {
                    field = Ext.apply({
                        name: columnHeader.dataIndex,
                        column: columnHeader
                    }, field);
                }
                field = Ext.ComponentManager.create(field, this.defaultFieldXType);
            }
            columnHeader.field = field;
        }
        return field;
    },

    
    initEvents: function() {
        var me = this;
        me.initEditTriggers();
        me.initCancelTriggers();
    },

    
    initCancelTriggers: Ext.emptyFn,

    
    initEditTriggers: function() {
        var me = this,
            view = me.view;

        
        if (me.triggerEvent == 'cellfocus') {
            me.mon(view, 'cellfocus', me.onCellFocus, me);
        } else if (me.triggerEvent == 'rowfocus') {
            me.mon(view, 'rowfocus', me.onRowFocus, me);
        } else {

            
            
            
            
            
            
            if (view.getSelectionModel().isCellModel) {
                view.onCellFocus = Ext.Function.bind(me.beforeViewCellFocus, me);
            }

            
            me.mon(view, me.triggerEvent || ('cell' + (me.clicksToEdit === 1 ? 'click' : 'dblclick')), me.onCellClick, me);
        }

        
        
        me.initAddRemoveHeaderEvents()
        
        view.on('render', me.initKeyNavHeaderEvents, me, {single: true});
    },

    
    beforeViewCellFocus: function(position) {
        
        if (this.view.selModel.keyNavigation || !this.editing || !this.isCellEditable || !this.isCellEditable(position.row, position.columnHeader)) {
            this.view.focusCell.apply(this.view, arguments);
        }
    },

    
    onRowFocus: function(record, row, rowIdx) {
        this.startEdit(row, 0);
    },

    
    onCellFocus: function(record, cell, position) {
        this.startEdit(position.row, position.column);
    },

    
    onCellClick: function(view, cell, colIdx, record, row, rowIdx, e) {
        
        if(!view.expanderSelector || !e.getTarget(view.expanderSelector)) {
            this.startEdit(record, view.ownerCt.columnManager.getHeaderAtIndex(colIdx));
        }
    },

    initAddRemoveHeaderEvents: function(){
        var me = this;
        me.mon(me.grid.headerCt, {
            scope: me,
            add: me.onColumnAdd,
            remove: me.onColumnRemove,
            columnmove: me.onColumnMove
        });
    },

    initKeyNavHeaderEvents: function() {
        var me = this;

        me.keyNav = Ext.create('Ext.util.KeyNav', me.view.el, {
            enter: me.onEnterKey,
            esc: me.onEscKey,
            scope: me
        });
    },

    
    onColumnAdd: function(ct, column) {
        this.initFieldAccessors(column);
    },

    
    onColumnRemove: function(ct, column) {
        this.removeFieldAccessors(column);
    },

    
    
    
    onColumnMove: function(headerCt, column, fromIdx, toIdx) {
        this.initFieldAccessors(column);
    },

    
    onEnterKey: function(e) {
        var me = this,
            grid = me.grid,
            selModel = grid.getSelectionModel(),
            record,
            pos,
            columnHeader;

        
        
        if (selModel.getCurrentPosition && (pos = selModel.getCurrentPosition())) {
            record = pos.record;
            columnHeader = pos.columnHeader;
        }
        
        else {
            record = selModel.getLastSelected();
            columnHeader = grid.columnManager.getHeaderAtIndex(0);
        }

        
        if (record && columnHeader) {
            me.startEdit(record, columnHeader);
        }
    },

    
    onEscKey: function(e) {
        return this.cancelEdit();
    },

    
    beforeEdit: Ext.emptyFn,

    
    startEdit: function(record, columnHeader) {
        var me = this,
            context,
            layoutView = me.grid.lockable ? me.grid : me.view;

        
        
        if (!layoutView.componentLayoutCounter) {
            layoutView.on({
                boxready: Ext.Function.bind(me.startEdit, me, [record, columnHeader]),
                single: true
            });
            return false;
        }

        
        if (me.grid.collapsed || !me.grid.view.isVisible(true)) {
            return false;
        }

        context = me.getEditingContext(record, columnHeader);
        if (context == null) {
            return false;
        }
        if (!me.preventBeforeCheck) {
            if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel) {
                return false;
            }
        }

        
        me.editing = true;
        return context;
    },

    
    
    getEditingContext: function(record, columnHeader) {
        var me = this,
            grid = me.grid,
            view = me.view,
            gridRow = view.getNode(record, true),
            rowIdx, colIdx;

        
        if (!gridRow) {
            return;
        }

        
        columnHeader = grid.columnManager.getVisibleHeaderClosestToIndex(Ext.isNumber(columnHeader) ? columnHeader : columnHeader.getVisibleIndex());

        
        if (!columnHeader) {
            return;
        }

        colIdx = columnHeader.getVisibleIndex();

        if (Ext.isNumber(record)) {
            
            rowIdx = record;
            record = view.getRecord(gridRow);
        } else {
            rowIdx = view.indexOf(gridRow);
        }

        
        
        if (!record) {
            return;
        }

        return {
            grid   : grid,
            view   : view,
            store  : view.dataSource,
            record : record,
            field  : columnHeader.dataIndex,
            value  : record.get(columnHeader.dataIndex),
            row    : gridRow,
            column : columnHeader,
            rowIdx : rowIdx,
            colIdx : colIdx
        };
    },

    
    cancelEdit: function() {
        var me = this;

        me.editing = false;
        me.fireEvent('canceledit', me, me.context);
    },

    
    completeEdit: function() {
        var me = this;

        if (me.editing && me.validateEdit()) {
            me.fireEvent('edit', me, me.context);
        }

        me.context = null;
        me.editing = false;
    },

    
    validateEdit: function() {
        var me = this,
            context = me.context;

        return me.fireEvent('validateedit', me, context) !== false && !context.cancel;
    }
});


Ext.define('Ext.grid.plugin.CellEditing', {
    alias: 'plugin.cellediting',
    extend:  Ext.grid.plugin.Editing ,
                                                              
    lockableScope: 'both',

    
    
    
    

    init: function(grid) {
        var me = this,
            lockingPartner = me.lockingPartner;

        me.callParent(arguments);

        
        if (lockingPartner) {
            if (lockingPartner.editors) {
                me.editors = lockingPartner.editors;
            } else {
                me.editors = lockingPartner.editors = new Ext.util.MixedCollection(false, function(editor) {
                    return editor.editorId;
                });
            }
        } else {
            me.editors = new Ext.util.MixedCollection(false, function(editor) {
                return editor.editorId;
            });
        }
    },

    onReconfigure: function(grid, store, columns){
        
        if (columns) {
            this.editors.clear();
        }
        this.callParent();    
    },

    
    destroy: function() {
        var me = this;
        if (me.editors) {
            me.editors.each(Ext.destroy, Ext);
            me.editors.clear();
        }
        me.callParent(arguments);
    },

    onBodyScroll: function() {
        var me = this,
            ed = me.getActiveEditor(),
            scroll = me.view.el.getScroll();

        
        
        if (ed && ed.editing && ed.editingPlugin === me) {
            
            if (scroll.top !== me.scroll.top) {
                if (ed.field) {
                    if (ed.field.triggerBlur) {
                        ed.field.triggerBlur();
                    } else {
                        ed.field.blur();
                    }
                }
            }
            
            else {
                ed.realign();
            }
        }
        me.scroll = scroll;
    },

    
    
    initCancelTriggers: function() {
        var me   = this,
            grid = me.grid,
            view = grid.view;

        me.mon(view, 'bodyscroll', me.onBodyScroll, me);
        me.mon(grid, {
            columnresize: me.cancelEdit,
            columnmove: me.cancelEdit,
            scope: me
        });
    },

    isCellEditable: function(record, columnHeader) {
        var me = this,
            context = me.getEditingContext(record, columnHeader);

        if (me.grid.view.isVisible(true) && context) {
            columnHeader = context.column;
            record = context.record;
            if (columnHeader && me.getEditor(record, columnHeader)) {
                return true;
            }
        }
    },

    
    startEdit: function(record, columnHeader,  context) {
        var me = this,
            ed;

        if (!context) {
            me.preventBeforeCheck = true;
            context = me.callParent(arguments);
            delete me.preventBeforeCheck;
            if (context === false) {
                return false;
            }
        }

        
        
        if (context && me.grid.view.isVisible(true)) {

            record = context.record;
            columnHeader = context.column;

            
            
            
            me.completeEdit();

            
            if (columnHeader && !columnHeader.getEditor(record)) {
                return false;
            }

            
            me.context = context;

            context.originalValue = context.value = record.get(columnHeader.dataIndex);
            
            if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', me, context) === false || context.cancel) {
                return false;
            }

            ed = me.getEditor(record, columnHeader);

            
            me.grid.view.cancelFocus();
            me.view.scrollCellIntoView(me.getCell(record, columnHeader));
            if (ed) {
                me.showEditor(ed, context, context.value);
                return true;
            }
            return false;
        }
    },

    showEditor: function(ed, context, value) {
        var me = this,
            record = context.record,
            columnHeader = context.column,
            sm = me.grid.getSelectionModel(),
            selection = sm.getCurrentPosition(),
            otherView = selection && selection.view;

        
        
        if (otherView && otherView !== me.view) {
            return me.lockingPartner.showEditor(ed, me.lockingPartner.getEditingContext(selection.record, selection.columnHeader), value);
        }

        me.setEditingContext(context);
        me.setActiveEditor(ed);
        me.setActiveRecord(record);
        me.setActiveColumn(columnHeader);

        
        if (sm.selectByPosition && (!selection || selection.column !== context.colIdx || selection.row !== context.rowIdx)) {
            sm.selectByPosition({
                row: context.rowIdx,
                column: context.colIdx,
                view: me.view
            });
        }

        ed.startEdit(me.getCell(record, columnHeader), value, context);
        me.editing = true;
        me.scroll = me.view.el.getScroll();
    },

    completeEdit: function() {
        var activeEd = this.getActiveEditor();
        if (activeEd) {
            activeEd.completeEdit();
            this.editing = false;
        }
    },

    
    setEditingContext: function(context) {
        this.context = context;
        if (this.lockingPartner) {
            this.lockingPartner.context = context;
        }
    },

    setActiveEditor: function(ed) {
        this.activeEditor = ed;
        if (this.lockingPartner) {
            this.lockingPartner.activeEditor = ed;
        }
    },

    getActiveEditor: function() {
        return this.activeEditor;
    },

    setActiveColumn: function(column) {
        this.activeColumn = column;
        if (this.lockingPartner) {
            this.lockingPartner.activeColumn = column;
        }
    },

    getActiveColumn: function() {
        return this.activeColumn;
    },

    setActiveRecord: function(record) {
        this.activeRecord = record;
        if (this.lockingPartner) {
            this.lockingPartner.activeRecord = record;
        }
    },

    getActiveRecord: function() {
        return this.activeRecord;
    },

    getEditor: function(record, column) {
        var me = this,
            editors = me.editors,
            editorId = column.getItemId(),
            editor = editors.getByKey(editorId),
            
            editorOwner = me.grid.ownerLockable || me.grid;

        if (!editor) {
            editor = column.getEditor(record);
            if (!editor) {
                return false;
            }

            
            if (editor instanceof Ext.grid.CellEditor) {
                editor.floating = true;
            }
            
            else {
                editor = new Ext.grid.CellEditor({
                    floating: true,
                    editorId: editorId,
                    field: editor
                });
            }
            
            editorOwner.add(editor);
            editor.on({
                scope: me,
                specialkey: me.onSpecialKey,
                complete: me.onEditComplete,
                canceledit: me.cancelEdit
            });
            column.on('removed', me.cancelActiveEdit, me);
            editors.add(editor);
        }

        if (column.isTreeColumn) {
            editor.isForTree = column.isTreeColumn;
            editor.addCls(Ext.baseCSSPrefix + 'tree-cell-editor')
        }
        editor.grid = me.grid;
        
        
        editor.editingPlugin = me;
        return editor;
    },
    
    cancelActiveEdit: function(column){
        var context = this.context
        if (context && context.column === column) {
            this.cancelEdit();
        }   
    },
    
    
    setColumnField: function(column, field) {
        var ed = this.editors.getByKey(column.getItemId());
        Ext.destroy(ed, column.field);
        this.editors.removeAtKey(column.getItemId());
        this.callParent(arguments);
    },

    
    getCell: function(record, column) {
        return this.grid.getView().getCell(record, column);
    },

    onSpecialKey: function(ed, field, e) {
        var sm;
 
        if (e.getKey() === e.TAB) {
            e.stopEvent();

            if (ed) {
                
                
                ed.onEditorTab(e);
            }

            sm = ed.up('tablepanel').getSelectionModel();
            if (sm.onEditorTab) {
                return sm.onEditorTab(ed.editingPlugin, e);
            }
        }
    },

    onEditComplete : function(ed, value, startValue) {
        var me = this,
            activeColumn = me.getActiveColumn(),
            context = me.context,
            record;

        if (activeColumn) {
            record = context.record;

            me.setActiveEditor(null);
            me.setActiveColumn(null);
            me.setActiveRecord(null);
    
            context.value = value;
            if (!me.validateEdit()) {
                return;
            }

            
            
            if (!record.isEqual(value, startValue)) {
                record.set(activeColumn.dataIndex, value);
            }

            
            
            context.view.focus(false, true);
            me.fireEvent('edit', me, context);
            me.editing = false;
        }
    },

    
    cancelEdit: function() {
        var me = this,
            activeEd = me.getActiveEditor();

        me.setActiveEditor(null);
        me.setActiveColumn(null);
        me.setActiveRecord(null);
        if (activeEd) {
            activeEd.cancelEdit();
            me.context.view.focus();
            me.callParent(arguments);
            return;
        }
        
        return true;
    },

    
    startEditByPosition: function(position) {

        
        if (!position.isCellContext) {
            position = new Ext.grid.CellContext(this.view).setPosition(position);
        }

        
        position.setColumn(this.view.getHeaderCt().getVisibleHeaderClosestToIndex(position.column).getIndex());

        return this.startEdit(position.record, position.columnHeader);
    }
});

Ext.define('Ext.grid.plugin.DivRenderer', {
    alias: 'plugin.divrenderer',
    extend:  Ext.AbstractPlugin ,

    tableTpl: [
        '<div id="{view.id}-table" class="' + Ext.baseCSSPrefix + '{view.id}-table ' + Ext.baseCSSPrefix + 'grid-table" style="{tableStyle}">',
            '{%',
                'values.view.renderRows(values.rows, values.viewStartIndex, out);',
            '%}',
        '</div>',
        {
            priority: 0
        }
    ],

    rowTpl: [
        '{%',
            'var dataRowCls = values.recordIndex === -1 ? "" : " ' + Ext.baseCSSPrefix + 'grid-data-row";',
        '%}',
        '<dl {[values.rowId ? ("id=\\"" + values.rowId + "\\"") : ""]} ',
            'data-boundView="{view.id}" ',
            'data-recordId="{record.internalId}" ',
            'data-recordIndex="{recordIndex}" ',
            'class="{[values.itemClasses.join(" ")]} {[values.rowClasses.join(" ")]}{[dataRowCls]}" ',
            'style="position:relative" ',
            '{rowAttr:attributes}>',
            '<tpl for="columns">' +
                '{%',
                    'parent.view.renderCell(values, parent.record, parent.recordIndex, xindex - 1, out, parent)',
                 '%}',
            '</tpl>',
        '</dl>',
        {
            priority: 0
        }
    ],

    cellTpl: [
        '<dt class="{tdCls}" {tdAttr} data-cellIndex="{cellIndex}">',
            '<div {unselectableAttr} class="' + Ext.baseCSSPrefix + 'grid-cell-inner"',
                'style="text-align:{align};<tpl if="style">{style}</tpl>">{value}</div>',
        '</dt>', {
            priority: 0
        }
    ],

    selectors: {
        
        bodySelector: 'div',

        
        nodeContainerSelector: 'div',

        
        itemSelector: 'dl.' + Ext.baseCSSPrefix + 'grid-row',

        
        dataRowSelector: 'dl.' + Ext.baseCSSPrefix + 'grid-data-row',

        
        cellSelector: 'dt.' + Ext.baseCSSPrefix + 'grid-cell',

        innerSelector: 'div.' + Ext.baseCSSPrefix + 'grid-cell-inner',

        getNodeContainerSelector: function() {
            return this.getBodySelector();
        },

        getNodeContainer: function() {
            return this.el.getById(this.id + '-table', true);
        }
    },

    init: function(grid) {
        var view = grid.getView();
        view.tableTpl = Ext.XTemplate.getTpl(this, 'tableTpl');
        view.rowTpl   = Ext.XTemplate.getTpl(this, 'rowTpl');
        view.cellTpl  = Ext.XTemplate.getTpl(this, 'cellTpl');
        Ext.apply(view, this.selectors);
    }
});


Ext.define('Ext.grid.plugin.DragDrop', {
    extend:  Ext.AbstractPlugin ,
    alias: 'plugin.gridviewdragdrop',

           
                            
                               
      

    

    
    

    
    dragText : '{0} selected row{1}',
    

    
    ddGroup : "GridDD",

    

    

    
    enableDrop: true,

    
    enableDrag: true,
    
    
    containerScroll: false,

    init : function(view) {
        view.on('render', this.onViewRender, this, {single: true});
    },

    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },

    enable: function() {
        var me = this;
        if (me.dragZone) {
            me.dragZone.unlock();
        }
        if (me.dropZone) {
            me.dropZone.unlock();
        }
        me.callParent();
    },

    disable: function() {
        var me = this;
        if (me.dragZone) {
            me.dragZone.lock();
        }
        if (me.dropZone) {
            me.dropZone.lock();
        }
        me.callParent();
    },

    onViewRender : function(view) {
        var me = this,
            scrollEl;

        if (me.enableDrag) {
            if (me.containerScroll) {
                scrollEl = view.getEl();
            }
            
            me.dragZone = new Ext.view.DragZone({
                view: view,
                ddGroup: me.dragGroup || me.ddGroup,
                dragText: me.dragText,
                containerScroll: me.containerScroll,
                scrollEl: scrollEl
            });
        }

        if (me.enableDrop) {
            me.dropZone = new Ext.grid.ViewDropZone({
                view: view,
                ddGroup: me.dropGroup || me.ddGroup
            });
        }
    }
});


Ext.define('Ext.grid.plugin.RowEditing', {
    extend:  Ext.grid.plugin.Editing ,
    alias: 'plugin.rowediting',

               
                            
      

    lockableScope: 'top',

    editStyle: 'row',

    
    autoCancel: true,

    

    
    errorSummary: true,

    constructor: function() {
        var me = this;

        me.callParent(arguments);

        if (!me.clicksToMoveEditor) {
            me.clicksToMoveEditor = me.clicksToEdit;
        }

        me.autoCancel = !!me.autoCancel;
    },

    
    destroy: function() {
        Ext.destroy(this.editor);
        this.callParent(arguments);
    },

    
    startEdit: function(record, columnHeader) {
        var me = this,
            editor = me.getEditor(),
            context;

        if (editor.beforeEdit() !== false) {
            context = me.callParent(arguments);
            if (context) {
                me.context = context;

                
                if (me.lockingPartner) {
                    me.lockingPartner.cancelEdit();
                }
                editor.startEdit(context.record, context.column, context);
                return true;
            }
        }
        return false;
    },

    
    cancelEdit: function() {
        var me = this;

        if (me.editing) {
            me.getEditor().cancelEdit();
            me.callParent(arguments);
            return;
        }
        
        return true;
    },

    
    completeEdit: function() {
        var me = this;

        if (me.editing && me.validateEdit()) {
            me.editing = false;
            me.fireEvent('edit', me, me.context);
        }
    },

    
    validateEdit: function() {
        var me             = this,
            editor         = me.editor,
            context        = me.context,
            record         = context.record,
            newValues      = {},
            originalValues = {},
            editors        = editor.query('>[isFormField]'),
            e,
            eLen           = editors.length,
            name, item;

        for (e = 0; e < eLen; e++) {
            item = editors[e];
            name = item.name;

            newValues[name]      = item.getValue();
            originalValues[name] = record.get(name);
        }

        Ext.apply(context, {
            newValues      : newValues,
            originalValues : originalValues
        });

        return me.callParent(arguments) && me.getEditor().completeEdit();
    },

    
    getEditor: function() {
        var me = this;

        if (!me.editor) {
            me.editor = me.initEditor();
        }
        return me.editor;
    },

    
    initEditor: function() {
        return new Ext.grid.RowEditor(this.initEditorConfig());
    },
    
    initEditorConfig: function(){
        var me       = this,
            grid     = me.grid,
            view     = me.view,
            headerCt = grid.headerCt,
            btns     = ['saveBtnText', 'cancelBtnText', 'errorsText', 'dirtyText'],
            b,
            bLen     = btns.length,
            cfg      = {
                autoCancel: me.autoCancel,
                errorSummary: me.errorSummary,
                fields: headerCt.getGridColumns(),
                hidden: true,
                view: view,
                
                editingPlugin: me
            },
            item;

        for (b = 0; b < bLen; b++) {
            item = btns[b];

            if (Ext.isDefined(me[item])) {
                cfg[item] = me[item];
            }
        }
        return cfg;    
    },

    
    initEditTriggers: function() {
        var me = this,
            view = me.view,
            moveEditorEvent = me.clicksToMoveEditor === 1 ? 'click' : 'dblclick';

        me.callParent(arguments);

        if (me.clicksToMoveEditor !== me.clicksToEdit) {
            me.mon(view, 'cell' + moveEditorEvent, me.moveEditorByClick, me);
        }

        view.on({
            render: function() {
                me.mon(me.grid.headerCt, {
                    scope: me,
                    columnresize: me.onColumnResize,
                    columnhide: me.onColumnHide,
                    columnshow: me.onColumnShow
                });
            },
            single: true
        });
    },

    startEditByClick: function() {
        var me = this;
        if (!me.editing || me.clicksToMoveEditor === me.clicksToEdit) {
            me.callParent(arguments);
        }
    },

    moveEditorByClick: function() {
        var me = this;
        if (me.editing) {
            me.superclass.onCellClick.apply(me, arguments);
        }
    },
    
    
    onColumnAdd: function(ct, column) {
        if (column.isHeader) {
            var me = this,
                editor;

            me.initFieldAccessors(column);

            
            
            editor = me.editor;
            if (editor && editor.onColumnAdd) {
                editor.onColumnAdd(column);
            }
        }
    },

    
    onColumnRemove: function(ct, column) {
        if (column.isHeader) {
            var me = this,
                editor = me.getEditor();

            if (editor && editor.onColumnRemove) {
                editor.onColumnRemove(ct, column);
            }
            me.removeFieldAccessors(column);
        }
    },

    
    onColumnResize: function(ct, column, width) {
        if (column.isHeader) {
            var me = this,
                editor = me.getEditor();

            if (editor && editor.onColumnResize) {
                editor.onColumnResize(column, width);
            }
        }
    },

    
    onColumnHide: function(ct, column) {
        
        var me = this,
            editor = me.getEditor();

        if (editor && editor.onColumnHide) {
            editor.onColumnHide(column);
        }
    },

    
    onColumnShow: function(ct, column) {
        
        var me = this,
            editor = me.getEditor();

        if (editor && editor.onColumnShow) {
            editor.onColumnShow(column);
        }
    },

    
    onColumnMove: function(ct, column, fromIdx, toIdx) {
        
        var me = this,
            editor = me.getEditor();

        
        
        me.initFieldAccessors(column);

        if (editor && editor.onColumnMove) {
            
            
            editor.onColumnMove(column, fromIdx, toIdx);
        }
    },

    
    setColumnField: function(column, field) {
        var me = this,
            editor = me.getEditor();
            
        editor.removeField(column);
        me.callParent(arguments);
        me.getEditor().setField(column);
    }
});






Ext.define('Ext.grid.plugin.RowExpander', {
    extend:  Ext.AbstractPlugin ,
    lockableScope: 'normal',

               
                                   
                                  
      

    alias: 'plugin.rowexpander',

    rowBodyTpl: null,

    
    expandOnEnter: true,

    
    expandOnDblClick: true,

    
    selectRowOnExpand: false,

    rowBodyTrSelector: '.x-grid-rowbody-tr',
    rowBodyHiddenCls: 'x-grid-row-body-hidden',
    rowCollapsedCls: 'x-grid-row-collapsed',

    addCollapsedCls: {
        before: function(values, out) {
            var me = this.rowExpander;
            if (!me.recordsExpanded[values.record.internalId]) {
                values.itemClasses.push(me.rowCollapsedCls);
            }
        },
        priority: 500
    },

    
    

    setCmp: function(grid) {
        var me = this,
            rowBodyTpl,
            features;

        me.callParent(arguments);

        me.recordsExpanded = {};

        me.rowBodyTpl = Ext.XTemplate.getTpl(me, 'rowBodyTpl');
        rowBodyTpl = this.rowBodyTpl;
        features = [{
            ftype: 'rowbody',
            lockableScope: 'normal',
            recordsExpanded: me.recordsExpanded,
            rowBodyHiddenCls: me.rowBodyHiddenCls,
            rowCollapsedCls: me.rowCollapsedCls,
            setupRowData: me.getRowBodyFeatureData,
            setup: me.setup,
            getRowBodyContents: function(record) {
                return rowBodyTpl.applyTemplate(record.getData());
            }
        },{
            ftype: 'rowwrap',
            lockableScope: 'normal'
        }];
 
        if (grid.features) {
            grid.features = Ext.Array.push(features, grid.features);
        } else {
            grid.features = features;
        }
        
    },

    init: function(grid) {
        var me = this,
            reconfigurable = grid,
            view, lockedView;

        me.callParent(arguments);
        me.grid = grid;
        view = me.view = grid.getView();
        
        
        me.addExpander();
        
        
        
        me.bindView(view);
        view.addRowTpl(me.addCollapsedCls).rowExpander = me;

        
        
        if (grid.ownerLockable) {
            
            reconfigurable = grid.ownerLockable;
            reconfigurable.syncRowHeight = false;
            lockedView = reconfigurable.lockedGrid.getView();

            
            
            me.bindView(lockedView);
            lockedView.addRowTpl(me.addCollapsedCls).rowExpander = me;

            
            
            reconfigurable.mon(reconfigurable, 'columnschanged', me.refreshRowHeights, me);
            reconfigurable.mon(reconfigurable.store, 'datachanged', me.refreshRowHeights, me);
        }
        reconfigurable.on('beforereconfigure', me.beforeReconfigure, me);

        if (grid.ownerLockable && !grid.rowLines) {
            
            
            
            
            view.on('rowfocus', me.refreshRowHeights, me);
        }
    },
    
    beforeReconfigure: function(grid, store, columns, oldStore, oldColumns) {
        var expander = this.getHeaderConfig();
        expander.locked = true;
        columns.unshift(expander);
    },

    
    addExpander: function() {
        var me = this,
            expanderGrid = me.grid,
            expanderHeader = me.getHeaderConfig();

        
        if (expanderGrid.ownerLockable) {
            expanderGrid = expanderGrid.ownerLockable.lockedGrid;
            expanderGrid.width += expanderHeader.width;
        }
        expanderGrid.headerCt.insert(0, expanderHeader);
    },

    getRowBodyFeatureData: function(record, idx, rowValues) {
        var me = this
        me.self.prototype.setupRowData.apply(me, arguments);

        rowValues.rowBody = me.getRowBodyContents(record);
        rowValues.rowBodyCls = me.recordsExpanded[record.internalId] ? '' : me.rowBodyHiddenCls;
    },
    
    setup: function(rows, rowValues){
        var me = this;
        me.self.prototype.setup.apply(me, arguments);
        
        if (!me.grid.ownerLockable) {
            rowValues.rowBodyColspan -= 1;
        }    
    },

    bindView: function(view) {
        if (this.expandOnEnter) {
            view.on('itemkeydown', this.onKeyDown, this);
        }
        if (this.expandOnDblClick) {
            view.on('itemdblclick', this.onDblClick, this);
        }
    },

    onKeyDown: function(view, record, row, rowIdx, e) {
        if (e.getKey() == e.ENTER) {
            var ds   = view.store,
                sels = view.getSelectionModel().getSelection(),
                ln   = sels.length,
                i = 0;

            for (; i < ln; i++) {
                rowIdx = ds.indexOf(sels[i]);
                this.toggleRow(rowIdx, sels[i]);
            }
        }
    },

    onDblClick: function(view, record, row, rowIdx, e) {
        this.toggleRow(rowIdx, record);
    },

    toggleRow: function(rowIdx, record) {
        var me = this,
            view = me.view,
            rowNode = view.getNode(rowIdx),
            row = Ext.fly(rowNode, '_rowExpander'),
            nextBd = row.down(me.rowBodyTrSelector, true),
            isCollapsed = row.hasCls(me.rowCollapsedCls),
            addOrRemoveCls = isCollapsed ? 'removeCls' : 'addCls',
            ownerLock, rowHeight, fireView;

        
        Ext.suspendLayouts();
        row[addOrRemoveCls](me.rowCollapsedCls);
        Ext.fly(nextBd)[addOrRemoveCls](me.rowBodyHiddenCls);
        me.recordsExpanded[record.internalId] = isCollapsed;
        view.refreshSize();

        
        if (me.grid.ownerLockable) {
            ownerLock = me.grid.ownerLockable;
            fireView = ownerLock.getView();
            view = ownerLock.lockedGrid.view;
            rowHeight = row.getHeight();
            row = Ext.fly(view.getNode(rowIdx), '_rowExpander');
            row.setHeight(rowHeight);
            row[addOrRemoveCls](me.rowCollapsedCls);
            view.refreshSize();
        } else {
            fireView = view;
        }
        fireView.fireEvent(isCollapsed ? 'expandbody' : 'collapsebody', row.dom, record, nextBd);
        
        Ext.resumeLayouts(true);
    },

    
    
    
    
    
    
    refreshRowHeights: function() {
        Ext.globalEvents.on({
            idle: this.doRefreshRowHeights,
            scope: this,
            single: true
        });
    },

    doRefreshRowHeights: function() {
        var me = this,
            recordsExpanded = me.recordsExpanded,
            key, record,
            lockedView = me.grid.ownerLockable.lockedGrid.view,
            normalView = me.grid.ownerLockable.normalGrid.view,
            normalRow,
            lockedRow,
            lockedHeight,
            normalHeight;

        for (key in recordsExpanded) {
            if (recordsExpanded.hasOwnProperty(key)) {
                record = this.view.store.data.get(key);
                lockedRow = lockedView.getNode(record, false);
                normalRow = normalView.getNode(record, false);
                lockedRow.style.height = normalRow.style.height = '';
                lockedHeight = lockedRow.offsetHeight;
                normalHeight = normalRow.offsetHeight;
                if (normalHeight > lockedHeight) {
                    lockedRow.style.height = normalHeight + 'px';
                } else if (lockedHeight > normalHeight) {
                    normalRow.style.height = lockedHeight + 'px';
                }
            }
        }
    },

    getHeaderConfig: function() {
        var me = this;

        return {
            width: 24,
            lockable: false,
            sortable: false,
            resizable: false,
            draggable: false,
            hideable: false,
            menuDisabled: true,
            tdCls: Ext.baseCSSPrefix + 'grid-cell-special',
            innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-row-expander',
            renderer: function(value, metadata) {
                
                if (!me.grid.ownerLockable) {
                    metadata.tdAttr += ' rowspan="2"';
                }
                return '<div class="' + Ext.baseCSSPrefix + 'grid-row-expander"></div>';
            },
            processEvent: function(type, view, cell, rowIndex, cellIndex, e, record) {
                if (type == "mousedown" && e.getTarget('.x-grid-row-expander')) {
                    me.toggleRow(rowIndex, record);
                    return me.selectRowOnExpand;
                }
            }
        };
    }
});


Ext.define('Ext.grid.property.Grid', {

    extend:  Ext.grid.Panel ,

    alias: 'widget.propertygrid',

    alternateClassName: 'Ext.grid.PropertyGrid',

           
                                     
                                 
                                           
                       
                             
                             
                             
                               
                                
      
    
    

   

    

    

    

    
    valueField: 'value',

    
    nameField: 'name',
    
    
    inferTypes: true,

    

    
    enableColumnMove: false,
    columnLines: true,
    stripeRows: false,
    trackMouseOver: false,
    clicksToEdit: 1,
    enableHdMenu: false,
    
    gridCls: Ext.baseCSSPrefix + 'property-grid',

    
    initComponent : function(){
        var me = this;

        me.source = me.source || {};
        me.addCls(me.gridCls);
        me.plugins = me.plugins || [];

        
        me.plugins.push(new Ext.grid.plugin.CellEditing({
            clicksToEdit: me.clicksToEdit,

            
            startEdit: function(record, column) {
                
                return this.self.prototype.startEdit.call(this, record, me.headerCt.child('#' + me.valueField));
            }
        }));

        me.selModel = {
            selType: 'cellmodel',
            onCellSelect: function(position) {
                if (position.column != 1) {
                    position.column = 1;
                }
                return this.self.prototype.onCellSelect.call(this, position);
            }
        };
        
        me.sourceConfig = Ext.apply({}, me.sourceConfig);  

        
        if (!me.store) {
            me.propStore = me.store = new Ext.grid.property.Store(me, me.source);
        }
        me.configure(me.sourceConfig);

        if (me.sortableColumns) {
            me.store.sort('name', 'ASC');
        }
        me.columns = new Ext.grid.property.HeaderContainer(me, me.store);

        me.addEvents(
            
            'beforepropertychange',
            
            'propertychange'
        );
        me.callParent();

        
        me.getView().walkCells = this.walkCells;

        
        me.editors = {
            'date'    : new Ext.grid.CellEditor({ field: new Ext.form.field.Date({selectOnFocus: true})}),
            'string'  : new Ext.grid.CellEditor({ field: new Ext.form.field.Text({selectOnFocus: true})}),
            'number'  : new Ext.grid.CellEditor({ field: new Ext.form.field.Number({selectOnFocus: true})}),
            'boolean' : new Ext.grid.CellEditor({ field: new Ext.form.field.ComboBox({
                editable: false,
                store: [[ true, me.headerCt.trueText ], [false, me.headerCt.falseText ]]
            })})
        };

        
        me.store.on('update', me.onUpdate, me);
    },
    
    configure: function(config){
        var me = this,
            store = me.store,
            i = 0,
            len = me.store.getCount(),
            nameField = me.nameField,
            valueField = me.valueField,
            name, value, rec, type;
        
        me.configureLegacy(config);  
        
        if (me.inferTypes) {
            for (; i < len; ++i) {
                rec = store.getAt(i);
                name = rec.get(nameField);
                if (!me.getConfig(name, 'type')) {
                    value = rec.get(valueField);
                    if (Ext.isDate(value)) {
                        type = 'date';
                    } else if (Ext.isNumber(value)) {
                        type = 'number';
                    } else if (Ext.isBoolean(value)) {
                        type = 'boolean';
                    } else {
                        type = 'string';
                    }
                    me.setConfig(name, 'type', type);
                }
            }
        }
    },
    
    getConfig: function(fieldName, key, defaultValue) {
        var config = this.sourceConfig[fieldName],
            out;
            
        if (config) {
            out = config[key];
        }    
        return out || defaultValue;
    },
    
    setConfig: function(fieldName, key, value) {
        var sourceCfg = this.sourceConfig,
            o = sourceCfg[fieldName];
            
        if (!o) {
            o = sourceCfg[fieldName] = {
                __copied: true
            };
        } else if (!o.__copied) {
            o = Ext.apply({
                __copied: true
            }, o);
            sourceCfg[fieldName] = o;
        }
        o[key] = value;
        return value;
    },
    
    
    configureLegacy: function(config){
        var me = this,
            o, key, value;
            
        me.copyLegacyObject(config, me.customRenderers, 'renderer');
        me.copyLegacyObject(config, me.customEditors, 'editor');
        me.copyLegacyObject(config, me.propertyNames, 'displayName');
        
    },
    
    copyLegacyObject: function(config, o, keyName){
        var key, value;
        
        for (key in o) {
            if (o.hasOwnProperty(key)) {
                if (!config[key]) {
                    config[key] = {};
                }
                config[key][keyName] = o[key];
            }    
        }
    },

    
    onUpdate : function(store, record, operation) {
        var me = this,
            v, oldValue;

        if (me.rendered && operation == Ext.data.Model.EDIT) {
            v = record.get(me.valueField);
            oldValue = record.modified.value;
            if (me.fireEvent('beforepropertychange', me.source, record.getId(), v, oldValue) !== false) {
                if (me.source) {
                    me.source[record.getId()] = v;
                }
                record.commit();
                me.fireEvent('propertychange', me.source, record.getId(), v, oldValue);
            } else {
                record.reject();
            }
        }
    },

    
    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {
        if (direction == 'left') {
            direction = 'up';
        } else if (direction == 'right') {
            direction = 'down';
        }
        pos = Ext.view.Table.prototype.walkCells.call(this, pos, direction, e, preventWrap, verifierFn, scope);
        if (pos && !pos.column) {
            pos.column = 1;
        }
        return pos;
    },

    
    
    getCellEditor : function(record, column) {
        var me = this,
            propName = record.get(me.nameField),
            val = record.get(me.valueField),
            editor = me.getConfig(propName, 'editor'),
            type = me.getConfig(propName, 'type'),
            editors = me.editors;

        
        
        if (editor) {
            if (!(editor instanceof Ext.grid.CellEditor)) {
                if (!(editor instanceof Ext.form.field.Base)) {
                    editor = Ext.ComponentManager.create(editor, 'textfield');
                }
                editor = me.setConfig(propName, 'editor', new Ext.grid.CellEditor({ field: editor }));
            }
        } else if (type) {
            switch (type) {
                case 'date':
                    editor = editors.date;
                    break;
                case 'number':
                    editor = editors.number;
                    break;
                case 'boolean':
                    editor = me.editors['boolean'];
                    break;
                default:
                    editor = editors.string;
            }
        } else if (Ext.isDate(val)) {
            editor = editors.date;
        } else if (Ext.isNumber(val)) {
            editor = editors.number;
        } else if (Ext.isBoolean(val)) {
            editor = editors['boolean'];
        } else {
            editor = editors.string;
        }

        
        editor.editorId = propName;
        return editor;
    },

    beforeDestroy: function() {
        var me = this;
        me.callParent();
        me.destroyEditors(me.editors);
        me.destroyEditors(me.customEditors);
        delete me.source;
    },

    destroyEditors: function (editors) {
        for (var ed in editors) {
            if (editors.hasOwnProperty(ed)) {
                Ext.destroy(editors[ed]);
            }
        }
    },

    
    setSource: function(source, sourceConfig) {
        var me = this;
        
        me.source = source;
        if (sourceConfig !== undefined) {
            me.sourceConfig = Ext.apply({}, sourceConfig);  
            me.configure(me.sourceConfig);
        }
        me.propStore.setSource(source);
    },

    
    getSource: function() {
        return this.propStore.getSource();
    },

    
    setProperty: function(prop, value, create) {
        this.propStore.setValue(prop, value, create);
    },

    
    removeProperty: function(prop) {
        this.propStore.remove(prop);
    }

    
    
});


Ext.define('Ext.grid.property.HeaderContainer', {

    extend:  Ext.grid.header.Container ,

    alternateClassName: 'Ext.grid.PropertyColumnModel',
    
    nameWidth: 115,

    
    
    nameText : 'Name',
    
    
    valueText : 'Value',
    
    
    dateFormat : 'm/j/Y',
    
    
    trueText: 'true',
    
    
    falseText: 'false',
    

    
    nameColumnCls: Ext.baseCSSPrefix + 'grid-property-name',
    nameColumnInnerCls: Ext.baseCSSPrefix + 'grid-cell-inner-property-name',

    
    constructor : function(grid, store) {
        var me = this;
        
        me.grid = grid;
        me.store = store;
        me.callParent([{
            isRootHeader: true,
            enableColumnResize: Ext.isDefined(grid.enableColumnResize) ? grid.enableColumnResize : me.enableColumnResize,
            enableColumnMove: Ext.isDefined(grid.enableColumnMove) ? grid.enableColumnMove : me.enableColumnMove,
            items: [{
                header: me.nameText,
                width: grid.nameColumnWidth || me.nameWidth,
                sortable: grid.sortableColumns,
                dataIndex: grid.nameField,
                renderer: Ext.Function.bind(me.renderProp, me),
                itemId: grid.nameField,
                menuDisabled :true,
                tdCls: me.nameColumnCls,
                innerCls: me.nameColumnInnerCls
            }, {
                header: me.valueText,
                renderer: Ext.Function.bind(me.renderCell, me),
                getEditor: Ext.Function.bind(me.getCellEditor, me),
                sortable: grid.sortableColumns,
                flex: 1,
                fixed: true,
                dataIndex: grid.valueField,
                itemId: grid.valueField,
                menuDisabled: true
            }]
        }]);
    },
    
    getCellEditor: function(record){
        return this.grid.getCellEditor(record, this);
    },

    
    
    renderProp : function(v) {
        return this.getPropertyName(v);
    },

    
    
    renderCell : function(val, meta, rec) {
        var me = this,
            grid = me.grid,
            renderer = grid.getConfig(rec.get(grid.nameField), 'renderer'),
            result = val;

        if (renderer) {
            return renderer.apply(me, arguments);
        }
        if (Ext.isDate(val)) {
            result = me.renderDate(val);
        } else if (Ext.isBoolean(val)) {
            result = me.renderBool(val);
        }
        return Ext.util.Format.htmlEncode(result);
    },

    
    renderDate : Ext.util.Format.date,

    
    renderBool : function(bVal) {
        return this[bVal ? 'trueText' : 'falseText'];
    },

    
    
    getPropertyName : function(name) {
        return this.grid.getConfig(name, 'displayName', name);
    }
});


Ext.define('Ext.grid.property.Property', {
    extend:  Ext.data.Model ,

    alternateClassName: 'Ext.PropGridProperty',

    fields: [{
        name: 'name',
        type: 'string'
    }, {
        name: 'value'
    }],
    idProperty: 'name'
});


Ext.define('Ext.grid.property.Store', {

    extend:  Ext.data.Store ,

    alternateClassName: 'Ext.grid.PropertyStore',

    sortOnLoad: false,

                                                                                                                 

    
    constructor : function(grid, source){
        var me = this;
        
        me.grid = grid;
        me.source = source;
        me.callParent([{
            data: source,
            model: Ext.grid.property.Property,
            proxy: me.getProxy()
        }]);
    },

    
    getProxy: function() {
        if (!this.proxy) {
            Ext.grid.property.Store.prototype.proxy = new Ext.data.proxy.Memory({
                model: Ext.grid.property.Property,
                reader: this.getReader()
            });
        }
        return this.proxy;
    },

    
    getReader: function() {
        if (!this.reader) {
            Ext.grid.property.Store.prototype.reader = new Ext.data.reader.Reader({
                model: Ext.grid.property.Property,

                buildExtractors: Ext.emptyFn,

                read: function(dataObject) {
                    return this.readRecords(dataObject);
                },

                readRecords: function(dataObject) {
                    var val,
                        propName,
                        result = {
                            records: [],
                            success: true
                        };

                    for (propName in dataObject) {
                        if (dataObject.hasOwnProperty(propName)) {
                            val = dataObject[propName];
                            if (this.isEditableValue(val)) {
                                result.records.push(new Ext.grid.property.Property({
                                    name: propName,
                                    value: val
                                }, propName));
                            }
                        }
                    }
                    result.total = result.count = result.records.length;
                    return new Ext.data.ResultSet(result);
                },

                
                isEditableValue: function(val){
                    return Ext.isPrimitive(val) || Ext.isDate(val) || val === null;
                }
            });
        }
        return this.reader;
    },

    
    
    setSource : function(dataObject) {
        var me = this;

        me.source = dataObject;
        me.suspendEvents();
        me.removeAll();
        me.proxy.data = dataObject;
        me.load();
        me.resumeEvents();
        me.fireEvent('datachanged', me);
        me.fireEvent('refresh', me);
    },

    
    getProperty : function(row) {
       return Ext.isNumber(row) ? this.getAt(row) : this.getById(row);
    },

    
    setValue : function(prop, value, create){
        var me = this,
            rec = me.getRec(prop);
            
        if (rec) {
            rec.set('value', value);
            me.source[prop] = value;
        } else if (create) {
            
            me.source[prop] = value;
            rec = new Ext.grid.property.Property({name: prop, value: value}, prop);
            me.add(rec);
        }
    },

    
    remove : function(prop) {
        var rec = this.getRec(prop);
        if (rec) {
            this.callParent([rec]);
            delete this.source[prop];
        }
    },

    
    getRec : function(prop) {
        return this.getById(prop);
    },

    
    
    getSource : function() {
        return this.source;
    }
});


Ext.define('Ext.layout.ClassList', (function () {

    var splitWords = Ext.String.splitWords,
        toMap = Ext.Array.toMap;

    return {
        dirty: false,

        constructor: function (owner) {
            this.owner = owner;
            this.map = toMap(this.classes = splitWords(owner.el.className));
        },

        
        add: function (cls) {
            var me = this;

            if (!me.map[cls]) {
                me.map[cls] = true;
                me.classes.push(cls);
                if (!me.dirty) {
                    me.dirty = true;
                    me.owner.markDirty();
                }
            }
        },

        
        addMany: function (classes) {
            Ext.each(splitWords(classes), this.add, this);
        },

        contains: function (cls) {
            return this.map[cls];
        },

        flush: function () {
            this.owner.el.className = this.classes.join(' ');
            this.dirty = false;
        },

        
        remove: function (cls) {
            var me = this;

            if (me.map[cls]) {
                delete me.map[cls];
                me.classes = Ext.Array.filter(me.classes, function (c) {
                    return c != cls;
                });
                if (!me.dirty) {
                    me.dirty = true;
                    me.owner.markDirty();
                }
            }
        },

        
        removeMany: function (classes) {
            var me = this,
                remove = toMap(splitWords(classes));

            me.classes = Ext.Array.filter(me.classes, function (c) {
                if (!remove[c]) {
                    return true;
                }

                delete me.map[c];
                if (!me.dirty) {
                    me.dirty = true;
                    me.owner.markDirty();
                }
                return false;
            });
        }
    };
}()));


Ext.define('Ext.util.Queue', {

    constructor: function() {
        this.clear();
    },

    add : function(obj) {
        var me = this,
            key = me.getKey(obj);

        if (!me.map[key]) {
            ++me.length;
            me.items.push(obj);
            me.map[key] = obj;
        }

        return obj;
    },

    
    clear : function(){
        var me = this,
            items = me.items;

        me.items = [];
        me.map = {};
        me.length = 0;

        return items;
    },

    contains: function (obj) {
        var key = this.getKey(obj);

        return this.map.hasOwnProperty(key);
    },

    
    getCount : function(){
        return this.length;
    },

    getKey : function(obj){
         return obj.id;
    },

    
    remove : function(obj){
        var me = this,
            key = me.getKey(obj),
            items = me.items,
            index;

        if (me.map[key]) {
            index = Ext.Array.indexOf(items, obj);
            Ext.Array.erase(items, index, 1);
            delete me.map[key];
            --me.length;
        }

        return obj;
    }
});


Ext.define('Ext.layout.ContextItem', {

                                       

    heightModel: null,
    widthModel: null,
    sizeModel: null,

    
    optOut: false,

    ownerSizePolicy: null, 

    boxChildren: null,

    boxParent: null,
    isBorderBoxValue: null,

    children: [],

    dirty: null,

    
    dirtyCount: 0,

    hasRawContent: true,

    isContextItem: true,

    isTopLevel: false,

    consumersContentHeight: 0,
    consumersContentWidth: 0,
    consumersContainerHeight: 0,
    consumersContainerWidth: 0,
    consumersHeight: 0,
    consumersWidth: 0,

    ownerCtContext: null,

    remainingChildDimensions: 0,

    
    props: null,

    
    state: null,

    
    wrapsComponent: false,

    constructor: function (config) {
        var me = this,
            sizeModels = Ext.layout.SizeModel.sizeModels,
            configured = sizeModels.configured,
            shrinkWrap = sizeModels.shrinkWrap,
            el, lastBox, ownerCt, ownerCtContext, props, sizeModel, target,
            lastWidth, lastHeight, sameWidth, sameHeight, widthModel, heightModel, optOut;

        Ext.apply(me, config);

        el = me.el;
        me.id = el.id;

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        

        me.flushedProps = {};
        me.props = props = {};

        
        me.styles = {};

        target = me.target;

        if (!target.isComponent) {
            lastBox = el.lastBox;
        } else {
            me.wrapsComponent = true;
            me.framing = target.frameSize || null;
            me.isComponentChild = target.ownerLayout && target.ownerLayout.isComponentLayout;

            lastBox = target.lastBox;

            
            
            ownerCt = target.ownerCt;
            if (ownerCt && (ownerCtContext = ownerCt.el && me.context.items[ownerCt.el.id])) {
                me.ownerCtContext = ownerCtContext;
            }

            
            
            me.sizeModel = sizeModel = target.getSizeModel(ownerCtContext &&
                ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]);

            
            
            
            
            

            me.widthModel = widthModel = sizeModel.width;
            me.heightModel = heightModel = sizeModel.height;

            
            
            
            if (lastBox && lastBox.invalid === false) {
                sameWidth = (target.width === (lastWidth = lastBox.width));
                sameHeight = (target.height === (lastHeight = lastBox.height));

                if (widthModel === shrinkWrap && heightModel === shrinkWrap) {
                    optOut = true;
                } else if (widthModel === configured && sameWidth) {
                    optOut = heightModel === shrinkWrap ||
                            (heightModel === configured && sameHeight);
                }

                if (optOut) {
                    
                    me.optOut = true;
                    props.width = lastWidth;
                    props.height = lastHeight;
                }
            }
        }

        me.lastBox = lastBox;
    },

    
    init: function (full, options) {
        var me = this,
            oldProps = me.props,
            oldDirty = me.dirty,
            ownerCtContext = me.ownerCtContext,
            ownerLayout = me.target.ownerLayout,
            firstTime = !me.state,
            ret = full || firstTime,
            children, i, n, ownerCt, sizeModel, target,
            oldHeightModel = me.heightModel,
            oldWidthModel = me.widthModel,
            newHeightModel, newWidthModel,
            remainingCount = 0;

        me.dirty = me.invalid = false;
        me.props = {};

        
        me.remainingChildDimensions = 0;

        if (me.boxChildren) {
            me.boxChildren.length = 0; 
        }

        if (!firstTime) {
            me.clearAllBlocks('blocks');
            me.clearAllBlocks('domBlocks');
        }

        
        if (!me.wrapsComponent) {
            return ret;
        }

        
        target = me.target;
        me.state = {}; 

        if (firstTime) {
            
            
            if (target.beforeLayout && target.beforeLayout !== Ext.emptyFn) {
                target.beforeLayout();
            }

            
            
            
            
            
            
            if (!ownerCtContext && (ownerCt = target.ownerCt)) {
                ownerCtContext = me.context.items[ownerCt.el.id];
            }

            if (ownerCtContext) {
                me.ownerCtContext = ownerCtContext;
                me.isBoxParent = target.ownerLayout.isItemBoxParent(me);
            } else {
                me.isTopLevel = true; 
            }

            me.frameBodyContext = me.getEl('frameBody');
        } else {
            ownerCtContext = me.ownerCtContext;

            
            me.isTopLevel = !ownerCtContext;

            
            
            children = me.children;
            for (i = 0, n = children.length; i < n; ++i) {
                children[i].init(true);
            }
        }

        
        
        
        me.hasRawContent = !(target.isContainer && target.items.items.length > 0);

        if (full) {
            
            
            me.widthModel = me.heightModel = null;
            sizeModel = target.getSizeModel(ownerCtContext && 
                ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]);

            if (firstTime) {
                me.sizeModel = sizeModel;
            }

            me.widthModel = sizeModel.width;
            me.heightModel = sizeModel.height;

            
            
            
            if (ownerCtContext && !me.isComponentChild) {
                ownerCtContext.remainingChildDimensions += 2;
            }
        } else if (oldProps) {
            
            
            me.recoverProp('x', oldProps, oldDirty);
            me.recoverProp('y', oldProps, oldDirty);
            
            
            if (me.widthModel.calculated) {
                me.recoverProp('width', oldProps, oldDirty);
            } else if ('width' in oldProps) {
                ++remainingCount;
            }
            if (me.heightModel.calculated) {
                me.recoverProp('height', oldProps, oldDirty);
            } else if ('height' in oldProps) {
                ++remainingCount;
            }
            
            
            
            
            
            
            if (ownerCtContext && !me.isComponentChild) {
                ownerCtContext.remainingChildDimensions += remainingCount;
            }
        }

        if (oldProps && ownerLayout && ownerLayout.manageMargins) {
            me.recoverProp('margin-top', oldProps, oldDirty);
            me.recoverProp('margin-right', oldProps, oldDirty);
            me.recoverProp('margin-bottom', oldProps, oldDirty);
            me.recoverProp('margin-left', oldProps, oldDirty);
        }

        
        
        if (options) {
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            newHeightModel = options.heightModel;
            newWidthModel = options.widthModel;
            if (newWidthModel && newHeightModel && oldWidthModel && oldHeightModel) {
                if (oldWidthModel.shrinkWrap && oldHeightModel.shrinkWrap) {
                    if (newWidthModel.constrainedMax && newHeightModel.constrainedMin) {
                        newHeightModel = null;
                    }
                }
            }

            
            if (newWidthModel) {
                me.widthModel = newWidthModel;
            }
            if (newHeightModel) {
                me.heightModel = newHeightModel;
            }

            if (options.state) {
                Ext.apply(me.state, options.state);
            }
        }

        return ret;
    },

    
    initContinue: function (full) {
        var me = this,
            ownerCtContext = me.ownerCtContext,
            comp = me.target,
            widthModel = me.widthModel,
            hierarchyState = comp.getHierarchyState(),
            boxParent;

        if (widthModel.fixed) { 
            hierarchyState.inShrinkWrapTable = false;
        } else {
            delete hierarchyState.inShrinkWrapTable;
        }

        if (full) {
            if (ownerCtContext && widthModel.shrinkWrap) {
                boxParent = ownerCtContext.isBoxParent ? ownerCtContext : ownerCtContext.boxParent;
                if (boxParent) {
                    boxParent.addBoxChild(me);
                }
            } else if (widthModel.natural) {
                me.boxParent = ownerCtContext;
            }
        }

        return full;
    },

    
    initDone: function(containerLayoutDone) {
        var me = this,
            props = me.props,
            state = me.state;

        
        if (me.remainingChildDimensions === 0) {
            props.containerChildrenSizeDone = true;
        }
        if (containerLayoutDone) {
            props.containerLayoutDone = true;
        }

        if (me.boxChildren && me.boxChildren.length && me.widthModel.shrinkWrap) {
            
            
            me.el.setWidth(10000);

            
            state.blocks = (state.blocks || 0) + 1;
        }
    },

    
    initAnimation: function() {
        var me = this,
            target = me.target,
            ownerCtContext = me.ownerCtContext;

        if (ownerCtContext && ownerCtContext.isTopLevel) {
            
            
            me.animatePolicy = target.ownerLayout.getAnimatePolicy(me);
        } else if (!ownerCtContext && target.isCollapsingOrExpanding && target.animCollapse) {
            
            
            
            me.animatePolicy = target.componentLayout.getAnimatePolicy(me);
        }

        if (me.animatePolicy) {
            me.context.queueAnimation(me);
        }
    },

    
    addCls: function(newCls) {
        this.getClassList().addMany(newCls);
    },

    
    removeCls: function(removeCls) {
        this.getClassList().removeMany(removeCls);
    },

    
    addBlock: function (name, layout, propName) {
        var me = this,
            collection = me[name] || (me[name] = {}),
            blockedLayouts = collection[propName] || (collection[propName] = {});

        if (!blockedLayouts[layout.id]) {
            blockedLayouts[layout.id] = layout;
            ++layout.blockCount;
            ++me.context.blockCount;
        }
    },

    addBoxChild: function (boxChildItem) {
        var me = this,
            children,
            widthModel = boxChildItem.widthModel;

        boxChildItem.boxParent = this;

        
        
        
        
        
        
        boxChildItem.measuresBox = widthModel.shrinkWrap ? boxChildItem.hasRawContent : widthModel.natural;

        if (boxChildItem.measuresBox) {
            children = me.boxChildren;

            if (children) {
                children.push(boxChildItem);
            } else {
                me.boxChildren = [ boxChildItem ];
            }
        }
    },

    
    addPositionStyles: function(styles, props) {
        var x = props.x,
            y = props.y,
            count = 0;

        if (x !== undefined) {
            styles.left = x + 'px';
            ++count;
        }
        if (y !== undefined) {
            styles.top = y + 'px';
            ++count;
        }
        return count;
    },

    
    addTrigger: function (propName, inDom) {
        var me = this,
            name = inDom ? 'domTriggers' : 'triggers',
            collection = me[name] || (me[name] = {}),
            context = me.context,
            layout = context.currentLayout,
            triggers = collection[propName] || (collection[propName] = {});

        if (!triggers[layout.id]) {
            triggers[layout.id] = layout;
            ++layout.triggerCount;

            triggers = context.triggers[inDom ? 'dom' : 'data'];
            (triggers[layout.id] || (triggers[layout.id] = [])).push({
                item: this,
                prop: propName
            });

            if (me.props[propName] !== undefined) {
                if (!inDom || !(me.dirty && (propName in me.dirty))) {
                    ++layout.firedTriggers;
                }
            }
        }
    },

    boxChildMeasured: function () {
        var me = this,
            state = me.state,
            count = (state.boxesMeasured = (state.boxesMeasured || 0) + 1);

        if (count == me.boxChildren.length) {
            
            
            state.clearBoxWidth = 1;
            ++me.context.progressCount;
            me.markDirty();
        }
    },

    borderNames: [ 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'],
    marginNames: [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ],
    paddingNames: [ 'padding-top', 'padding-right', 'padding-bottom', 'padding-left' ],
    trblNames: [ 'top', 'right', 'bottom', 'left' ],

    cacheMissHandlers: {
        borderInfo: function (me) {
            var info = me.getStyles(me.borderNames, me.trblNames);

            info.width = info.left + info.right;
            info.height = info.top + info.bottom;

            return info;
        },

        marginInfo: function (me) {
            var info = me.getStyles(me.marginNames, me.trblNames);

            info.width = info.left + info.right;
            info.height = info.top + info.bottom;

            return info;
        },

        paddingInfo: function (me) {
            
            var item = me.frameBodyContext || me,
                info = item.getStyles(me.paddingNames, me.trblNames);

            info.width = info.left + info.right;
            info.height = info.top + info.bottom;

            return info;
        }
    },

    checkCache: function (entry) {
        return this.cacheMissHandlers[entry](this);
    },

    clearAllBlocks: function (name) {
        var collection = this[name],
            propName;

        if (collection) {
            for (propName in collection) {
                this.clearBlocks(name, propName);
            }
        }
    },

    
    clearBlocks: function (name, propName) {
        var collection = this[name],
            blockedLayouts = collection && collection[propName],
            context, layout, layoutId;

        if (blockedLayouts) {
            delete collection[propName];

            context = this.context;

            for (layoutId in blockedLayouts) {
                layout = blockedLayouts[layoutId];

                --context.blockCount;
                if (! --layout.blockCount && !layout.pending && !layout.done) {
                    context.queueLayout(layout);
                }
            }
        }
    },

    
    block: function (layout, propName) {
        this.addBlock('blocks', layout, propName);
    },

    
    domBlock: function (layout, propName) {
        this.addBlock('domBlocks', layout, propName);
    },

    
    fireTriggers: function (name, propName) {
        var collection = this[name],
            triggers = collection && collection[propName],
            context = this.context,
            layout, layoutId;

        if (triggers) {
            for (layoutId in triggers) {
                layout = triggers[layoutId];
                ++layout.firedTriggers;
                if (!layout.done && !layout.blockCount && !layout.pending) {
                    context.queueLayout(layout);
                }
            }
        }
    },

    
    flush: function () {
        var me = this,
            dirty = me.dirty,
            state = me.state,
            targetEl = me.el;

        me.dirtyCount = 0;

        
        if (me.classList && me.classList.dirty) {
            me.classList.flush();
        }

        
        if ('attributes' in me) {
            targetEl.set(me.attributes);
            delete me.attributes;
        }

        
        if ('innerHTML' in me) {
            targetEl.innerHTML = me.innerHTML;
            delete me.innerHTML;
        }

        if (state && state.clearBoxWidth) {
            state.clearBoxWidth = 0;
            me.el.setStyle('width', null);

            if (! --state.blocks) {
                me.context.queueItemLayouts(me);
            }
        }

        if (dirty) {
            delete me.dirty;
            me.writeProps(dirty, true);
        }
    },

    
    flushAnimations: function() {
        var me = this,
            animateFrom = me.previousSize,
            target, targetAnim, duration, animateProps, anim,
            changeCount, j, propsLen, propName, oldValue, newValue;

        
        if (animateFrom) {
            target = me.target;
            targetAnim = target.layout && target.layout.animate;
            if (targetAnim) {
                duration = Ext.isNumber(targetAnim) ? targetAnim : targetAnim.duration;
            }
            animateProps = Ext.Object.getKeys(me.animatePolicy);

            
            
            anim = Ext.apply({}, {
                from: {},
                to: {},
                duration: duration || Ext.fx.Anim.prototype.duration
            }, targetAnim);

            for (changeCount = 0, j = 0, propsLen = animateProps.length; j < propsLen; j++) {
                propName = animateProps[j];
                oldValue = animateFrom[propName];
                newValue = me.peek(propName);
                if (oldValue != newValue) {
                    propName = me.translateProps[propName]||propName;
                    anim.from[propName] = oldValue;
                    anim.to[propName] = newValue;
                    ++changeCount;
                }
            }

            
            if (changeCount) {
                
                if (me.isCollapsingOrExpanding === 1) {
                    target.componentLayout.undoLayout(me);
                }

                
                else {
                    me.writeProps(anim.from);
                }
                me.el.animate(anim);

                Ext.fx.Manager.getFxQueue(me.el.id)[0].on({
                    afteranimate: function() {
                        if (me.isCollapsingOrExpanding === 1) {
                            target.componentLayout.redoLayout(me);
                            target.afterCollapse(true);
                        } else if (me.isCollapsingOrExpanding === 2) {
                            target.afterExpand(true);
                        }
                    }
                });
            }
        }
    },

    
    getBorderInfo: function () {
        var me = this,
            info = me.borderInfo;

        if (!info) {
            me.borderInfo = info = me.checkCache('borderInfo');
        }

        return info;
    },

    
    getClassList: function () {
        return this.classList || (this.classList = new Ext.layout.ClassList(this));
    },

    
    getEl: function (nameOrEl, owner) {
        var me = this,
            src, el, elContext;

        if (nameOrEl) {
            if (nameOrEl.dom) {
                el = nameOrEl;
            } else {
                src = me.target;
                if (owner) {
                    src = owner;
                }

                el = src[nameOrEl];
                if (typeof el == 'function') { 
                    el = el.call(src);
                    if (el === me.el) {
                        return this; 
                    }
                }
            }

            if (el) {
                elContext = me.context.getEl(me, el);
            }
        }

        return elContext || null;
    },

    
    getFrameInfo: function () {
        var me = this,
            info = me.frameInfo,
            framing, border;

        if (!info) {
            framing = me.framing;
            border = me.getBorderInfo();

            me.frameInfo = info = 
                framing ? {
                    top   : framing.top    + border.top,
                    right : framing.right  + border.right,
                    bottom: framing.bottom + border.bottom,
                    left  : framing.left   + border.left,
                    width : framing.width  + border.width,
                    height: framing.height + border.height
                } : border;
        }

        return info;
    },

    
    getMarginInfo: function () {
        var me = this,
            info = me.marginInfo,
            comp, manageMargins, margins, ownerLayout, ownerLayoutId;

        if (!info) {
            if (!me.wrapsComponent) {
                info = me.checkCache('marginInfo');
            } else {
                comp = me.target;
                ownerLayout = comp.ownerLayout;
                ownerLayoutId = ownerLayout ? ownerLayout.id : null;
                manageMargins = ownerLayout && ownerLayout.manageMargins;

                
                
                
                
                
                
                
                
                

                
                
                
                

                info = comp.margin$;
                if (info && info.ownerId !== ownerLayoutId) {
                    
                    info = null;

                    
                    
                    
                }

                if (!info) { 
                    
                    info = me.parseMargins(comp, comp.margin) || me.checkCache('marginInfo');

                    
                    if (manageMargins) {
                        margins = me.parseMargins(comp, comp.margins, ownerLayout.defaultMargins);

                        if (margins) { 
                            
                            info = {
                                top:    info.top    + margins.top,
                                right:  info.right  + margins.right,
                                bottom: info.bottom + margins.bottom,
                                left:   info.left   + margins.left
                            };
                        }

                        me.setProp('margin-top', 0);
                        me.setProp('margin-right', 0);
                        me.setProp('margin-bottom', 0);
                        me.setProp('margin-left', 0);
                    }

                    
                    info.ownerId = ownerLayoutId;
                    comp.margin$ = info;
                }

                info.width  = info.left + info.right;
                info.height = info.top  + info.bottom;
            }

            me.marginInfo = info;
        }

        return info;
    },

    
    clearMarginCache: function() {
        delete this.marginInfo;
        delete this.target.margin$;
    },

    
    getPaddingInfo: function () {
        var me = this,
            info = me.paddingInfo;

        if (!info) {
            me.paddingInfo = info = me.checkCache('paddingInfo');
        }

        return info;
    },

    
    getProp: function (propName) {
        var me = this,
            result = me.props[propName];

        me.addTrigger(propName);
        return result;
    },

    
    getDomProp: function (propName) {
        var me = this,
            result = (me.dirty && (propName in me.dirty)) ? undefined : me.props[propName];

        me.addTrigger(propName, true);
        return result;
    },

    
    getStyle: function (styleName) {
        var me = this,
            styles = me.styles,
            info, value;

        if (styleName in styles) {
            value = styles[styleName];
        } else {
            info = me.styleInfo[styleName];
            value = me.el.getStyle(styleName);

            if (info && info.parseInt) {
                value = parseInt(value, 10) || 0;
            }

            styles[styleName] = value;
        }

        return value;
    },

    
    getStyles: function (styleNames, altNames) {
        var me = this,
            styleCache = me.styles,
            values = {},
            hits = 0,
            n = styleNames.length,
            i, missing, missingAltNames, name, info, styleInfo, styles, value;

        altNames = altNames || styleNames;

        
        
        for (i = 0; i < n; ++i) {
            name = styleNames[i];

            if (name in styleCache) {
                values[altNames[i]] = styleCache[name];
                ++hits;

                if (i && hits==1) { 
                    missing = styleNames.slice(0, i);
                    missingAltNames = altNames.slice(0, i);
                }
            } else if (hits) {
                (missing || (missing = [])).push(name);
                (missingAltNames || (missingAltNames = [])).push(altNames[i]);
            }
        }

        if (hits < n) {
            missing = missing || styleNames;
            missingAltNames = missingAltNames || altNames;
            styleInfo = me.styleInfo;

            styles = me.el.getStyle(missing);

            for (i = missing.length; i--; ) {
                name = missing[i];
                info = styleInfo[name];
                value = styles[name];

                if (info && info.parseInt) {
                    value = parseInt(value, 10) || 0;
                }

                values[missingAltNames[i]] = value;
                styleCache[name] = value;
            }
        }

        return values;
    },

    
    hasProp: function (propName) {
        return this.getProp(propName) != null;
    },

    
    hasDomProp: function (propName) {
        return this.getDomProp(propName) != null;
    },

    
    invalidate: function (options) {
        this.context.queueInvalidate(this, options);
    },

    markDirty: function () {
        if (++this.dirtyCount == 1) {
            
            this.context.queueFlush(this);
        }
    },

    onBoxMeasured: function () {
        var boxParent = this.boxParent,
            state = this.state;

        if (boxParent && boxParent.widthModel.shrinkWrap && !state.boxMeasured && this.measuresBox) {
            
            
            state.boxMeasured = 1; 
            boxParent.boxChildMeasured();
        }
    },

    parseMargins: function (comp, margins, defaultMargins) {
        if (margins === true) {
            margins = 5;
        }

        var type = typeof margins,
            ret;

        if (type == 'string' || type == 'number') {
            ret = comp.parseBox(margins);
        } else if (margins || defaultMargins) {
            ret = { top: 0, right: 0, bottom: 0, left: 0 }; 

            if (defaultMargins) {
                Ext.apply(ret, this.parseMargins(comp, defaultMargins)); 
            }

            if (margins) {
                margins = Ext.apply(ret, comp.parseBox(margins)); 
            }
        }

        return ret;
    },

    peek: function (propName) {
        return this.props[propName];
    },

    
    recoverProp: function (propName, oldProps, oldDirty) {
        var me = this,
            props = me.props,
            dirty;

        if (propName in oldProps) {
            props[propName] = oldProps[propName];

            if (oldDirty && propName in oldDirty) {
                dirty = me.dirty || (me.dirty = {});
                dirty[propName] = oldDirty[propName];
            }
        }
    },

    redo: function(deep) {
        var me = this,
            items, len, i;

        me.revertProps(me.props);

        if (deep && me.wrapsComponent) {
            
            if (me.childItems) {
                for (i = 0, items = me.childItems, len = items.length; i < len; i++) {
                    items[i].redo(deep);
                }
            }

            
            for (i = 0, items = me.children, len = items.length; i < len; i++) {
                items[i].redo();
            }
        }
    },

    
    removeEl: function(nameOrEl, owner) {
        var me = this,
            src, el;

        if (nameOrEl) {
            if (nameOrEl.dom) {
                el = nameOrEl;
            } else {
                src = me.target;
                if (owner) {
                    src = owner;
                }

                el = src[nameOrEl];
                if (typeof el == 'function') { 
                    el = el.call(src);
                    if (el === me.el) {
                        return this; 
                    }
                }
            }

            if (el) {
                me.context.removeEl(me, el);
            }
        }
    },

    revertProps: function (props) {
        var name,
            flushed = this.flushedProps,
            reverted = {};

        for (name in props) {
            if (flushed.hasOwnProperty(name)) {
                reverted[name] = props[name];
            }
        }

        this.writeProps(reverted);
    },

    
    setAttribute: function(name, value) {
        var me = this;
        if (!me.attributes) {
            me.attributes = {};
        }
        me.attributes[name] = value;
        me.markDirty();
    },

    setBox: function (box) {
        var me = this;

        if ('left' in box) {
            me.setProp('x', box.left);
        }
        if ('top' in box) {
            me.setProp('y', box.top);
        }

        
        
        
        me.setSize(box.width, box.height);
    },

    
    setContentHeight: function (height, measured) {
        if (!measured && this.hasRawContent) {
            return 1;
        }

        return this.setProp('contentHeight', height);
    },

    
    setContentWidth: function (width, measured) {
        if (!measured && this.hasRawContent) {
            return 1;
        }

        return this.setProp('contentWidth', width);
    },

    
    setContentSize: function (width, height, measured) {
        return this.setContentWidth(width, measured) +
               this.setContentHeight(height, measured) == 2;
    },

    
    setProp: function (propName, value, dirty) {
        var me = this,
            valueType = typeof value,
            borderBox, info;

        if (valueType == 'undefined' || (valueType === 'number' && isNaN(value))) {
            return 0;
        }
        if (me.props[propName] === value) {
            return 1;
        }

        me.props[propName] = value;
        ++me.context.progressCount;

        if (dirty === false) {
            
            
            me.fireTriggers('domTriggers', propName);
            me.clearBlocks('domBlocks', propName);
        } else {
            info = me.styleInfo[propName];
            if (info) {
                if (!me.dirty) {
                    me.dirty = {};
                }

                if (propName == 'width' || propName == 'height') {
                    borderBox = me.isBorderBoxValue;
                    if (borderBox === null) {
                        me.isBorderBoxValue = borderBox = !!me.el.isBorderBox();
                    }

                    if (!borderBox) {
                        me.borderInfo || me.getBorderInfo();
                        me.paddingInfo || me.getPaddingInfo();
                    }
                }
                me.dirty[propName] = value;
                me.markDirty();
            }
        }

        
        me.fireTriggers('triggers', propName);
        me.clearBlocks('blocks', propName);
        return 1;
    },

    
    setHeight: function (height, dirty ) {
        var me = this,
            comp = me.target,
            ownerCtContext = me.ownerCtContext,
            frameBody, frameInfo, min, oldHeight, rem;

        if (height < 0) {
            height = 0;
        }
        if (!me.wrapsComponent) {
            if (!me.setProp('height', height, dirty)) {
                return NaN;
            }
        } else {
            min = me.collapsedVert ? 0 : (comp.minHeight || 0);
            height = Ext.Number.constrain(height, min, comp.maxHeight);
            oldHeight = me.props.height;
            if (!me.setProp('height', height, dirty)) {
                return NaN;
            }

            
            
            if (ownerCtContext && !me.isComponentChild && isNaN(oldHeight)) {
                rem = --ownerCtContext.remainingChildDimensions;
                if (!rem) {
                    
                    
                    
                    ownerCtContext.setProp('containerChildrenSizeDone', true);
                }
            }

            frameBody = me.frameBodyContext;
            if (frameBody){
                frameInfo = me.getFrameInfo();
                frameBody.setHeight(height - frameInfo.height, dirty);
            }
        }

        return height;
    },

    
    setWidth: function (width, dirty ) {
        var me = this,
            comp = me.target,
            ownerCtContext = me.ownerCtContext,
            frameBody, frameInfo, min, oldWidth, rem;

        if (width < 0) {
            width = 0;
        }
        if (!me.wrapsComponent) {
            if (!me.setProp('width', width, dirty)) {
                return NaN;
            }
        } else {
            min = me.collapsedHorz ? 0 : (comp.minWidth || 0);
            width = Ext.Number.constrain(width, min, comp.maxWidth);
            oldWidth = me.props.width
            if (!me.setProp('width', width, dirty)) {
                return NaN;
            }

            
            
            if (ownerCtContext && !me.isComponentChild && isNaN(oldWidth)) {
                rem = --ownerCtContext.remainingChildDimensions;
                if (!rem) {
                    
                    
                    
                    ownerCtContext.setProp('containerChildrenSizeDone', true);
                }
            }

            
            frameBody = me.frameBodyContext;
            if (frameBody) {
                frameInfo = me.getFrameInfo();
                frameBody.setWidth(width - frameInfo.width, dirty);
            }

            
        }

        return width;
    },

    setSize: function (width, height, dirty) {
        this.setWidth(width, dirty);
        this.setHeight(height, dirty);
    },

    translateProps: {
        x: 'left',
        y: 'top'
    },

    undo: function(deep) {
        var me = this,
            items, len, i;

        me.revertProps(me.lastBox);

        if (deep && me.wrapsComponent) {
            
            if (me.childItems) {
                for (i = 0, items = me.childItems, len = items.length; i < len; i++) {
                    items[i].undo(deep);
                }
            }

            
            for (i = 0, items = me.children, len = items.length; i < len; i++) {
                items[i].undo();
            }
        }
    },

    unsetProp: function (propName) {
        var dirty = this.dirty;

        delete this.props[propName];
        if (dirty) {
            delete dirty[propName];
        }
    },

    writeProps: function(dirtyProps, flushing) {
        if (!(dirtyProps && typeof dirtyProps == 'object')) {
            return;
        }

        var me = this,
            el = me.el,
            styles = {},
            styleCount = 0, 
            styleInfo = me.styleInfo,

            info,
            propName,
            numericValue,
            width = dirtyProps.width,
            height = dirtyProps.height,
            isBorderBox = me.isBorderBoxValue,
            target = me.target,
            max = Math.max,
            paddingWidth = 0,
            paddingHeight = 0,
            hasWidth, hasHeight, isAbsolute, scrollbarSize, style, targetEl;

        
        if ('displayed' in dirtyProps) {
            el.setDisplayed(dirtyProps.displayed);
        }

        
        for (propName in dirtyProps) {
            if (flushing) {
                me.fireTriggers('domTriggers', propName);
                me.clearBlocks('domBlocks', propName);
                me.flushedProps[propName] = 1;
            }

            info = styleInfo[propName];
            if (info && info.dom) {
                
                if (info.suffix && (numericValue = parseInt(dirtyProps[propName], 10))) {
                    styles[propName] = numericValue + info.suffix;
                }
                
                else {
                    styles[propName] = dirtyProps[propName];
                }
                ++styleCount;
            }
        }

        
        if ('x' in dirtyProps || 'y' in dirtyProps) {
            if (target.isComponent) {
                target.setPosition(dirtyProps.x, dirtyProps.y);
            } else {
                
                styleCount += me.addPositionStyles(styles, dirtyProps);
            }
        }

        
        if (!isBorderBox && (width > 0 || height > 0)) { 
            
            
            if(!me.frameBodyContext) {
                
                paddingWidth = me.paddingInfo.width;
                paddingHeight = me.paddingInfo.height;
            }
            if (width) {
                width = max(parseInt(width, 10) - (me.borderInfo.width + paddingWidth), 0);
                styles.width = width + 'px';
                ++styleCount;
            }
            if (height) {
                height = max(parseInt(height, 10) - (me.borderInfo.height + paddingHeight), 0);
                styles.height = height + 'px';
                ++styleCount;
            }
        }

        
        
        
        
        
        
        if (me.wrapsComponent && Ext.isIE9 && Ext.isStrict) {
            
            
            if ((hasWidth = width !== undefined && me.hasOverflowY) ||
                (hasHeight = height !== undefined && me.hasOverflowX)) {
                
                isAbsolute = me.isAbsolute;
                if (isAbsolute === undefined) {
                    isAbsolute = false;
                    targetEl = me.target.getTargetEl();
                    style = targetEl.getStyle('position');

                    if (style == 'absolute') {
                        style = targetEl.getStyle('box-sizing');
                        isAbsolute = (style == 'border-box');
                    }

                    me.isAbsolute = isAbsolute; 
                }

                if (isAbsolute) {
                    scrollbarSize = Ext.getScrollbarSize();

                    if (hasWidth) {
                        width = parseInt(width, 10) + scrollbarSize.width;
                        styles.width = width + 'px';
                        ++styleCount;
                    }
                    if (hasHeight) {
                        height = parseInt(height, 10) + scrollbarSize.height;
                        styles.height = height + 'px';
                        ++styleCount;
                    }
                }
            }
        }

        
        if (styleCount) {
            el.setStyle(styles);
        }
    }
}, function () {

    var px =    { dom: true, parseInt: true, suffix: 'px' },
        isDom = { dom: true },
        faux =  { dom: false };

    
    
    
    
    
    
    
    
    this.prototype.styleInfo = {
        containerChildrenSizeDone:  faux,
        containerLayoutDone:    faux,
        displayed:              faux,
        done:                   faux,
        x:                      faux,
        y:                      faux,

        
        columnWidthsDone:       faux,

        left:                   px,
        top:                    px,
        right:                  px,
        bottom:                 px,
        width:                  px,
        height:                 px,

        'border-top-width':     px,
        'border-right-width':   px,
        'border-bottom-width':  px,
        'border-left-width':    px,

        'margin-top':           px,
        'margin-right':         px,
        'margin-bottom':        px,
        'margin-left':          px,

        'padding-top':          px,
        'padding-right':        px,
        'padding-bottom':       px,
        'padding-left':         px,

        'line-height':          isDom,
        display:                isDom
    };
});


Ext.define('Ext.layout.Context', {
               
                         
                                 
                            
                      
                        
      

    remainingLayouts: 0,

    
    state: 0,

    constructor: function (config) {
        var me = this;

        Ext.apply(me, config);

        
        me.items = {};

        
        me.layouts = {};

        
        me.blockCount = 0;
        
        me.cycleCount = 0;
        
        me.flushCount = 0;
        
        me.calcCount = 0;

        me.animateQueue = me.newQueue();
        me.completionQueue = me.newQueue();
        me.finalizeQueue = me.newQueue();
        me.finishQueue = me.newQueue();
        me.flushQueue = me.newQueue();

        me.invalidateData = {};

        
        me.layoutQueue = me.newQueue();

        
        
        me.invalidQueue = [];

        me.triggers = {
            data: {
                
            },
            dom: {}
        };
    },

    callLayout: function (layout, methodName) {
        this.currentLayout = layout;
        layout[methodName](this.getCmp(layout.owner));
    },

    cancelComponent: function (comp, isChild, isDestroying) {
        var me = this,
            components = comp,
            isArray = !comp.isComponent,
            length = isArray ? components.length : 1,
            i, k, klen, items, layout, newQueue, oldQueue, entry, temp,
            ownerCtContext;

        for (i = 0; i < length; ++i) {
            if (isArray) {
                comp = components[i];
            }

            
            if (isDestroying && comp.ownerCt) {
                ownerCtContext = this.items[comp.ownerCt.el.id];
                if (ownerCtContext) {
                    Ext.Array.remove(ownerCtContext.childItems, me.getCmp(comp));
                }
            }

            if (!isChild) {
                oldQueue = me.invalidQueue;
                klen = oldQueue.length;

                if (klen) {
                    me.invalidQueue = newQueue = [];
                    for (k = 0; k < klen; ++k) {
                        entry = oldQueue[k];
                        temp = entry.item.target;
                        if (temp != comp && !temp.isDescendant(comp)) {
                            newQueue.push(entry);
                        }
                    }
                }
            }

            layout = comp.componentLayout;
            me.cancelLayout(layout);

            if (layout.getLayoutItems) {
                items = layout.getLayoutItems();
                if (items.length) {
                    me.cancelComponent(items, true);
                }
            }

            if (comp.isContainer && !comp.collapsed) {
                layout = comp.layout;
                me.cancelLayout(layout);

                items = layout.getVisibleItems();
                if (items.length) {
                    me.cancelComponent(items, true);
                }
            }
        }
    },

    cancelLayout: function (layout) {
        var me = this;

        me.completionQueue.remove(layout);
        me.finalizeQueue.remove(layout);
        me.finishQueue.remove(layout);
        me.layoutQueue.remove(layout);

        if (layout.running) {
            me.layoutDone(layout);
        }

        layout.ownerContext = null;
    },

    clearTriggers: function (layout, inDom) {
        var id = layout.id,
            collection = this.triggers[inDom ? 'dom' : 'data'],
            triggers = collection && collection[id],
            length = (triggers && triggers.length) || 0,
            i, item, trigger;

        for (i = 0; i < length; ++i) {
            trigger = triggers[i];
            item = trigger.item;

            collection = inDom ? item.domTriggers : item.triggers;
            delete collection[trigger.prop][id];
        }
    },

    
    flush: function () {
        var me = this,
            items = me.flushQueue.clear(),
            length = items.length, i;

        if (length) {
            ++me.flushCount;

            for (i = 0; i < length; ++i) {
                items[i].flush();
            }
        }
    },

    flushAnimations: function() {
        var me = this,
            items = me.animateQueue.clear(),
            len = items.length,
            i;

        if (len) {
            for (i = 0; i < len; i++) {
                
                
                
                if (items[i].target.animate !== false) {
                    items[i].flushAnimations();
                }
            }

            
            
            Ext.fx.Manager.runner();
        }
    },

    flushInvalidates: function () {
        var me = this,
            queue = me.invalidQueue,
            length = queue && queue.length,
            comp, components, entry, i;

        me.invalidQueue = [];

        if (length) {
            components = [];
            for (i = 0; i < length; ++i) {
                comp = (entry = queue[i]).item.target;
                
                
                
                
                if (!comp.container.isDetachedBody) {
                    components.push(comp);

                    if (entry.options) {
                        me.invalidateData[comp.id] = entry.options;
                    }
                }
            }

            me.invalidate(components, null);
        }
    },

    flushLayouts: function (queueName, methodName, dontClear) {
        var me = this,
            layouts = dontClear ? me[queueName].items : me[queueName].clear(),
            length = layouts.length,
            i, layout;

        if (length) {
            for (i = 0; i < length; ++i) {
                layout = layouts[i];
                if (!layout.running) {
                    me.callLayout(layout, methodName);
                }
            }
            me.currentLayout = null;
        }
    },

    
    getCmp: function (cmp) {
        return this.getItem(cmp, cmp.el);
    },

    
    getEl: function (parent, el) {
        var item = this.getItem(el, el);

        if (!item.parent) {
            item.parent = parent;

            
            
            
            if (parent.children.length) {
                parent.children.push(item);
            } else {
                parent.children = [ item ]; 
            }
        }

        return item;
    },

    getItem: function (target, el) {
        var id = el.id,
            items = this.items,
            item = items[id] ||
                  (items[id] = new Ext.layout.ContextItem({
                                    context: this,
                                    target: target,
                                    el: el
                                }));

        return item;
    },

    handleFailure: function () {
        
        
        
        var layouts = this.layouts,
            layout, key;

        Ext.failedLayouts = (Ext.failedLayouts || 0) + 1;

        for (key in layouts) {
            layout = layouts[key];

            if (layouts.hasOwnProperty(key)) {
                layout.running      = false;
                layout.ownerContext = null;
            }
        }

    },

    
    invalidate: function (components, full) {
        var me = this,
            isArray = !components.isComponent,
            containerLayoutDone,
            firstTime, i, comp, item, items, length, componentLayout, layout,
            invalidateOptions, token;

        for (i = 0, length = isArray ? components.length : 1; i < length; ++i) {
            comp = isArray ? components[i] : components;

            if (comp.rendered && !comp.hidden) {
                item = me.getCmp(comp);
                
                
                




                componentLayout = comp.componentLayout;
                firstTime = !componentLayout.ownerContext;
                layout = (comp.isContainer && !comp.collapsed) ? comp.layout : null;

                
                invalidateOptions = me.invalidateData[item.id];
                delete me.invalidateData[item.id];

                
                
                
                
                
                
                token = item.init(full, invalidateOptions);

                if (invalidateOptions) {
                    me.processInvalidate(invalidateOptions, item, 'before');
                }

                
                
                
                if (componentLayout.beforeLayoutCycle) {
                    componentLayout.beforeLayoutCycle(item);
                }
                
                if (layout && layout.beforeLayoutCycle) {
                    
                    
                    
                    
                    layout.beforeLayoutCycle(item);
                }

                
                
                token = item.initContinue(token);

                
                
                containerLayoutDone = true;

                
                
                
                
                if (componentLayout.getLayoutItems) {
                    componentLayout.renderChildren();

                    items = componentLayout.getLayoutItems();
                    if (items.length) {
                        me.invalidate(items, true);
                    }
                }

                if (layout) {
                    containerLayoutDone = false;
                    layout.renderChildren();

                    items = layout.getVisibleItems();
                    if (items.length) {
                        me.invalidate(items, true);
                    }
                }

                
                
                item.initDone(containerLayoutDone);

                
                
                me.resetLayout(componentLayout, item, firstTime);
                if (layout) {
                    me.resetLayout(layout, item, firstTime);
                }

                
                
                
                item.initAnimation();

                if (invalidateOptions) {
                    me.processInvalidate(invalidateOptions, item, 'after');
                }
            }
        }

        me.currentLayout = null;
    },

    layoutDone: function (layout) {
        var ownerContext = layout.ownerContext;

        layout.running = false;

        
        if (layout.isComponentLayout) {
            if (ownerContext.measuresBox) {
                ownerContext.onBoxMeasured(); 
            }

            ownerContext.setProp('done', true);
        } else {
            ownerContext.setProp('containerLayoutDone', true);
        }

        --this.remainingLayouts;
        ++this.progressCount; 
    },

    newQueue: function () {
        return new Ext.util.Queue();
    },

    processInvalidate: function (options, item, name) {
        
        
        if (options[name]) {
            var me = this,
                currentLayout = me.currentLayout;

            me.currentLayout = options.layout || null;

            options[name](item, options);

            me.currentLayout = currentLayout;
        }
    },

    
    queueAnimation: function (item) {
        this.animateQueue.add(item);
    },

    
    queueCompletion: function (layout) {
        this.completionQueue.add(layout);
    },

    
    queueFinalize: function (layout) {
        this.finalizeQueue.add(layout);
    },

    
    queueFlush: function (item) {
        this.flushQueue.add(item);
    },

    chainFns: function (oldOptions, newOptions, funcName) {
        var me = this,
            oldLayout = oldOptions.layout,
            newLayout = newOptions.layout,
            oldFn = oldOptions[funcName],
            newFn = newOptions[funcName];

        
        
        return function (contextItem) {
            var prev = me.currentLayout;
            if (oldFn) {
                me.currentLayout = oldLayout;
                oldFn.call(oldOptions.scope || oldOptions, contextItem, oldOptions);
            }
            me.currentLayout = newLayout;
            newFn.call(newOptions.scope || newOptions, contextItem, newOptions);
            me.currentLayout = prev;
        };
    },

    
    queueInvalidate: function (item, options) {
        var me = this,
            newQueue = [],
            oldQueue = me.invalidQueue,
            index = oldQueue.length,
            comp, old, oldComp, oldOptions, oldState;

        if (item.isComponent) {
            item = me.getCmp(comp = item);
        } else {
            comp = item.target;
        }

        item.invalid = true;

        
        
        
        while (index--) {
            old = oldQueue[index];
            oldComp = old.item.target;

            if (comp.isDescendant(oldComp)) {
                return; 
            }

            if (oldComp == comp) {
                
                if (!(oldOptions = old.options)) {
                    old.options = options;
                } else if (options) {
                    if (options.widthModel) {
                        oldOptions.widthModel = options.widthModel;
                    }
                    if (options.heightModel) {
                        oldOptions.heightModel = options.heightModel;
                    }
                    if (!(oldState = oldOptions.state)) {
                        oldOptions.state = options.state;
                    } else if (options.state) {
                        Ext.apply(oldState, options.state);
                    }

                    if (options.before) {
                        oldOptions.before = me.chainFns(oldOptions, options, 'before');
                    }
                    if (options.after) {
                        oldOptions.after = me.chainFns(oldOptions, options, 'after');
                    }
                }

                
                return;
            }

            if (!oldComp.isDescendant(comp)) {
                newQueue.push(old); 
            }
            
        }
        

        
        
        newQueue.push({ item: item, options: options });

        me.invalidQueue = newQueue;
    },

    queueItemLayouts: function (item) {
        var comp = item.isComponent ? item : item.target,
            layout = comp.componentLayout;

        if (!layout.pending && !layout.invalid && !layout.done) {
            this.queueLayout(layout);
        }

        layout = comp.layout;
        if (layout && !layout.pending && !layout.invalid && !layout.done) {
            this.queueLayout(layout);
        }
    },

    
    queueLayout: function (layout) {
        this.layoutQueue.add(layout);
        layout.pending = true;
    },

    
    removeEl: function (parent, el) {
        var id = el.id,
            children = parent.children,
            items = this.items;

        if(children) {
            Ext.Array.remove(children, items[id]);
        }
        delete items[id];
    },

    
    resetLayout: function (layout, ownerContext, firstTime) {
        var me = this;

        me.currentLayout = layout;

        layout.done = false;
        layout.pending = true;
        layout.firedTriggers = 0;

        me.layoutQueue.add(layout);

        if (firstTime) {
            me.layouts[layout.id] = layout; 
            layout.running = true;

            if (layout.finishedLayout) {
                me.finishQueue.add(layout);
            }

            

            ++me.remainingLayouts;
            ++layout.layoutCount; 

            layout.ownerContext = ownerContext;
            layout.beginCount = 0; 
            layout.blockCount = 0; 
            layout.calcCount = 0; 
            layout.triggerCount = 0; 

            if (!layout.initialized) {
                layout.initLayout();
            }

            layout.beginLayout(ownerContext);
        } else {
            ++layout.beginCount;

            if (!layout.running) {
                
                ++me.remainingLayouts;
                layout.running = true;

                if (layout.isComponentLayout) {
                    
                    
                    
                    ownerContext.unsetProp('done');
                }

                
                me.completionQueue.remove(layout);
                me.finalizeQueue.remove(layout);
            }
        }

        layout.beginLayoutCycle(ownerContext, firstTime);
    },

    
    run: function () {
        var me = this,
            flushed = false,
            watchDog = 100;

        me.flushInvalidates();

        me.state = 1;
        me.totalCount = me.layoutQueue.getCount();

        
        
        
        
        
        
        me.flush();

        
        while ((me.remainingLayouts || me.invalidQueue.length) && watchDog--) {
            if (me.invalidQueue.length) {
                me.flushInvalidates();
            }

            
            if (me.runCycle()) {
                flushed = false; 
                
            } else if (!flushed) {
                
                
                me.flush();
                flushed = true; 

                me.flushLayouts('completionQueue', 'completeLayout');
            } else if (!me.invalidQueue.length) {
                
                me.state = 2;
                break;
            }

            if (!(me.remainingLayouts || me.invalidQueue.length)) {
                me.flush();
                me.flushLayouts('completionQueue', 'completeLayout');
                me.flushLayouts('finalizeQueue', 'finalizeLayout');
            }
        }

        return me.runComplete();
    },

    runComplete: function () {
        var me = this;

        me.state = 2;

        if (me.remainingLayouts) {
            me.handleFailure();
            return false;
        }

        me.flush();

        
        me.flushLayouts('finishQueue', 'finishedLayout', true);

        
        me.flushLayouts('finishQueue', 'notifyOwner');

        me.flush(); 

        me.flushAnimations();

        return true;
    },

    
    runCycle: function () {
        var me = this,
            layouts = me.layoutQueue.clear(),
            length = layouts.length,
            i;

        ++me.cycleCount;

        
        
        me.progressCount = 0;

        for (i = 0; i < length; ++i) {
            me.runLayout(me.currentLayout = layouts[i]);
        }

        me.currentLayout = null;

        return me.progressCount > 0;
    },

    
    runLayout: function (layout) {
        var me = this,
            ownerContext = me.getCmp(layout.owner);

        layout.pending = false;

        if (ownerContext.state.blocks) {
            return;
        }

        
        
        
        
        layout.done = true;

        ++layout.calcCount;
        ++me.calcCount;

        layout.calculate(ownerContext);

        if (layout.done) {
            me.layoutDone(layout);

            if (layout.completeLayout) {
                me.queueCompletion(layout);
            }
            if (layout.finalizeLayout) {
                me.queueFinalize(layout);
            }
        } else if (!layout.pending && !layout.invalid && !(layout.blockCount + layout.triggerCount - layout.firedTriggers)) {
            
            
            me.queueLayout(layout);
        }
    },

    
    setItemSize: function(item, width, height) {
        var items = item,
            len = 1,
            contextItem, i;

        
        
        
        
        

        if (item.isComposite) {
            items = item.elements;
            len = items.length;
            item = items[0];
        } else if (!item.dom && !item.el) { 
            len = items.length;
            item = items[0];
        }
        

        for (i = 0; i < len; ) {
            contextItem = this.get(item);
            contextItem.setSize(width, height);

            item = items[++i]; 
        }
    }
});














Ext.define('Ext.layout.component.Body', {

    

    alias: ['layout.body'],

    extend:  Ext.layout.component.Auto ,

    

    type: 'body',

    beginLayout: function (ownerContext) {
        this.callParent(arguments);

        ownerContext.bodyContext = ownerContext.getEl('body');
    },

    beginLayoutCycle: function(ownerContext, firstCycle){
        var me = this,
            lastWidthModel = me.lastWidthModel,
            lastHeightModel = me.lastHeightModel,
            body = me.owner.body;

        me.callParent(arguments);

        if (lastWidthModel && lastWidthModel.fixed && ownerContext.widthModel.shrinkWrap) {
            body.setWidth(null);
        }

        if (lastHeightModel && lastHeightModel.fixed && ownerContext.heightModel.shrinkWrap) {
            body.setHeight(null);
        }
    },

    
    
    
    
    
    

    calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
        var height = this.callParent(arguments);

        if (ownerContext.targetContext != ownerContext) {
            height += ownerContext.getPaddingInfo().height;
        }

        return height;
    },

    calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) {
        var width = this.callParent(arguments);

        if (ownerContext.targetContext != ownerContext) {
            width += ownerContext.getPaddingInfo().width;
        }

        return width;
    },

    measureContentWidth: function (ownerContext) {
        return ownerContext.bodyContext.setWidth(ownerContext.bodyContext.el.dom.offsetWidth, false);
    },

    measureContentHeight: function (ownerContext) {
        return ownerContext.bodyContext.setHeight(ownerContext.bodyContext.el.dom.offsetHeight, false);
    },

    publishInnerHeight: function (ownerContext, height) {
        var innerHeight = height - ownerContext.getFrameInfo().height,
            targetContext = ownerContext.targetContext;

        if (targetContext != ownerContext) {
            innerHeight -= ownerContext.getPaddingInfo().height;
        }

        
        return ownerContext.bodyContext.setHeight(innerHeight, !ownerContext.heightModel.natural);
    },

    publishInnerWidth: function (ownerContext, width) {
        var innerWidth = width - ownerContext.getFrameInfo().width,
            targetContext = ownerContext.targetContext;

        if (targetContext != ownerContext) {
            innerWidth -= ownerContext.getPaddingInfo().width;
        }

        ownerContext.bodyContext.setWidth(innerWidth, !ownerContext.widthModel.natural);
    }
});


Ext.define('Ext.layout.component.FieldSet', {
    extend:  Ext.layout.component.Body ,
    alias: ['layout.fieldset'],

    type: 'fieldset',
    
    defaultCollapsedWidth: 100,

    beforeLayoutCycle: function (ownerContext) {
        if (ownerContext.target.collapsed) {
            ownerContext.heightModel = this.sizeModels.shrinkWrap;
        }
    },

    beginLayoutCycle: function (ownerContext) {
        var target = ownerContext.target,
            lastSize;

        this.callParent(arguments);

        
        
        
        if (target.collapsed) {
            ownerContext.setContentHeight(0);
            
            
            ownerContext.restoreMinHeight = target.minHeight;
            delete target.minHeight;

            
            
            
            if (ownerContext.widthModel.shrinkWrap) {
                lastSize = target.lastComponentSize;
                ownerContext.setContentWidth((lastSize && lastSize.contentWidth) || this.defaultCollapsedWidth);
            }
        }
    },
    
    finishedLayout: function(ownerContext) {
        var owner = this.owner,
            restore = ownerContext.restoreMinHeight;
             
        this.callParent(arguments);
        if (restore) {
            owner.minHeight = restore;
        }
    },

    calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
        var border = ownerContext.getBorderInfo(),
            legend = ownerContext.target.legend;
            
        
        
        return ownerContext.getProp('contentHeight') +
               ownerContext.getPaddingInfo().height +
               
               ((Ext.isIEQuirks || Ext.isIE8m) ?
                   ownerContext.bodyContext.getPaddingInfo().top : 0) +
               (legend ? legend.getHeight() : border.top) +
               border.bottom;
    },

    publishInnerHeight: function (ownerContext, height) {
        
        
        
        var legend = ownerContext.target.legend;
        if (legend) {
            height -= legend.getHeight();
        }
        this.callParent([ownerContext, height]);
    },

    getLayoutItems : function() {
        var legend = this.owner.legend;
        return legend ? [legend] : [];
    }
});


Ext.define('Ext.layout.component.field.Slider', {

    

    alias: ['layout.sliderfield'],

    extend:  Ext.layout.component.field.Field ,

    

    type: 'sliderfield',

    beginLayout: function(ownerContext) {
        this.callParent(arguments);

        ownerContext.endElContext   = ownerContext.getEl('endEl');
        ownerContext.innerElContext = ownerContext.getEl('innerEl');
        ownerContext.bodyElContext  = ownerContext.getEl('bodyEl');
    },

    publishInnerHeight: function (ownerContext, height) {
        var innerHeight = height - this.measureLabelErrorHeight(ownerContext),
            endElPad,
            inputPad;
        if (this.owner.vertical) {
            endElPad = ownerContext.endElContext.getPaddingInfo();
            inputPad = ownerContext.inputContext.getPaddingInfo();
            ownerContext.innerElContext.setHeight(innerHeight - inputPad.height - endElPad.height);
        } else {
            ownerContext.bodyElContext.setHeight(innerHeight);
        }
    },

    publishInnerWidth: function (ownerContext, width) {
        if (!this.owner.vertical) {
            var endElPad = ownerContext.endElContext.getPaddingInfo(),
                inputPad = ownerContext.inputContext.getPaddingInfo();

            ownerContext.innerElContext.setWidth(width - inputPad.left - endElPad.right - ownerContext.labelContext.getProp('width'));
        }
    },

    beginLayoutFixed: function(ownerContext, width, suffix) {
        var me = this,
            ieInputWidthAdjustment = me.ieInputWidthAdjustment;

        if (ieInputWidthAdjustment) {
            
            
            me.owner.bodyEl.setStyle('padding-right', ieInputWidthAdjustment + 'px');
        }

        me.callParent(arguments);
    }
});


Ext.define('Ext.layout.container.Absolute', {

    

    alias: 'layout.absolute',
    extend:  Ext.layout.container.Anchor ,
    alternateClassName: 'Ext.layout.AbsoluteLayout',

    

    targetCls: Ext.baseCSSPrefix + 'abs-layout-ct',
    itemCls: Ext.baseCSSPrefix + 'abs-layout-item',

    
    ignoreOnContentChange: true,

    type: 'absolute',

    
    adjustWidthAnchor: function(value, childContext) {
        var padding = this.targetPadding,
            x = childContext.getStyle('left');

        return value - x + padding.left;
    },

    
    adjustHeightAnchor: function(value, childContext) {
        var padding = this.targetPadding,
            y = childContext.getStyle('top');

        return value - y + padding.top;
    },

    isItemLayoutRoot: function (item) {
        return this.ignoreOnContentChange || this.callParent(arguments);
    },

    isItemShrinkWrap: function (item) {
        return true;
    },

    beginLayout: function (ownerContext) {
        var me = this,
            target = me.getTarget();

        me.callParent(arguments);

        
        if (target.dom !== document.body) {
            target.position();
        }

        me.targetPadding = ownerContext.targetContext.getPaddingInfo();
    },

    isItemBoxParent: function (itemContext) {
        return true;
    },

    onContentChange: function () {
        if (this.ignoreOnContentChange) {
            return false;
        }
        return this.callParent(arguments);
    },

    calculateContentSize: function (ownerContext, dimensions) {
        var me = this,
            containerDimensions = (dimensions || 0) |
                   ((ownerContext.widthModel.shrinkWrap ? 1 : 0) |
                    (ownerContext.heightModel.shrinkWrap ? 2 : 0)),
            calcWidth = (containerDimensions & 1) || undefined,
            calcHeight = (containerDimensions & 2) || undefined,
            childItems = ownerContext.childItems,
            length = childItems.length,
            contentHeight = 0,
            contentWidth = 0,
            needed = 0,
            props = ownerContext.props,
            targetPadding, child, childContext, height, i, margins, width;

        if (calcWidth) {
            if (isNaN(props.contentWidth)) {
                ++needed;
            } else {
                calcWidth = undefined;
            }
        }
        if (calcHeight) {
            if (isNaN(props.contentHeight)) {
                ++needed;
            } else {
                calcHeight = undefined;
            }
        }

        if (needed) {
            for (i = 0; i < length; ++i) {
                childContext = childItems[i];
                child = childContext.target;
                height = calcHeight && childContext.getProp('height');
                width = calcWidth && childContext.getProp('width');
                margins = childContext.getMarginInfo();

                height += margins.bottom;
                width  += margins.right;

                contentHeight = Math.max(contentHeight, (child.y || 0) + height);
                contentWidth = Math.max(contentWidth, (child.x || 0) + width);

                if (isNaN(contentHeight) && isNaN(contentWidth)) {
                    me.done = false;
                    return;
                }
            }

            if (calcWidth || calcHeight) {
                targetPadding = ownerContext.targetContext.getPaddingInfo();
            }
            if (calcWidth && !ownerContext.setContentWidth(contentWidth + targetPadding.width)) {
                me.done = false;
            }
            if (calcHeight && !ownerContext.setContentHeight(contentHeight + targetPadding.height)) {
                me.done = false;
            }

            
        }
    }
});


Ext.define('Ext.layout.container.Accordion', {
    extend:  Ext.layout.container.VBox ,
    alias: ['layout.accordion'],
    alternateClassName: 'Ext.layout.AccordionLayout',

    targetCls: Ext.baseCSSPrefix + 'accordion-layout-ct',
    itemCls: [Ext.baseCSSPrefix + 'box-item', Ext.baseCSSPrefix + 'accordion-item'],

    align: 'stretch',

    
    fill : true,

    

    
    titleCollapse : true,

    
    hideCollapseTool : false,

    
    collapseFirst : undefined,

    
    animate : true,
    
    activeOnTop : false,
    
    multi: false,
    
    defaultAnimatePolicy: {
        y: true,
        height: true
    },

    constructor: function() {
        var me = this;

        me.callParent(arguments);

        if (!me.multi && me.animate) {
            me.animatePolicy = Ext.apply({}, me.defaultAnimatePolicy);
        } else {
            me.animatePolicy = null;
        }
    },

    beforeRenderItems: function (items) {
        var me = this,
            ln = items.length,
            i = 0,
            owner = me.owner,
            collapseFirst = me.collapseFirst,
            hasCollapseFirst = Ext.isDefined(collapseFirst),
            expandedItem = me.getExpanded(true)[0],
            multi = me.multi,
            comp;

        for (; i < ln; i++) {
            comp = items[i];
            if (!comp.rendered) {
                
                if (!multi || comp.collapsible !== false) {
                    comp.collapsible = true;
                }
                
                if (comp.collapsible) {
                    if (hasCollapseFirst) {
                        comp.collapseFirst = collapseFirst;
                    }
                    if (me.hideCollapseTool) {
                        comp.hideCollapseTool = me.hideCollapseTool;
                        comp.titleCollapse = true;
                    } else if (me.titleCollapse && comp.titleCollapse === undefined) {
                        
                        
                        comp.titleCollapse = me.titleCollapse;
                    }
                }
                
                delete comp.hideHeader;
                delete comp.width;
                comp.title = comp.title || '&#160;';
                comp.addBodyCls(Ext.baseCSSPrefix + 'accordion-body');

                
                
                
                if (!multi) {
                    if (expandedItem) {
                        comp.collapsed = expandedItem !== comp;
                    } else if (comp.hasOwnProperty('collapsed') && comp.collapsed === false) {
                        expandedItem = comp;
                    } else {
                        comp.collapsed = true;
                    }

                    
                    owner.mon(comp, {
                        show: me.onComponentShow,
                        beforeexpand: me.onComponentExpand,
                        beforecollapse: me.onComponentCollapse,
                        scope: me
                    });
                }
                
                
                owner.mon(comp, 'beforecollapse', me.onComponentCollapse, me);
                comp.headerOverCls = Ext.baseCSSPrefix + 'accordion-hd-over';
            }
        }

        
        if (!multi) {
            if (!expandedItem) {
                if (ln) {
                    items[0].collapsed = false;
                }
            } else if (me.activeOnTop) {
                expandedItem.collapsed = false;
                me.configureItem(expandedItem);
                if (owner.items.indexOf(expandedItem) > 0) {
                    owner.insert(0, expandedItem);
                }
            }
        }
    },

    getItemsRenderTree: function(items) {
        this.beforeRenderItems(items);
        return this.callParent(arguments);
    },

    renderItems : function(items, target) {
        this.beforeRenderItems(items);

        this.callParent(arguments);
    },

    configureItem: function(item) {
        this.callParent(arguments);

        
        
        item.animCollapse = item.border = false;

        
        if (this.fill) {
            item.flex = 1;
        }
    },

    beginLayout: function (ownerContext) {
        this.callParent(arguments);
        this.updatePanelClasses(ownerContext);
    },

    updatePanelClasses: function(ownerContext) {
        var children = ownerContext.visibleItems,
            ln = children.length,
            siblingCollapsed = true,
            i, child, header;

        for (i = 0; i < ln; i++) {
            child = children[i];
            header = child.header;
            header.addCls(Ext.baseCSSPrefix + 'accordion-hd');

            if (siblingCollapsed) {
                header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
            } else {
                header.addCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
            }

            if (i + 1 == ln && child.collapsed) {
                header.addCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
            } else {
                header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
            }

            siblingCollapsed = child.collapsed;
        }
    },

    
    
    
    onComponentExpand: function(toExpand) {
        var me = this,
            owner = me.owner,
            multi = me.multi,
            animate = me.animate,
            moveToTop = !multi && !me.animate && me.activeOnTop,
            expanded,
            expandedCount, i,
            previousValue;

        if (!me.processing) {
            me.processing = true;
            previousValue = owner.deferLayouts;
            owner.deferLayouts = true;
            expanded = multi ? [] : me.getExpanded();
            expandedCount = expanded.length;
            
            
            for (i = 0; i < expandedCount; i++) {
                expanded[i].collapse();
            }
            
            if (moveToTop) {
                
                Ext.suspendLayouts();
                owner.insert(0, toExpand);
                Ext.resumeLayouts();
            }
            
            owner.deferLayouts = previousValue;
            me.processing = false;
        }
    },

    onComponentCollapse: function(comp) {
        var me = this,
            owner = me.owner,
            toExpand,
            expanded,
            previousValue;

        if (me.owner.items.getCount() === 1) {
            
            return false;
        }

        if (!me.processing) {
            me.processing = true;
            previousValue = owner.deferLayouts;
            owner.deferLayouts = true;
            toExpand = comp.next() || comp.prev();

            
            
            if (me.multi) {
                expanded = me.getExpanded();

                
                
                if (expanded.length === 1) {
                    toExpand.expand();
                }

            } else if (toExpand) {
                toExpand.expand();
            }
            owner.deferLayouts = previousValue;
            me.processing = false;
        }
    },

    onComponentShow: function(comp) {
        
        this.onComponentExpand(comp);
    },
    
    getExpanded: function(explicitCheck){
        var items = this.owner.items.items,
            len = items.length,
            i = 0,
            out = [],
            add,
            item;
            
        for (; i < len; ++i) {
            item = items[i];
            if (explicitCheck) {
                add = item.hasOwnProperty('collapsed') && item.collapsed === false;
            } else {
                add = !item.collapsed;
            }
            if (add) {
                out.push(item);
            }
        }
        return out;
            
    }
});


Ext.define('Ext.resizer.Splitter', {
    extend:  Ext.Component ,
                                
                                          
    alias: 'widget.splitter',

    childEls: [
        'collapseEl'
    ],

    renderTpl: [
        '<tpl if="collapsible===true">',
            '<div id="{id}-collapseEl" class="', Ext.baseCSSPrefix, 'collapse-el ',
                Ext.baseCSSPrefix, 'layout-split-{collapseDir}{childElCls}">&#160;',
            '</div>',
        '</tpl>'
    ],

    baseCls: Ext.baseCSSPrefix + 'splitter',
    collapsedClsInternal: Ext.baseCSSPrefix + 'splitter-collapsed',
    
    
    canResize: true,

    
    collapsible: false,

    

    
    collapseOnDblClick: true,

    
    defaultSplitMin: 40,

    
    defaultSplitMax: 1000,

    

    
    collapseTarget: 'next',

    

    horizontal: false,
    vertical: false,

    
    size: 5,

    
    getTrackerConfig: function () {
        return {
            xclass: 'Ext.resizer.SplitterTracker',
            el: this.el,
            splitter: this
        };
    },

    beforeRender: function() {
        var me = this,
            target = me.getCollapseTarget();

        me.callParent();

        if (target.collapsed) {
            me.addCls(me.collapsedClsInternal);
        }
        if (!me.canResize) {
            me.addCls(me.baseCls + '-noresize');
        }

        Ext.applyIf(me.renderData, {
            collapseDir: me.getCollapseDirection(),
            collapsible: me.collapsible || target.collapsible
        });

        me.protoEl.unselectable();
    },

    onRender: function() {
        var me = this,
            collapseEl;

        me.callParent(arguments);

        
        if (me.performCollapse !== false) {
            if (me.renderData.collapsible) {
                me.mon(me.collapseEl, 'click', me.toggleTargetCmp, me);
            }
            if (me.collapseOnDblClick) {
                me.mon(me.el, 'dblclick', me.toggleTargetCmp, me);
            }
        }

        
        me.mon(me.getCollapseTarget(), {
            collapse: me.onTargetCollapse,
            expand: me.onTargetExpand,
            beforeexpand: me.onBeforeTargetExpand,
            beforecollapse: me.onBeforeTargetCollapse,
            scope: me
        });

        if (me.canResize) {
            me.tracker = Ext.create(me.getTrackerConfig());
            
            me.relayEvents(me.tracker, [ 'beforedragstart', 'dragstart', 'dragend' ]);
        }

        collapseEl = me.collapseEl;
        if (collapseEl) {
            collapseEl.lastCollapseDirCls = me.collapseDirProps[me.collapseDirection].cls;
        }
    },

    getCollapseDirection: function() {
        var me = this,
            dir = me.collapseDirection,
            collapseTarget, idx, items, type;

        if (!dir) {
            collapseTarget = me.collapseTarget;
            if (collapseTarget.isComponent) {
                dir = collapseTarget.collapseDirection;
            }

            if (!dir) {
                
                
                
                
                
                
                
                type = me.ownerCt.layout.type;
                if (collapseTarget.isComponent) {
                    items = me.ownerCt.items;
                    idx = Number(items.indexOf(collapseTarget) === items.indexOf(me) - 1) << 1 | Number(type === 'hbox');
                } else {
                    idx = Number(me.collapseTarget === 'prev') << 1 | Number(type === 'hbox');
                }

                
                dir = ['bottom', 'right', 'top', 'left'][idx];
            }

            me.collapseDirection = dir;
        }

        me.setOrientation((dir === 'top' || dir === 'bottom') ? 'horizontal' : 'vertical');

        return dir;
    },

    getCollapseTarget: function() {
        var me = this;

        return me.collapseTarget.isComponent ? me.collapseTarget
                    : me.collapseTarget === 'prev' ? me.previousSibling() : me.nextSibling();
    },
    
    setCollapseEl: function(display){
        var el = this.collapseEl;
        if (el) {
            el.setDisplayed(display);
        }
    },
    
    onBeforeTargetExpand: function(target) {
        this.setCollapseEl('none');
    },
    
    onBeforeTargetCollapse: function(){
        this.setCollapseEl('none');
    },

    onTargetCollapse: function(target) {
        this.el.addCls([this.collapsedClsInternal, this.collapsedCls]);
        this.setCollapseEl('');
    },

    onTargetExpand: function(target) {
        this.el.removeCls([this.collapsedClsInternal, this.collapsedCls]);
        this.setCollapseEl('');
    },

    collapseDirProps: {
        top: {
            cls: Ext.baseCSSPrefix + 'layout-split-top'
        },
        right: {
            cls: Ext.baseCSSPrefix + 'layout-split-right'
        },
        bottom: {
            cls: Ext.baseCSSPrefix + 'layout-split-bottom'
        },
        left: {
            cls: Ext.baseCSSPrefix + 'layout-split-left'
        }
    },

    orientationProps: {
        horizontal: {
            opposite: 'vertical',
            fixedAxis: 'height',
            stretchedAxis: 'width'
        },
        vertical: {
            opposite: 'horizontal',
            fixedAxis: 'width',
            stretchedAxis: 'height'
        }
    },

    applyCollapseDirection: function () {
        var me = this,
            collapseEl = me.collapseEl,
            collapseDirProps = me.collapseDirProps[me.collapseDirection],
            cls;

        if (collapseEl) {
            cls = collapseEl.lastCollapseDirCls;
            if (cls) {
                collapseEl.removeCls(cls);
            }

            collapseEl.addCls(collapseEl.lastCollapseDirCls = collapseDirProps.cls);
        }
    },

    applyOrientation: function () {
        var me = this,
            orientation = me.orientation,
            orientationProps = me.orientationProps[orientation],
            defaultSize = me.size,
            fixedSizeProp = orientationProps.fixedAxis,
            stretchSizeProp = orientationProps.stretchedAxis,
            cls = me.baseCls + '-';

        me[orientation] = true;
        me[orientationProps.opposite] = false;

        if (!me.hasOwnProperty(fixedSizeProp) || me[fixedSizeProp] === '100%') {
            me[fixedSizeProp] = defaultSize;
        }
        if (!me.hasOwnProperty(stretchSizeProp) || me[stretchSizeProp] === defaultSize) {
            me[stretchSizeProp] = '100%';
        }

        me.removeCls(cls + orientationProps.opposite);
        me.addCls(cls + orientation);
    },

    setOrientation: function (orientation) {
        var me = this;

        if (me.orientation !== orientation) {
            me.orientation = orientation;
            me.applyOrientation();
        }
    },
    
    updateOrientation: function () {
        delete this.collapseDirection; 
        this.getCollapseDirection();
        this.applyCollapseDirection();
    },

    toggleTargetCmp: function(e, t) {
        var cmp = this.getCollapseTarget(),
            placeholder = cmp.placeholder,
            toggle;

        
        if (Ext.isFunction(cmp.expand) && Ext.isFunction(cmp.collapse)) {
            if (placeholder && !placeholder.hidden) {
                toggle = true;
            } else {
                toggle = !cmp.hidden;
            }

            if (toggle) {
                if (cmp.collapsed) {
                    cmp.expand();
                } else if (cmp.collapseDirection) {
                    cmp.collapse();
                } else {
                    cmp.collapse(this.renderData.collapseDir);
                }
            }
        }
    },

    
    setSize: function() {
        var me = this;
        me.callParent(arguments);
        if (Ext.isIE && me.el) {
            me.el.repaint();
        }
    },
    
    beforeDestroy: function(){
        Ext.destroy(this.tracker);
        this.callParent();
    }
});


Ext.define('Ext.resizer.BorderSplitter', {
    extend:  Ext.resizer.Splitter ,

                                                

    alias: 'widget.bordersplitter',

    
    collapseTarget: null,

    getTrackerConfig: function () {
        var trackerConfig = this.callParent();

        trackerConfig.xclass = 'Ext.resizer.BorderSplitterTracker';

        return trackerConfig;
    }
});


Ext.define('Ext.layout.container.Border', {

    extend:  Ext.layout.container.Container ,
    alias: 'layout.border',
    alternateClassName: 'Ext.layout.BorderLayout',

               
                                     
                      

                                                                  
                                            
      


    targetCls: Ext.baseCSSPrefix + 'border-layout-ct',

    itemCls: [Ext.baseCSSPrefix + 'border-item', Ext.baseCSSPrefix + 'box-item'],

    type: 'border',

    isBorderLayout: true,

    
    
    

    
    padding: undefined,

    percentageRe: /(\d+)%/,
    
    horzMarginProp: 'left',
    padOnContainerProp: 'left',
    padNotOnContainerProp: 'right',

    
    axisProps: {
        horz: {
            borderBegin: 'west',
            borderEnd: 'east',
            horizontal: true,
            posProp: 'x',
            sizeProp: 'width',
            sizePropCap: 'Width'
        },
        vert: {
            borderBegin: 'north',
            borderEnd: 'south',
            horizontal: false,
            posProp: 'y',
            sizeProp: 'height',
            sizePropCap: 'Height'
        }
    },

    
    centerRegion: null,

    manageMargins: true,

    panelCollapseAnimate: true,

    panelCollapseMode: 'placeholder',

    
    regionWeights: {
        north: 20,
        south: 10,
        center: 0,
        west: -10,
        east: -20
    },

    
    

    
    beginAxis: function (ownerContext, regions, name) {
        var me = this,
            props = me.axisProps[name],
            isVert = !props.horizontal,
            sizeProp = props.sizeProp,
            totalFlex = 0,
            childItems = ownerContext.childItems,
            length = childItems.length,
            center, i, childContext, centerFlex, comp, region, match, size, type, target, placeholder;

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            comp = childContext.target;

            childContext.layoutPos = {};

            if (comp.region) {
                childContext.region = region = comp.region;

                childContext.isCenter = comp.isCenter;
                childContext.isHorz = comp.isHorz;
                childContext.isVert = comp.isVert;

                childContext.weight = comp.weight || me.regionWeights[region] || 0;
                regions[comp.id] = childContext;

                if (comp.isCenter) {
                    center = childContext;
                    centerFlex = comp.flex;
                    ownerContext.centerRegion = center;

                    continue;
                }

                if (isVert !== childContext.isVert) {
                    continue;
                }

                

                childContext.reverseWeighting = (region == props.borderEnd);

                size = comp[sizeProp];
                type = typeof size;

                if (!comp.collapsed) {
                    if (type == 'string' && (match = me.percentageRe.exec(size))) {
                        childContext.percentage = parseInt(match[1], 10);
                    } else if (comp.flex) {
                        totalFlex += childContext.flex = comp.flex;
                    }
                }
            }
        }

        
        if (center) {
            target = center.target;

            if ((placeholder = target.placeholderFor)) {
                if (!centerFlex && isVert === placeholder.collapsedVertical()) {
                    
                    centerFlex = 0;
                    center.collapseAxis = name;
                }
            } else if (target.collapsed && (isVert === target.collapsedVertical())) {
                
                centerFlex = 0;
                center.collapseAxis = name;
            }
        }

        if (centerFlex == null) {
            
            centerFlex = 1;
        }

        totalFlex += centerFlex;

        return Ext.apply({
            before         : isVert ? 'top' : 'left',
            totalFlex      : totalFlex
        }, props);
    },

    beginLayout: function (ownerContext) {
        var me = this,
            items = me.getLayoutItems(),
            pad = me.padding,
            type = typeof pad,
            padOnContainer = false,
            childContext, item, length, i, regions, collapseTarget,
            doShow, hidden, region;

        
        if (pad) {
            if (type == 'string' || type == 'number') {
                pad = Ext.util.Format.parseBox(pad);
            }
        } else {
            pad = ownerContext.getEl('getTargetEl').getPaddingInfo();
            padOnContainer = true;
        }
        ownerContext.outerPad = pad;
        ownerContext.padOnContainer = padOnContainer;

        for (i = 0, length = items.length; i < length; ++i) {
            item = items[i];
            collapseTarget = me.getSplitterTarget(item);
            if (collapseTarget) {  
                doShow = undefined;
                hidden = !!item.hidden;
                if (!collapseTarget.split) {
                    if (collapseTarget.isCollapsingOrExpanding) {
                        doShow = !!collapseTarget.collapsed;
                    }
                } else if (hidden !== collapseTarget.hidden) {
                    doShow = !collapseTarget.hidden;
                }

                if (doShow) {
                    item.show();
                } else if (doShow === false) {
                    item.hide();
                }
            }
        }

        
        
        
        me.callParent(arguments);

        items = ownerContext.childItems;
        length = items.length;
        regions = {};

        ownerContext.borderAxisHorz = me.beginAxis(ownerContext, regions, 'horz');
        ownerContext.borderAxisVert = me.beginAxis(ownerContext, regions, 'vert');

        
        
        
        for (i = 0; i < length; ++i) {
            childContext = items[i];
            collapseTarget = me.getSplitterTarget(childContext.target);

            if (collapseTarget) { 
                region = regions[collapseTarget.id]
                if (!region) {
                        
                        
                        
                        region = ownerContext.getEl(collapseTarget.el, me);
                        region.region = collapseTarget.region;
                }
                childContext.collapseTarget = collapseTarget = region;
                childContext.weight = collapseTarget.weight;
                childContext.reverseWeighting = collapseTarget.reverseWeighting;
                collapseTarget.splitter = childContext;
                childContext.isHorz = collapseTarget.isHorz;
                childContext.isVert = collapseTarget.isVert;
            }
        }

        
        me.sortWeightedItems(items, 'reverseWeighting');
        me.setupSplitterNeighbors(items);
    },

    calculate: function (ownerContext) {
        var me = this,
            containerSize = me.getContainerSize(ownerContext),
            childItems = ownerContext.childItems,
            length = childItems.length,
            horz = ownerContext.borderAxisHorz,
            vert = ownerContext.borderAxisVert,
            pad = ownerContext.outerPad,
            padOnContainer = ownerContext.padOnContainer,
            i, childContext, childMargins, size, horzPercentTotal, vertPercentTotal;

        horz.begin = pad[me.padOnContainerProp];
        vert.begin = pad.top;
        
        
        
        horzPercentTotal = horz.end = horz.flexSpace = containerSize.width + (padOnContainer ? pad[me.padOnContainerProp] : -pad[me.padNotOnContainerProp]);
        vertPercentTotal = vert.end = vert.flexSpace = containerSize.height + (padOnContainer ? pad.top : -pad.bottom);

        
        
        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();

            
            if (childContext.isHorz || childContext.isCenter) {
                horz.addUnflexed(childMargins.width);
                horzPercentTotal -= childMargins.width;
            }

            if (childContext.isVert || childContext.isCenter) {
                vert.addUnflexed(childMargins.height);
                vertPercentTotal -= childMargins.height;
            }

            
            if (!childContext.flex && !childContext.percentage) {
                if (childContext.isHorz || (childContext.isCenter && childContext.collapseAxis === 'horz')) {
                    size = childContext.getProp('width');

                    horz.addUnflexed(size);

                    
                    if (childContext.collapseTarget) {
                        horzPercentTotal -= size;
                    }
                } else if (childContext.isVert || (childContext.isCenter && childContext.collapseAxis === 'vert')) {
                    size = childContext.getProp('height');

                    vert.addUnflexed(size);

                    
                    if (childContext.collapseTarget) {
                        vertPercentTotal -= size;
                    }
                }
                
            }
        }

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();

            
            if (childContext.percentage) {
                if (childContext.isHorz) {
                    size = Math.ceil(horzPercentTotal * childContext.percentage / 100);
                    size = childContext.setWidth(size);
                    horz.addUnflexed(size);
                } else if (childContext.isVert) {
                    size = Math.ceil(vertPercentTotal * childContext.percentage / 100);
                    size = childContext.setHeight(size);
                    vert.addUnflexed(size);
                }
                
            }
        }


        
        

        for (i = 0; i < length; ++i) {
            childContext = childItems[i];

            if (!childContext.isCenter) {
                me.calculateChildAxis(childContext, horz);
                me.calculateChildAxis(childContext, vert);
            }
        }

        
        
        
        if (me.finishAxis(ownerContext, vert) + me.finishAxis(ownerContext, horz) < 2) {
            me.done = false;
        } else {
            
            
            
            me.finishPositions(childItems);
        }
    },

    
    calculateChildAxis: function (childContext, axis) {
        var collapseTarget = childContext.collapseTarget,
            setSizeMethod = 'set' + axis.sizePropCap,
            sizeProp = axis.sizeProp,
            childMarginSize = childContext.getMarginInfo()[sizeProp],
            region, isBegin, flex, pos, size;

        if (collapseTarget) { 
            region = collapseTarget.region;
        } else {
            region = childContext.region;
            flex = childContext.flex;
        }

        isBegin = region == axis.borderBegin;

        if (!isBegin && region != axis.borderEnd) {
            
            
            childContext[setSizeMethod](axis.end - axis.begin - childMarginSize);
            pos = axis.begin;
        } else {
            if (flex) {
                size = Math.ceil(axis.flexSpace * (flex / axis.totalFlex));
                size = childContext[setSizeMethod](size);
            } else if (childContext.percentage) {
                
                size = childContext.peek(sizeProp);
            } else {
                size = childContext.getProp(sizeProp);
            }

            size += childMarginSize;

            if (isBegin) {
                pos = axis.begin;
                axis.begin += size;
            } else {
                axis.end = pos = axis.end - size;
            }
        }

        childContext.layoutPos[axis.posProp] = pos;
    },

    
    finishAxis: function (ownerContext, axis) {
        var size = axis.end - axis.begin,
            center = ownerContext.centerRegion;

        if (center) {
            center['set' + axis.sizePropCap](size - center.getMarginInfo()[axis.sizeProp]);
            center.layoutPos[axis.posProp] = axis.begin;
        }

        return Ext.isNumber(size) ? 1 : 0;
    },

    
    finishPositions: function (childItems) {
        var length = childItems.length,
            index, childContext,
            marginProp = this.horzMarginProp;

        for (index = 0; index < length; ++index) {
            childContext = childItems[index];

            childContext.setProp('x', childContext.layoutPos.x + childContext.marginInfo[marginProp]);
            childContext.setProp('y', childContext.layoutPos.y + childContext.marginInfo.top);
        }
    },

    getLayoutItems: function() {
        var owner = this.owner,
            ownerItems = (owner && owner.items && owner.items.items) || [],
            length = ownerItems.length,
            items = [],
            i = 0,
            ownerItem, placeholderFor;

        for (; i < length; i++) {
            ownerItem = ownerItems[i];
            placeholderFor = ownerItem.placeholderFor;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            if (ownerItem.hidden || ((!ownerItem.floated || ownerItem.isCollapsingOrExpanding === 2) &&
                !(placeholderFor && placeholderFor.isCollapsingOrExpanding === 2))) {
                items.push(ownerItem);
            } 
        }

        return items;
    },

    getPlaceholder: function (comp) {
        return comp.getPlaceholder && comp.getPlaceholder();
    },

    getSplitterTarget: function (splitter) {
        var collapseTarget = splitter.collapseTarget;

        if (collapseTarget && collapseTarget.collapsed) {
            return collapseTarget.placeholder || collapseTarget;
        }

        return collapseTarget;
    },

    isItemBoxParent: function (itemContext) {
        return true;
    },

    isItemShrinkWrap: function (item) {
        return true;
    },

    
    

    
    insertSplitter: function (item, index, hidden, splitterCfg) {
        var region = item.region,
            splitter = Ext.apply({
                xtype: 'bordersplitter',
                collapseTarget: item,
                id: item.id + '-splitter',
                hidden: hidden,
                canResize: item.splitterResize !== false,
                splitterFor: item
            }, splitterCfg),
            at = index + ((region === 'south' || region === 'east') ? 0 : 1);

        if (item.collapseMode === 'mini') {
            splitter.collapsedCls = item.collapsedCls;
        }

        item.splitter = this.owner.add(at, splitter);
    },

    
    onAdd: function (item, index) {
        var me = this,
            placeholderFor = item.placeholderFor,
            region = item.region,
            split,
            hidden,
            cfg;

        me.callParent(arguments);

        if (region) {
            Ext.apply(item, me.regionFlags[region]);

            if (item.initBorderRegion) {
                
                
                item.initBorderRegion();
            }

            if (region === 'center') {
                me.centerRegion = item;
            } else {
                split = item.split;
                hidden = !!item.hidden;
                
                if (typeof split === 'object') {
                    cfg = split;
                    split = true;
                }
                
                if ((item.isHorz || item.isVert) && (split || item.collapseMode == 'mini')) {
                    me.insertSplitter(item, index, hidden || !split, cfg);
                }
            }

            if (!item.hasOwnProperty('collapseMode')) {
                item.collapseMode = me.panelCollapseMode;
            }

            if (!item.hasOwnProperty('animCollapse')) {
                if (item.collapseMode !== 'placeholder') {
                    
                    
                    item.animCollapse = false;
                } else {
                    item.animCollapse = me.panelCollapseAnimate;
                }
            }
        } else if (placeholderFor) {
            Ext.apply(item, me.regionFlags[placeholderFor.region]);
            item.region = placeholderFor.region;
            item.weight = placeholderFor.weight;
        }
    },

    onDestroy: function() {
        this.centerRegion = null;
        this.callParent();
    },

    onRemove: function (item) {
        var me = this,
            region = item.region,
            splitter = item.splitter;

        if (region) {
            if (item.isCenter) {
                me.centerRegion = null;
            }

            delete item.isCenter;
            delete item.isHorz;
            delete item.isVert;

            if (splitter) {
                me.owner.doRemove(splitter, true); 
                delete item.splitter;
            }
        }

        me.callParent(arguments);
    },

    
    

    regionMeta: {
        center: { splitterDelta: 0 },

        north: { splitterDelta:  1 },
        south: { splitterDelta: -1 },

        west:  { splitterDelta:  1 },
        east:  { splitterDelta: -1 }
    },

    
    regionFlags: {
        center: { isCenter: true, isHorz: false, isVert: false },

        north: { isCenter: false, isHorz: false, isVert: true, collapseDirection: 'top' },
        south: { isCenter: false, isHorz: false, isVert: true, collapseDirection: 'bottom' },

        west: { isCenter: false, isHorz: true, isVert: false, collapseDirection: 'left' },
        east: { isCenter: false, isHorz: true, isVert: false, collapseDirection: 'right' }
    },

    setupSplitterNeighbors: function (items) {
        var edgeRegions = {
                
                
                
                
            },
            length = items.length,
            touchedRegions = this.touchedRegions,
            i, j, center, count, edge, comp, region, splitter, touched;

        for (i = 0; i < length; ++i) {
            comp = items[i].target;
            region = comp.region;

            if (comp.isCenter) {
                center = comp;
            } else if (region) {
                touched = touchedRegions[region];

                for (j = 0, count = touched.length; j < count; ++j) {
                    edge = edgeRegions[touched[j]];
                    if (edge) {
                        edge.neighbors.push(comp);
                    }
                }
                
                if (comp.placeholderFor) {
                    
                    splitter = comp.placeholderFor.splitter;
                } else {
                    splitter = comp.splitter;
                }
                if (splitter) {
                    splitter.neighbors = [];
                }

                edgeRegions[region] = splitter;
            }
        }

        if (center) {
            touched = touchedRegions.center;

            for (j = 0, count = touched.length; j < count; ++j) {
                edge = edgeRegions[touched[j]];
                if (edge) {
                    edge.neighbors.push(center);
                }
            }
        }
    },

    
    touchedRegions: {
        center: [ 'north', 'south', 'east',  'west' ],

        north:  [ 'north', 'east',  'west'  ],
        south:  [ 'south', 'east',  'west'  ],
        east:   [ 'east',  'north', 'south' ],
        west:   [ 'west',  'north', 'south' ]
    },

    sizePolicies: {
        vert: {
            readsWidth: 0,
            readsHeight: 1,
            setsWidth: 1,
            setsHeight: 0
        },
        horz: {
            readsWidth: 1,
            readsHeight: 0,
            setsWidth: 0,
            setsHeight: 1
        },
        flexAll: {
            readsWidth: 0,
            readsHeight: 0,
            setsWidth: 1,
            setsHeight: 1
        }
    },

    getItemSizePolicy: function (item) {
        var me = this,
            policies = this.sizePolicies,
            collapseTarget, size, policy, placeholderFor;

        if (item.isCenter) {
            placeholderFor = item.placeholderFor;

            if (placeholderFor) {
                if (placeholderFor.collapsedVertical()) {
                    return policies.vert;
                }
                return policies.horz;
            }
            if (item.collapsed) {
                if (item.collapsedVertical()) {
                    return policies.vert;
                }
                return policies.horz;
            }
            return policies.flexAll;
        }

        collapseTarget = item.collapseTarget;

        if (collapseTarget) {
            return collapseTarget.isVert ? policies.vert : policies.horz;
        }

        if (item.region) {
            if (item.isVert) {
                size = item.height;
                policy = policies.vert;
            } else {
                size = item.width;
                policy = policies.horz;
            }

            if (item.flex || (typeof size == 'string' && me.percentageRe.test(size))) {
                return policies.flexAll;
            }

            return policy;
        }

        return me.autoSizePolicy;
    }
}, function () {
    var methods = {
        addUnflexed: function (px) {
            this.flexSpace = Math.max(this.flexSpace - px, 0);
        }
    },
    props = this.prototype.axisProps;

    Ext.apply(props.horz, methods);
    Ext.apply(props.vert, methods);
});


Ext.define('Ext.layout.container.Card', {

    

    extend:  Ext.layout.container.Fit ,

    alternateClassName: 'Ext.layout.CardLayout',

    alias: 'layout.card',

    

    type: 'card',

    hideInactive: true,

    
    deferredRender : false,
    
    getRenderTree: function () {
        var me = this,
            activeItem = me.getActiveItem();

        if (activeItem) {

            
            if (activeItem.hasListeners.beforeactivate && activeItem.fireEvent('beforeactivate', activeItem) === false) {
 
                
                
                
                activeItem = me.activeItem = me.owner.activeItem = null;
            }
            
            
            else if (activeItem.hasListeners.activate) {
                activeItem.on({
                    boxready: function() {
                        activeItem.fireEvent('activate', activeItem);
                    },
                    single: true
                });
            }

            if (me.deferredRender) {
                if (activeItem) {
                    return me.getItemsRenderTree([activeItem]);
                }
            } else {
                return me.callParent(arguments);
            }
        }
    },

    renderChildren: function () {
        var me = this,
            active = me.getActiveItem();
            
        if (!me.deferredRender) {
            me.callParent();
        } else if (active) {
            
            me.renderItems([active], me.getRenderTarget());
        }
    },

    isValidParent : function(item, target, position) {
        
        
        var itemEl = item.el ? item.el.dom : Ext.getDom(item);
        return (itemEl && itemEl.parentNode === (target.dom || target)) || false;
    },

    
    getActiveItem: function() {
        var me = this,
            
            result = me.parseActiveItem(me.activeItem || (me.owner && me.owner.activeItem));

        
        if (result && me.owner.items.indexOf(result) != -1) {
            me.activeItem = result;
        } else {
            me.activeItem = null;
        }

        return me.activeItem;
    },

    
    parseActiveItem: function(item) {
        if (item && item.isComponent) {
            return item;
        } else if (typeof item == 'number' || item === undefined) {
            return this.getLayoutItems()[item || 0];
        } else {
            return this.owner.getComponent(item);
        }
    },

    
    
    configureItem: function(item) {
        if (item === this.getActiveItem()) {
            item.hidden = false;
        } else {
            item.hidden = true;
        }
        this.callParent(arguments);
    },

    onRemove: function(component) {
        var me = this;
        
        if (component === me.activeItem) {
            me.activeItem = null;
        }
    },

    
    getAnimation: function(newCard, owner) {
        var newAnim = (newCard || {}).cardSwitchAnimation;
        if (newAnim === false) {
            return false;
        }
        return newAnim || owner.cardSwitchAnimation;
    },

    
    getNext: function() {
        var wrap = arguments[0],
            items = this.getLayoutItems(),
            index = Ext.Array.indexOf(items, this.activeItem);
            
        return items[index + 1] || (wrap ? items[0] : false);
    },

    
    next: function() {
        var anim = arguments[0], 
            wrap = arguments[1];
        return this.setActiveItem(this.getNext(wrap), anim);
    },

    
    getPrev: function() {
        var wrap = arguments[0],
            items = this.getLayoutItems(),
            index = Ext.Array.indexOf(items, this.activeItem);
            
        return items[index - 1] || (wrap ? items[items.length - 1] : false);
    },

    
    prev: function() {
        var anim = arguments[0], 
            wrap = arguments[1];
        return this.setActiveItem(this.getPrev(wrap), anim);
    },

    
    setActiveItem: function(newCard) {
        var me = this,
            owner = me.owner,
            oldCard = me.activeItem,
            rendered = owner.rendered,
            newIndex;

        newCard = me.parseActiveItem(newCard);
        newIndex = owner.items.indexOf(newCard);

        
        
        if (newIndex == -1) {
            newIndex = owner.items.items.length;
            Ext.suspendLayouts();
            newCard = owner.add(newCard);
            Ext.resumeLayouts();
        }

        
        if (newCard && oldCard != newCard) {
            
            if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) {
                return false;
            }
            if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) {
                return false;
            }

            if (rendered) {
                Ext.suspendLayouts();

                
                if (!newCard.rendered) {
                    me.renderItem(newCard, me.getRenderTarget(), owner.items.length);
                }

                if (oldCard) {
                    if (me.hideInactive) {
                        oldCard.hide();
                        oldCard.hiddenByLayout = true;
                    }
                    oldCard.fireEvent('deactivate', oldCard, newCard);
                }
                
                if (newCard.hidden) {
                    newCard.show();
                }

                
                if (!newCard.hidden) {
                    me.activeItem = newCard;
                }
                Ext.resumeLayouts(true);
            } else {
                me.activeItem = newCard;
            }

            newCard.fireEvent('activate', newCard, oldCard);

            return me.activeItem;
        }
        return false;
    }
});


Ext.define('Ext.layout.container.Column', {

    extend:  Ext.layout.container.Auto ,
    alias: ['layout.column'],
    alternateClassName: 'Ext.layout.ColumnLayout',

    type: 'column',

    itemCls: Ext.baseCSSPrefix + 'column',

    targetCls: Ext.baseCSSPrefix + 'column-layout-ct',

    
    columnWidthSizePolicy: {
        readsWidth: 0,
        readsHeight: 1,
        setsWidth: 1,
        setsHeight: 0
    },
    
    createsInnerCt: true,

    manageOverflow: true,
    
    isItemShrinkWrap: function(ownerContext){
        return true;
    },

    getItemSizePolicy: function (item, ownerSizeModel) {
        if (item.columnWidth) {
            if (!ownerSizeModel) {
                ownerSizeModel = this.owner.getSizeModel();
            }

            if (!ownerSizeModel.width.shrinkWrap) {
                return this.columnWidthSizePolicy;
            }
        }
        return this.autoSizePolicy;
    },

    calculateItems: function (ownerContext, containerSize) {
        var me = this,
            targetContext = ownerContext.targetContext,
            items = ownerContext.childItems,
            len = items.length,
            contentWidth = 0,
            gotWidth = containerSize.gotWidth,
            blocked, availableWidth, i, itemContext, itemMarginWidth, itemWidth;

        
        if (gotWidth === false) { 
            
            targetContext.domBlock(me, 'width');
            blocked = true;
        } else if (gotWidth) {
            availableWidth = containerSize.width;
        } else {
            
            
            return true;
        }

        
        
        for (i = 0; i < len; ++i) {
            itemContext = items[i];

            
            
            
            itemMarginWidth = itemContext.getMarginInfo().width;

            if (!itemContext.widthModel.calculated) {
                itemWidth = itemContext.getProp('width');
                if (typeof itemWidth != 'number') {
                    itemContext.block(me, 'width');
                    blocked = true;
                }

                contentWidth += itemWidth + itemMarginWidth;
            }
        }

        if (!blocked) {
            availableWidth = (availableWidth < contentWidth) ? 0 : availableWidth - contentWidth;

            for (i = 0; i < len; ++i) {
                itemContext = items[i];
                if (itemContext.widthModel.calculated) {
                    itemMarginWidth = itemContext.marginInfo.width; 
                    itemWidth = itemContext.target.columnWidth;
                    itemWidth = Math.floor(itemWidth * availableWidth) - itemMarginWidth;
                    itemWidth = itemContext.setWidth(itemWidth); 
                    contentWidth += itemWidth + itemMarginWidth;
                }
            }

            ownerContext.setContentWidth(contentWidth + ownerContext.paddingContext.getPaddingInfo().width);
        }

        
        return !blocked;
    },

    setCtSizeIfNeeded: function(ownerContext, containerSize) {
        var me = this,
            padding = ownerContext.paddingContext.getPaddingInfo();

        me.callParent(arguments);

        
        
        if ((Ext.isIEQuirks || Ext.isIE7m) && me.isShrinkWrapTpl && padding.right) {
            ownerContext.outerCtContext.setProp('width',
                containerSize.width + padding.left);
        }
    }

});


Ext.define('Ext.layout.container.Form', {

    

    alias: 'layout.form',
    extend:  Ext.layout.container.Container ,
    alternateClassName: 'Ext.layout.FormLayout',

    
   
    tableCls: Ext.baseCSSPrefix + 'form-layout-table',

    type: 'form',
    
    createsInnerCt: true,

    manageOverflow: true,

    
    lastOverflowAdjust: {
        width: 0,
        height: 0
    },

    childEls: ['formTable'],
    
    padRow: '<tr><td class="' + Ext.baseCSSPrefix + 'form-item-pad" colspan="3"></td></tr>',

    renderTpl: [
        '<table id="{ownerId}-formTable" class="{tableCls}" style="width:100%" cellpadding="0">',
            '{%this.renderBody(out,values)%}',
        '</table>',
        '{%this.renderPadder(out,values)%}'
    ],

    getRenderData: function(){
        var data = this.callParent();
        data.tableCls = this.tableCls;
        return data;    
    },

    calculate : function (ownerContext) {
        var me = this,
            containerSize = me.getContainerSize(ownerContext, true),
            tableWidth,
            childItems,
            i = 0, length,
            shrinkwrapHeight = ownerContext.sizeModel.height.shrinkWrap;

        if (shrinkwrapHeight) {
            if (ownerContext.hasDomProp('containerChildrenSizeDone')) {
                ownerContext.setProp('contentHeight', me.formTable.dom.offsetHeight + ownerContext.targetContext.getPaddingInfo().height);
            } else {
                me.done = false;
            }
        }

        
        if (containerSize.gotWidth) {
            tableWidth = me.formTable.dom.offsetWidth;
            childItems = ownerContext.childItems;

            for (length = childItems.length; i < length; ++i) {
                childItems[i].setWidth(tableWidth, false);
            }
        } else {
            me.done = false;
        }
    },

    getRenderTarget: function() {
        return this.formTable;
    },

    getRenderTree: function() {
        var me = this,
            result = me.callParent(arguments),
            i, len;

        for (i = 0, len = result.length; i < len; i++) {
            result[i] = me.transformItemRenderTree(result[i]);
        }
        return result;
    },

    transformItemRenderTree: function(item) {

        if (item.tag && item.tag == 'table') {
            item.tag = 'tbody';
            delete item.cellspacing;
            delete item.cellpadding;

            
            
            
            
            if (Ext.isIE6) {
                item.cn = this.padRow;
            }

            return item;
        }

        return {
            tag: 'tbody',
            cn: {
                tag: 'tr',
                cn: {
                    tag: 'td',
                    colspan: 3,
                    style: 'width:100%',
                    cn: item
                }
            }
        };

    },

    isValidParent: function(item, target, position) {
        return true;
    },

    isItemShrinkWrap: function(item) {
        return ((item.shrinkWrap === true) ? 3 : item.shrinkWrap||0) & 2;
    },

    getItemSizePolicy: function(item) {
        return {
            setsWidth: 1,
            setsHeight: 0
        };
    },





    beginLayoutCycle: function (ownerContext, firstCycle) {
        var padEl = this.overflowPadderEl;

        if (padEl) {
            padEl.setStyle('display', 'none');
        }

        
        
        if (!ownerContext.state.overflowAdjust) {
            ownerContext.state.overflowAdjust = this.lastOverflowAdjust;
        }
    },

    
    calculateOverflow: function (ownerContext, containerSize, dimensions) {
        var me = this,
            targetContext = ownerContext.targetContext,
            manageOverflow = me.manageOverflow,
            state = ownerContext.state,
            overflowAdjust = state.overflowAdjust,
            padWidth, padHeight, padElContext, padding, scrollRangeFlags,
            scrollbarSize, contentW, contentH, ownerW, ownerH, scrollbars,
            xauto, yauto;

        if (manageOverflow && !state.secondPass && !me.reserveScrollbar) {
            
            
            xauto = (me.getOverflowXStyle(ownerContext) === 'auto');
            yauto = (me.getOverflowYStyle(ownerContext) === 'auto');

            
            
            
            if (!containerSize.gotWidth) {
                xauto = false;
            }
            if (!containerSize.gotHeight) {
                yauto = false;
            }

            if (xauto || yauto) {
                scrollbarSize = Ext.getScrollbarSize();

                
                
                contentW = ownerContext.peek('contentWidth');
                contentH = ownerContext.peek('contentHeight');
                
                
                
                
                padding = targetContext.getPaddingInfo();
                contentW -= padding.width;
                contentH -= padding.height;
                ownerW = containerSize.width;
                ownerH = containerSize.height;

                scrollbars = me.getScrollbarsNeeded(ownerW, ownerH, contentW, contentH);
                state.overflowState = scrollbars;

                if (typeof dimensions == 'number') {
                    scrollbars &= ~dimensions; 
                }

                overflowAdjust = {
                    width:  (xauto && (scrollbars & 2)) ? scrollbarSize.width : 0,
                    height: (yauto && (scrollbars & 1)) ? scrollbarSize.height : 0
                };

                
                
                if (overflowAdjust.width !== me.lastOverflowAdjust.width || overflowAdjust.height !== me.lastOverflowAdjust.height) {
                    me.done = false;

                    
                    
                    ownerContext.invalidate({
                        state: {
                            overflowAdjust: overflowAdjust,
                            overflowState: state.overflowState,
                            secondPass: true
                        }
                    });
                }
            }
        }

        if (!me.done) {
            return;
        }

        padElContext = ownerContext.padElContext ||
                      (ownerContext.padElContext = ownerContext.getEl('overflowPadderEl', me));

        
        
        if (padElContext) {
            scrollbars = state.overflowState; 
            padWidth = ownerContext.peek('contentWidth');
            
            
            padHeight = 1;

            if (scrollbars) {
                padding = targetContext.getPaddingInfo();
                scrollRangeFlags = me.scrollRangeFlags;

                if ((scrollbars & 2) && (scrollRangeFlags & 1)) { 
                    padHeight += padding.bottom;
                }

                if ((scrollbars & 1) && (scrollRangeFlags & 4)) { 
                    padWidth += padding.right;
                }
                padElContext.setProp('display', '');
                padElContext.setSize(padWidth, padHeight);
            } else {
                padElContext.setProp('display', 'none');
            }
        }
    },

    completeLayout: function (ownerContext) {
        
        this.lastOverflowAdjust = ownerContext.state.overflowAdjust;
    },

    
    doRenderPadder: function (out, renderData) {
        
        

        var me = renderData.$layout,
            owner = me.owner,
            scrollRangeFlags = me.getScrollRangeFlags();

        if (me.manageOverflow) {
            if (scrollRangeFlags & 5) { 
                out.push('<div id="',owner.id,'-overflowPadderEl" ',
                    
                    
                    
                    
                    
                    'style="font-size: 1px; height: 1px; margin-top: -1px; position: relative; z-index: -99999');

                out.push('"></div>');

                me.scrollRangeFlags = scrollRangeFlags; 
            }
        }
    },

    
    getContainerSize : function(ownerContext, inDom, ignoreOverflow) {
        
        
        
        
        

        var targetContext = ownerContext.targetContext,
            frameInfo = targetContext.getFrameInfo(),
            
            padding = targetContext.getPaddingInfo(),
            got = 0,
            needed = 0,
            overflowAdjust = ignoreOverflow ?  null : ownerContext.state.overflowAdjust,
            gotWidth, gotHeight, width, height;

        
        
        

        
        
        

        if (!ownerContext.widthModel.shrinkWrap) {
            ++needed;
            width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width');
            gotWidth = (typeof width == 'number');
            if (gotWidth) {
                ++got;
                width -= frameInfo.width + padding.width;
                if (overflowAdjust) {
                    width -= overflowAdjust.width;
                }
            }
        }

        if (!ownerContext.heightModel.shrinkWrap) {
            ++needed;
            height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height');
            gotHeight = (typeof height == 'number');
            if (gotHeight) {
                ++got;
                height -= frameInfo.height + padding.height;
                if (overflowAdjust) {
                    height -= overflowAdjust.height;
                }
            }
        }

        return {
            width: width,
            height: height,
            needed: needed,
            got: got,
            gotAll: got == needed,
            gotWidth: gotWidth,
            gotHeight: gotHeight
        };
    },

    
    getOverflowXStyle: function(ownerContext) {
        var me = this;

        return me.overflowXStyle ||
            (me.overflowXStyle = me.owner.scrollFlags.overflowX || ownerContext.targetContext.getStyle('overflow-x'));
    },

    
    getOverflowYStyle: function(ownerContext) {
        var me = this;

        return me.overflowYStyle || 
            (me.overflowYStyle = me.owner.scrollFlags.overflowY || ownerContext.targetContext.getStyle('overflow-y'));
    },

    
    getScrollRangeFlags: (function () {
        var flags = -1;

        return function () {
            if (flags < 0) {
                var div = Ext.getBody().createChild({
                        
                        cls: Ext.baseCSSPrefix + 'border-box',
                        style: {
                            width: '100px', height: '100px', padding: '10px',
                            overflow: 'auto'
                        },
                        children: [{
                            style: {
                                border: '1px solid red',
                                width: '150px', height: '150px',
                                margin: '0 5px 5px 0' 
                            }
                        }]
                    }),
                    scrollHeight = div.dom.scrollHeight,
                    scrollWidth = div.dom.scrollWidth,
                    heightFlags = {
                        
                        175: 0,
                        
                        165: 1,
                        
                        170: 2,
                        
                        160: 3
                    },
                    widthFlags = {
                        
                        175: 0,
                        
                        165: 4,
                        
                        170: 8,
                        
                        160: 12
                    };

                flags = (heightFlags[scrollHeight] || 0) | (widthFlags[scrollWidth] || 0);
                
                div.remove();
            }

            return flags;
        };
    }()),

    initLayout: function() {
        var me = this,
            scrollbarWidth = Ext.getScrollbarSize().width;

        me.callParent();

        
        
        
        if (scrollbarWidth && me.manageOverflow && !me.hasOwnProperty('lastOverflowAdjust')) {
            if (me.owner.scrollFlags.y || me.reserveScrollbar) {
                me.lastOverflowAdjust = {
                    width: scrollbarWidth,
                    height: 0
                };
            }
        }
    },

    setupRenderTpl: function (renderTpl) {
        this.callParent(arguments);

        renderTpl.renderPadder = this.doRenderPadder;
    }
});


Ext.define('Ext.menu.Item', {
    extend:  Ext.Component ,
    alias: 'widget.menuitem',
    alternateClassName: 'Ext.menu.TextItem',
    
    mixins: {
        queryable:  Ext.Queryable 
    },

    

    

    
    activeCls: Ext.baseCSSPrefix + 'menu-item-active',

    
    ariaRole: 'menuitem',

    
    canActivate: true,

    
    clickHideDelay: 0,

    
    destroyMenu: true,

    
    disabledCls: Ext.baseCSSPrefix + 'menu-item-disabled',

    

    

    
    hideOnClick: true,

    

    

    

    isMenuItem: true,

    

    

    
    menuAlign: 'tl-tr?',

    
    menuExpandDelay: 200,

    
    menuHideDelay: 200,

    

    

    
    tooltipType: 'qtip',

    arrowCls: Ext.baseCSSPrefix + 'menu-item-arrow',

    childEls: [
        'itemEl', 'iconEl', 'textEl', 'arrowEl'
    ],

    renderTpl: [
        '<tpl if="plain">',
            '{text}',
        '<tpl else>',
            '<a id="{id}-itemEl"',
                ' class="' + Ext.baseCSSPrefix + 'menu-item-link{childElCls}"',
                ' href="{href}"',
                '<tpl if="hrefTarget"> target="{hrefTarget}"</tpl>',
                ' hidefocus="true"',
                
                ' unselectable="on"',
                '<tpl if="tabIndex">',
                    ' tabIndex="{tabIndex}"',
                '</tpl>',
            '>',
                '<div role="img" id="{id}-iconEl" class="' + Ext.baseCSSPrefix + 'menu-item-icon {iconCls}',
                    '{childElCls} {glyphCls}" style="<tpl if="icon">background-image:url({icon});</tpl>',
                    '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">',
                    '<tpl if="glyph">&#{glyph};</tpl>',
                '</div>',
                '<span id="{id}-textEl" class="' + Ext.baseCSSPrefix + 'menu-item-text" unselectable="on">{text}</span>',
                '<img id="{id}-arrowEl" src="{blank}" class="{arrowCls}',
                    '{childElCls}"/>',
            '</a>',
        '</tpl>'
    ],

    maskOnDisable: false,

    

    

    activate: function() {
        var me = this;

        if (!me.activated && me.canActivate && me.rendered && !me.isDisabled() && me.isVisible()) {
            me.el.addCls(me.activeCls);
            me.focus();
            me.activated = true;
            me.fireEvent('activate', me);
        }
    },

    getFocusEl: function() {
        return this.itemEl;
    },

    deactivate: function() {
        var me = this;

        if (me.activated) {
            me.el.removeCls(me.activeCls);
            me.blur();
            me.hideMenu();
            me.activated = false;
            me.fireEvent('deactivate', me);
        }
    },

    deferHideMenu: function() {
        if (this.menu.isVisible()) {
            this.menu.hide();
        }
    },
    
    cancelDeferHide: function(){
        clearTimeout(this.hideMenuTimer);
    },

    deferHideParentMenus: function() {
        var ancestor;
        Ext.menu.Manager.hideAll();

        if (!Ext.Element.getActiveElement()) {
            
            ancestor = this.up(':not([hidden])');
            if (ancestor) {
                ancestor.focus();
            }
        }
    },

    expandMenu: function(delay) {
        var me = this;

        if (me.menu) {
            me.cancelDeferHide();
            if (delay === 0) {
                me.doExpandMenu();
            } else {
                clearTimeout(me.expandMenuTimer);
                me.expandMenuTimer = Ext.defer(me.doExpandMenu, Ext.isNumber(delay) ? delay : me.menuExpandDelay, me);
            }
        }
    },

    doExpandMenu: function() {
        var me = this,
            menu = me.menu;

        if (me.activated && (!menu.rendered || !menu.isVisible())) {
            me.parentMenu.activeChild = menu;
            menu.parentItem = me;
            menu.parentMenu = me.parentMenu;
            menu.showBy(me, me.menuAlign);
        }
    },

    getRefItems: function(deep) {
        var menu = this.menu,
            items;

        if (menu) {
            items = menu.getRefItems(deep);
            items.unshift(menu);
        }
        return items || [];
    },

    hideMenu: function(delay) {
        var me = this;

        if (me.menu) {
            clearTimeout(me.expandMenuTimer);
            me.hideMenuTimer = Ext.defer(me.deferHideMenu, Ext.isNumber(delay) ? delay : me.menuHideDelay, me);
        }
    },

    initComponent: function() {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            cls = [prefix + 'menu-item'],
            menu;

        me.addEvents(
            
            'activate',

            
            'click',

            
            'deactivate',

            
            'textchange',

            
            'iconchange'
        );

        if (me.plain) {
            cls.push(prefix + 'menu-item-plain');
        }

        if (me.cls) {
            cls.push(me.cls);
        }

        me.cls = cls.join(' ');

        if (me.menu) {
            menu = me.menu;
            delete me.menu;
            me.setMenu(menu);
        }

        me.callParent(arguments);
    },

    onClick: function(e) {
        var me = this,
            clickHideDelay = me.clickHideDelay;

        if (!me.href) {
            e.stopEvent();
        }

        if (me.disabled) {
            return;
        }

        if (me.hideOnClick) {
            if (!clickHideDelay) {
                me.deferHideParentMenus();
            } else {
                me.deferHideParentMenusTimer = Ext.defer(me.deferHideParentMenus, clickHideDelay, me);
            }
        }

        Ext.callback(me.handler, me.scope || me, [me, e]);
        me.fireEvent('click', me, e);

        if (!me.hideOnClick) {
            me.focus();
        }
    },

    onRemoved: function() {
        var me = this;

        
        if (me.activated && me.parentMenu.activeItem === me) {
            me.parentMenu.deactivateActiveItem();
        }
        me.callParent(arguments);
        me.parentMenu = me.ownerButton = null;
    },

    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.clearTip();
        }
        me.callParent();
    },

    onDestroy: function() {
        var me = this;

        clearTimeout(me.expandMenuTimer);
        me.cancelDeferHide();
        clearTimeout(me.deferHideParentMenusTimer);

        me.setMenu(null);
        me.callParent(arguments);
    },

    beforeRender: function() {
        var me = this,
            blank = Ext.BLANK_IMAGE_URL,
            glyph = me.glyph,
            glyphFontFamily = Ext._glyphFontFamily,
            glyphParts, iconCls, arrowCls;

        me.callParent();

        if (me.iconAlign === 'right') {
            iconCls = me.checkChangeDisabled ? me.disabledCls : '';
            arrowCls = Ext.baseCSSPrefix + 'menu-item-icon-right ' + me.iconCls;
        } else {
            iconCls = (me.iconCls || '') + (me.checkChangeDisabled ? ' ' + me.disabledCls : '');
            arrowCls = me.menu ? me.arrowCls : '';
        }

        if (typeof glyph === 'string') {
            glyphParts = glyph.split('@');
            glyph = glyphParts[0];
            glyphFontFamily = glyphParts[1];
        }

        Ext.applyIf(me.renderData, {
            href: me.href || '#',
            hrefTarget: me.hrefTarget,
            icon: me.icon,
            iconCls: iconCls,
            glyph: glyph,
            glyphCls: glyph ? Ext.baseCSSPrefix + 'menu-item-glyph' : undefined,
            glyphFontFamily: glyphFontFamily,
            hasIcon: !!(me.icon || me.iconCls || glyph),
            iconAlign: me.iconAlign,
            plain: me.plain,
            text: me.text,
            arrowCls: arrowCls,
            blank: blank,
            tabIndex: me.tabIndex
        });
    },

    onRender: function() {
        var me = this;

        me.callParent(arguments);

        if (me.tooltip) {
            me.setTooltip(me.tooltip, true);
        }
    },
    
    
    setMenu: function(menu, destroyMenu) {
        var me = this,
            oldMenu = me.menu,
            arrowEl = me.arrowEl;
            
        if (oldMenu) {
            delete oldMenu.parentItem;
            delete oldMenu.parentMenu;
            delete oldMenu.ownerItem;
            
            if (destroyMenu === true || (destroyMenu !== false && me.destroyMenu)) {
                Ext.destroy(oldMenu);
            }
        }
        if (menu) {
            me.menu = Ext.menu.Manager.get(menu);
            me.menu.ownerItem = me;
        } else {
            me.menu = null;
        }
        
        if (me.rendered && !me.destroying && arrowEl) {
            arrowEl[me.menu ? 'addCls' : 'removeCls'](me.arrowCls);
        }
    },

    
    setHandler: function(fn, scope) {
        this.handler = fn || null;
        this.scope = scope;
    },

    
    setIcon: function(icon){
        var iconEl = this.iconEl,
            oldIcon = this.icon;
        if (iconEl) {
            iconEl.src = icon || Ext.BLANK_IMAGE_URL;
        }
        this.icon = icon;
        this.fireEvent('iconchange', this, oldIcon, icon);
    },

    
    setIconCls: function(iconCls) {
        var me = this,
            iconEl = me.iconEl,
            oldCls = me.iconCls;

        if (iconEl) {
            if (me.iconCls) {
                iconEl.removeCls(me.iconCls);
            }

            if (iconCls) {
                iconEl.addCls(iconCls);
            }
        }

        me.iconCls = iconCls;
        me.fireEvent('iconchange', me, oldCls, iconCls);
    },

    
    setText: function(text) {
        var me = this,
            el = me.textEl || me.el,
            oldText = me.text;

        me.text = text;

        if (me.rendered) {
            el.update(text || '');
            
            me.ownerCt.updateLayout();
        }
        me.fireEvent('textchange', me, oldText, text);
    },

    getTipAttr: function(){
        return this.tooltipType == 'qtip' ? 'data-qtip' : 'title';
    },

    
    clearTip: function() {
        if (Ext.quickTipsActive && Ext.isObject(this.tooltip)) {
            Ext.tip.QuickTipManager.unregister(this.itemEl);
        }
    },

    
    setTooltip: function(tooltip, initial) {
        var me = this;

        if (me.rendered) {
            if (!initial) {
                me.clearTip();
            }

            if (Ext.quickTipsActive && Ext.isObject(tooltip)) {
                Ext.tip.QuickTipManager.register(Ext.apply({
                    target: me.itemEl.id
                },
                tooltip));
                me.tooltip = tooltip;
            } else {
                me.itemEl.dom.setAttribute(me.getTipAttr(), tooltip);
            }
        } else {
            me.tooltip = tooltip;
        }

        return me;
    }
});


Ext.define('Ext.menu.CheckItem', {
    extend:  Ext.menu.Item ,
    alias: 'widget.menucheckitem',
    
    
    
    

    
    
    

    
    checkedCls: Ext.baseCSSPrefix + 'menu-item-checked',
    
    uncheckedCls: Ext.baseCSSPrefix + 'menu-item-unchecked',
    
    groupCls: Ext.baseCSSPrefix + 'menu-group-icon',

    
    hideOnClick: false,
    
    
    checkChangeDisabled: false,

    childEls: [
        'itemEl', 'iconEl', 'textEl', 'checkEl'
    ],
    
    showCheckbox: true,

    renderTpl: [
        '<tpl if="plain">',
            '{text}',
        '<tpl else>',
            '{%var showCheckbox = values.showCheckbox,',
            '      rightCheckbox = showCheckbox && values.hasIcon && (values.iconAlign !== "left"), textCls = rightCheckbox ? "' + Ext.baseCSSPrefix + 'right-check-item-text" : "";%}',
            '<a id="{id}-itemEl" class="' + Ext.baseCSSPrefix + 'menu-item-link{childElCls}" href="{href}" <tpl if="hrefTarget">target="{hrefTarget}"</tpl> hidefocus="true" unselectable="on"',
                '<tpl if="tabIndex">',
                    ' tabIndex="{tabIndex}"',
                '</tpl>',
            '>',
                '{%if (values.hasIcon && (values.iconAlign !== "left")) {%}',
                    '<div role="img" id="{id}-iconEl" class="' + Ext.baseCSSPrefix + 'menu-item-icon {iconCls}',
                        '{childElCls} {glyphCls}" style="<tpl if="icon">background-image:url({icon});</tpl>',
                        '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">',
                        '<tpl if="glyph">&#{glyph};</tpl>',
                    '</div>',
                '{%} else if (showCheckbox){%}',
                    '<img id="{id}-checkEl" src="{blank}" class="' + Ext.baseCSSPrefix + 'menu-item-icon{childElCls}" />',
                '{%}%}',
                '<span id="{id}-textEl" class="' + Ext.baseCSSPrefix + 'menu-item-text {[textCls]}{childElCls}" <tpl if="arrowCls">style="margin-right: 17px;"</tpl> >{text}</span>',

                
                '{%if (rightCheckbox) {%}',
                    '<img id="{id}-checkEl" src="{blank}" class="' + Ext.baseCSSPrefix + 'menu-item-icon-right{childElCls}" />',
                '{%} else if (values.arrowCls) {%}',
                    '<img id="{id}-arrowEl" src="{blank}" class="{arrowCls}{childElCls}"/>',
                '{%}%}',
            '</a>',
        '</tpl>'
    ],

    initComponent: function() {
        var me = this;
        
        
        me.checked = !!me.checked;
        me.addEvents(
            
            'beforecheckchange',

            
            'checkchange'
        );

        me.callParent(arguments);

        Ext.menu.Manager.registerCheckable(me);

        if (me.group) {
            me.showCheckbox = false
            if (!(me.iconCls || me.icon || me.glyph)) {
                me.iconCls = me.groupCls;
            }
            if (me.initialConfig.hideOnClick !== false) {
                me.hideOnClick = true;
            }
        }
    },
    
    beforeRender: function() {
        this.callParent();
        this.renderData.showCheckbox = this.showCheckbox;
    },
    
    afterRender: function() {
        var me = this;
        me.callParent();
        me.checked = !me.checked;
        me.setChecked(!me.checked, true);
        if (me.checkChangeDisabled) {
            me.disableCheckChange();
        }
    },
    
    
    disableCheckChange: function() {
        var me = this,
            checkEl = me.checkEl;

        if (checkEl) {
            checkEl.addCls(me.disabledCls);
        }
        
        
        if (!(Ext.isIE10p || (Ext.isIE9 && Ext.isStrict)) && me.rendered) {
            me.el.repaint();
        }
        me.checkChangeDisabled = true;
    },

    
    enableCheckChange: function() {
        var me = this,
            checkEl = me.checkEl;
            
        if (checkEl) {
            checkEl.removeCls(me.disabledCls);
        }
        me.checkChangeDisabled = false;
    },

    onClick: function(e) {
        var me = this;
        if(!me.disabled && !me.checkChangeDisabled && !(me.checked && me.group)) {
            me.setChecked(!me.checked);
        }
        this.callParent([e]);
    },

    onDestroy: function() {
        Ext.menu.Manager.unregisterCheckable(this);
        this.callParent(arguments);
    },

    
    setChecked: function(checked, suppressEvents) {
        var me = this;
        if (me.checked !== checked && (suppressEvents || me.fireEvent('beforecheckchange', me, checked) !== false)) {
            if (me.el) {
                me.el[checked  ? 'addCls' : 'removeCls'](me.checkedCls)[!checked ? 'addCls' : 'removeCls'](me.uncheckedCls);
            }
            me.checked = checked;
            Ext.menu.Manager.onCheckChange(me, checked);
            if (!suppressEvents) {
                Ext.callback(me.checkHandler, me.scope, [me, checked]);
                me.fireEvent('checkchange', me, checked);
            }
        }
    }
});


Ext.define('Ext.menu.KeyNav', {
    extend:  Ext.util.KeyNav ,

                                   
    
    constructor: function(config) {
        var me = this;

        me.menu = config.target;
        me.callParent([Ext.apply({
            down: me.down,
            enter: me.enter,
            esc: me.escape,
            left: me.left,
            right: me.right,
            space: me.enter,
            tab: me.tab,
            up: me.up
        }, config)]);
    },

    down: function(e) {
        var me = this,
            fi = me.menu.focusedItem;

        if (fi && e.getKey() == Ext.EventObject.DOWN && me.isWhitelisted(fi)) {
            return true;
        }
        me.focusNextItem(1);
    },

    enter: function(e) {
        var menu = this.menu,
            focused = menu.focusedItem;
 
        if (menu.activeItem) {
            menu.onClick(e);
        } else if (focused && focused.isFormField) {
            
            return true;
        }
    },

    escape: function(e) {
        Ext.menu.Manager.hideAll();
    },

    focusNextItem: function(step) {
        var menu = this.menu,
            items = menu.items,
            focusedItem = menu.focusedItem,
            startIdx = focusedItem ? items.indexOf(focusedItem) : -1,
            idx = startIdx + step,
            len = items.length,
            count = 0,
            item;

        
        while (count < len && idx !== startIdx) {
            if (idx < 0) {
                idx = len - 1;
            } else if (idx >= len) {
                idx = 0;
            }

            item = items.getAt(idx);
            if (menu.canActivateItem(item)) {
                menu.setActiveItem(item);
                break;
            }
            idx += step;
            ++count;
        }
    },

    isWhitelisted: function(item) {
        return Ext.FocusManager.isWhitelisted(item);
    },

    left: function(e) {
        var menu = this.menu,
            fi = menu.focusedItem;

        if (fi && this.isWhitelisted(fi)) {
            return true;
        }

        menu.hide();
        if (menu.parentMenu) {
            menu.parentMenu.focus();
        }
    },

    right: function(e) {
        var menu = this.menu,
            fi = menu.focusedItem,
            ai = menu.activeItem,
            am;

        if (fi && this.isWhitelisted(fi)) {
            return true;
        }

        if (ai) {
            am = menu.activeItem.menu;
            if (am) {
                ai.expandMenu(0);
                am.setActiveItem(am.child(':focusable'));
            }
        }
    },

    tab: function(e) {
        var me = this;

        if (e.shiftKey) {
            me.up(e);
        } else {
            me.down(e);
        }
    },

    up: function(e) {
        var me = this,
            fi = me.menu.focusedItem;

        if (fi && e.getKey() == Ext.EventObject.UP && me.isWhitelisted(fi)) {
            return true;
        }
        me.focusNextItem(-1);
    }
});


Ext.define('Ext.menu.Separator', {
    extend:  Ext.menu.Item ,
    alias: 'widget.menuseparator',

    

    
    canActivate: false,

    

    

    

    focusable: false,

    

    

    
    hideOnClick: false,

    

    

    

    

    

    

    
    plain: true,

    
    separatorCls: Ext.baseCSSPrefix + 'menu-item-separator',

    
    text: '&#160;',

    beforeRender: function(ct, pos) {
        var me = this;

        me.callParent();

        me.addCls(me.separatorCls);
    }
});


Ext.define('Ext.menu.Menu', {
    extend:  Ext.panel.Panel ,
    alias: 'widget.menu',
               
                                   
                                    
                             
                        
                          
                           
                            
      

    
    
    
    enableKeyNav: true,

    
    allowOtherMenus: false,

    
    ariaRole: 'menu',

    

    
    floating: true,

    
    constrain: true,

    
    hidden: true,

    hideMode: 'visibility',

    
    ignoreParentClicks: false,

    
    isMenu: true,

    

    
    showSeparator : true,

    
    minWidth: undefined,

    defaultMinWidth: 120,

    

    initComponent: function() {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            cls = [prefix + 'menu'],
            bodyCls = me.bodyCls ? [me.bodyCls] : [],
            isFloating = me.floating !== false;

        me.addEvents(
            
            'click',

            
            'mouseenter',

            
            'mouseleave',

            
            'mouseover'
        );

        Ext.menu.Manager.register(me);

        
        if (me.plain) {
            cls.push(prefix + 'menu-plain');
        }
        me.cls = cls.join(' ');

        
        bodyCls.push(prefix + 'menu-body', Ext.dom.Element.unselectableCls);
        me.bodyCls = bodyCls.join(' ');

        
        
        
        
        if (!me.layout) {
            me.layout = {
                type: 'vbox',
                align: 'stretchmax',
                overflowHandler: 'Scroller'
            };
        }

        if (isFloating)  {
            
            if (me.minWidth === undefined) {
                me.minWidth = me.defaultMinWidth;
            }
        } else {
            
            me.hidden = !!me.initialConfig.hidden;
            me.constrain = false;
        }

        me.callParent(arguments);
    },

    
    
    registerWithOwnerCt: function() {
        if (this.floating) {
            this.ownerCt = null;
            Ext.WindowManager.register(this);
        }
    },

    
    
    initHierarchyEvents: Ext.emptyFn,

    
    isVisible: function() {
        return this.callParent();
    },

    
    
    getHierarchyState: function() {
        var result = this.callParent();
        result.hidden = this.hidden;
        return result;
    },

    beforeRender: function() {
        this.callParent(arguments);

        
        
        if (!this.getSizeModel().width.shrinkWrap) {
            this.layout.align = 'stretch';
        }
    },

    onBoxReady: function() {
        var me = this;

        me.callParent(arguments);

        
        if (me.showSeparator) {
            me.iconSepEl = me.layout.getElementTarget().insertFirst({
                cls: Ext.baseCSSPrefix + 'menu-icon-separator',
                html: '&#160;'
            });
        }

        me.mon(me.el, {
            click: me.onClick,
            mouseover: me.onMouseOver,
            scope: me
        });
        me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);

        
        if (me.enableKeyNav) {
            me.keyNav = new Ext.menu.KeyNav({
                target: me,
                keyMap: me.getKeyMap()
            });
        }
    },

    getRefOwner: function() {
        
        
        
        return this.parentMenu || this.ownerButton || this.callParent(arguments);
    },

    
    canActivateItem: function(item) {
        return item && !item.isDisabled() && item.isVisible() && (item.canActivate || item.getXTypes().indexOf('menuitem') < 0);
    },

    
    deactivateActiveItem: function(andBlurFocusedItem) {
        var me = this,
            activeItem = me.activeItem,
            focusedItem = me.focusedItem;

        if (activeItem) {
            activeItem.deactivate();
            if (!activeItem.activated) {
                delete me.activeItem;
            }
        }

        
        
        if (focusedItem && andBlurFocusedItem) {
            focusedItem.blur();
            delete me.focusedItem;
        }
    },

    
    getFocusEl: function() {
        return this.focusedItem || this.el;
    },

    
    hide: function() {
        this.deactivateActiveItem(true);
        this.callParent(arguments);
    },

    
    getItemFromEvent: function(e) {
        return this.getChildByElement(e.getTarget());
    },

    lookupComponent: function(cmp) {
        var me = this;

        if (typeof cmp == 'string') {
            cmp = me.lookupItemFromString(cmp);
        } else if (Ext.isObject(cmp)) {
            cmp = me.lookupItemFromObject(cmp);
        }

        
        
        cmp.minWidth = cmp.minWidth || me.minWidth;

        return cmp;
    },

    
    lookupItemFromObject: function(cmp) {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            cls;

        if (!cmp.isComponent) {
            if (!cmp.xtype) {
                cmp = Ext.create('Ext.menu.' + (Ext.isBoolean(cmp.checked) ? 'Check': '') + 'Item', cmp);
            } else {
                cmp = Ext.ComponentManager.create(cmp, cmp.xtype);
            }
        }

        if (cmp.isMenuItem) {
            cmp.parentMenu = me;
        }

        if (!cmp.isMenuItem && !cmp.dock) {
            cls = [prefix + 'menu-item-cmp'];

            
            
            
            if (!me.plain && (cmp.indent !== false || cmp.iconCls === 'no-icon')) {
                cls.push(prefix + 'menu-item-indent');
            }

            if (cmp.rendered) {
                cmp.el.addCls(cls);
            } else {
                cmp.cls = (cmp.cls || '') + ' ' + cls.join(' ');
            }
        }
        return cmp;
    },

    
    lookupItemFromString: function(cmp) {
        return (cmp == 'separator' || cmp == '-') ?
            new Ext.menu.Separator()
            : new Ext.menu.Item({
                canActivate: false,
                hideOnClick: false,
                plain: true,
                text: cmp
            });
    },

    onClick: function(e) {
        var me = this,
            item;

        if (me.disabled) {
            e.stopEvent();
            return;
        }

        item = (e.type === 'click') ? me.getItemFromEvent(e) : me.activeItem;
        if (item && item.isMenuItem) {
            if (!item.menu || !me.ignoreParentClicks) {
                item.onClick(e);
            } else {
                e.stopEvent();
            }
        }
        
        if (!item || item.disabled) {
            item = undefined;
        }
        me.fireEvent('click', me, item, e);
    },

    onDestroy: function() {
        var me = this;

        Ext.menu.Manager.unregister(me);
        me.parentMenu = me.ownerButton = null;
        if (me.rendered) {
            me.el.un(me.mouseMonitor);
            Ext.destroy(me.keyNav);
            me.keyNav = null;
        }
        me.callParent(arguments);
    },

    onMouseLeave: function(e) {
        var me = this;

        me.deactivateActiveItem();

        if (me.disabled) {
            return;
        }

        me.fireEvent('mouseleave', me, e);
    },

    onMouseOver: function(e) {
        var me = this,
            fromEl = e.getRelatedTarget(),
            mouseEnter = !me.el.contains(fromEl),
            item = me.getItemFromEvent(e),
            parentMenu = me.parentMenu,
            parentItem = me.parentItem;

        if (mouseEnter && parentMenu) {
            parentMenu.setActiveItem(parentItem);
            parentItem.cancelDeferHide();
            parentMenu.mouseMonitor.mouseenter();
        }

        if (me.disabled) {
            return;
        }

        
        if (item && !item.activated) {
            me.setActiveItem(item);
            if (item.activated && item.expandMenu) {
                item.expandMenu();
            }
        }
        if (mouseEnter) {
            me.fireEvent('mouseenter', me, e);
        }
        me.fireEvent('mouseover', me, item, e);
    },

    setActiveItem: function(item) {
        var me = this;

        if (item && (item != me.activeItem)) {
            me.deactivateActiveItem();
            if (me.canActivateItem(item)) {
                if (item.activate) {
                    item.activate();
                    if (item.activated) {
                        me.activeItem = item;
                        me.focusedItem = item;
                        me.focus();
                    }
                } else {
                    item.focus();
                    me.focusedItem = item;
                }
            }
            item.el.scrollIntoView(me.layout.getRenderTarget());
        }
    },

    showBy: function(cmp, pos, off) {
        var me = this;

        me.callParent(arguments);
        if (!me.hidden) {
            
            me.setVerticalPosition();
        }
        return me;
    },

    beforeShow: function() {
        var me = this,
            viewHeight;

        
        if (me.floating) {
            me.savedMaxHeight = me.maxHeight;
            viewHeight = me.container.getViewSize().height;
            me.maxHeight = Math.min(me.maxHeight || viewHeight, viewHeight);
        }

        me.callParent(arguments);
    },

    afterShow: function() {
        var me = this;

        me.callParent(arguments);

        
        if (me.floating) {
            me.maxHeight = me.savedMaxHeight;
        }
    },

    
    
    
    setVerticalPosition: function() {
        var me = this,
            max,
            y = me.getY(),
            returnY = y,
            height = me.getHeight(),
            viewportHeight = Ext.Element.getViewportHeight().height,
            parentEl = me.el.parent(),
            viewHeight = parentEl.getViewSize().height,
            normalY = y - parentEl.getScroll().top; 

        parentEl = null;

        if (me.floating) {
            max = me.maxHeight ? me.maxHeight : viewHeight - normalY;
            if (height > viewHeight) {
                returnY = y - normalY;
            } else if (max < height) {
                returnY = y - (height - max);
            } else if((y + height) > viewportHeight){ 
                returnY = viewportHeight - height;
            }
        }
        me.setY(returnY);
    }
});


 Ext.define('Ext.menu.ColorPicker', {
     extend:  Ext.menu.Menu ,

     alias: 'widget.colormenu',

                
                          
       

    
    hideOnClick : true,

    
    pickerId : null,

    

    

    

    initComponent : function(){
        var me = this,
            cfg = Ext.apply({}, me.initialConfig);

        
        delete cfg.listeners;
        Ext.apply(me, {
            plain: true,
            showSeparator: false,
            items: Ext.applyIf({
                cls: Ext.baseCSSPrefix + 'menu-color-item',
                id: me.pickerId,
                xtype: 'colorpicker'
            }, cfg)
        });

        me.callParent(arguments);

        me.picker = me.down('colorpicker');

        
        me.relayEvents(me.picker, ['select']);

        if (me.hideOnClick) {
            me.on('select', me.hidePickerOnSelect, me);
        }
    },

    
    hidePickerOnSelect: function() {
        Ext.menu.Manager.hideAll();
    }
 });


 Ext.define('Ext.menu.DatePicker', {
     extend:  Ext.menu.Menu ,

     alias: 'widget.datemenu',

                
                         
       

    
    hideOnClick : true,

    
    pickerId : null,

    

    

    initComponent : function(){
        var me = this,
            cfg = Ext.apply({}, me.initialConfig);
            
        
        delete cfg.listeners;
            
        Ext.apply(me, {
            showSeparator: false,
            plain: true,
            border: false,
            bodyPadding: 0, 
            items: Ext.applyIf({
                cls: Ext.baseCSSPrefix + 'menu-date-item',
                id: me.pickerId,
                xtype: 'datepicker'
            }, cfg)
        });

        me.callParent(arguments);

        me.picker = me.down('datepicker');
        
        me.relayEvents(me.picker, ['select']);

        if (me.hideOnClick) {
            me.on('select', me.hidePickerOnSelect, me);
        }
    },

    hidePickerOnSelect: function() {
        Ext.menu.Manager.hideAll();
    }
 });


Ext.define('Ext.panel.Tool', {
    extend:  Ext.Component ,
                                          
    alias: 'widget.tool',

    
    isTool: true,

    baseCls: Ext.baseCSSPrefix + 'tool',
    disabledCls: Ext.baseCSSPrefix + 'tool-disabled',
    
    
    toolPressedCls: Ext.baseCSSPrefix + 'tool-pressed',
    
    toolOverCls: Ext.baseCSSPrefix + 'tool-over',

    ariaRole: 'button',

    childEls: [
        'toolEl'
    ],

    renderTpl: [
        '<img role="presentation" id="{id}-toolEl" src="{blank}" class="{baseCls}-img {baseCls}-{type}' +
            '{childElCls}" role="presentation"/>'
    ],

    
    toolOwner: null,

    

    

    

    

    

     
    tooltipType: 'qtip',

    
    stopEvent: true,

    
    height: 15,
    width: 15,


    initComponent: function() {
        var me = this;
        me.addEvents(
            
            'click'
        );


        me.type = me.type || me.id;

        Ext.applyIf(me.renderData, {
            baseCls: me.baseCls,
            blank: Ext.BLANK_IMAGE_URL,
            type: me.type
        });

        
        me.tooltip = me.tooltip || me.qtip;
        me.callParent();
    },

    
    afterRender: function() {
        var me = this,
            attr;

        me.callParent(arguments);

        me.el.on({
            click: me.onClick,
            mousedown: me.onMouseDown,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            scope: me
        });

        if (me.tooltip) {
            if (Ext.quickTipsActive && Ext.isObject(me.tooltip)) {
                Ext.tip.QuickTipManager.register(Ext.apply({
                    target: me.id
                }, me.tooltip));
            }
            else {
                attr = me.tooltipType == 'qtip' ? 'data-qtip' : 'title';
                me.el.dom.setAttribute(attr, me.tooltip);
            }
        }
    },

    getFocusEl: function() {
        return this.el;
    },

    
    setType: function(type) {
        var me = this,
            oldType = me.type;

        me.type = type;
        if (me.rendered) {
            if (oldType) {
                me.toolEl.removeCls(me.baseCls + '-' + oldType);
            }
            me.toolEl.addCls(me.baseCls + '-' + type);
        } else {
            me.renderData.type = type;
        }
        return me;
    },

    
    onClick: function(e, target) {
        var me = this;

        if (me.disabled) {
            return false;
        }

        
        me.el.removeCls(me.toolPressedCls);
        me.el.removeCls(me.toolOverCls);

        if (me.stopEvent !== false) {
            e.stopEvent();
        }

        if (me.handler) {
            Ext.callback(me.handler, me.scope || me, [e, target, me.ownerCt, me]);
        } else if (me.callback) {
            Ext.callback(me.callback, me.scope || me, [me.toolOwner || me.ownerCt, me, e]);
        }
        me.fireEvent('click', me, e);
        return true;
    },

    
    onDestroy: function(){
        if (Ext.quickTipsActive && Ext.isObject(this.tooltip)) {
            Ext.tip.QuickTipManager.unregister(this.id);
        }
        this.callParent();
    },

    
    onMouseDown: function() {
        if (this.disabled) {
            return false;
        }

        this.el.addCls(this.toolPressedCls);
    },

    
    onMouseOver: function() {
        if (this.disabled) {
            return false;
        }
        this.el.addCls(this.toolOverCls);
    },

    
    onMouseOut: function() {
        this.el.removeCls(this.toolOverCls);
    }
});


Ext.define('Ext.resizer.SplitterTracker', {
    extend:  Ext.dd.DragTracker ,
                                  
    enabled: true,
    
    overlayCls: Ext.baseCSSPrefix + 'resizable-overlay',

    createDragOverlay: function () {
        var overlay;

        overlay = this.overlay =  Ext.getBody().createChild({
            cls: this.overlayCls, 
            html: '&#160;'
        });

        overlay.unselectable();
        overlay.setSize(Ext.Element.getViewWidth(true), Ext.Element.getViewHeight(true));
        overlay.show();
    },

    getPrevCmp: function() {
        var splitter = this.getSplitter();
        return splitter.previousSibling(':not([hidden])');
    },

    getNextCmp: function() {
        var splitter = this.getSplitter();
        return splitter.nextSibling(':not([hidden])');
    },

    
    
    onBeforeStart: function(e) {
        var me = this,
            prevCmp = me.getPrevCmp(),
            nextCmp = me.getNextCmp(),
            collapseEl = me.getSplitter().collapseEl,
            target = e.getTarget(),
            box;
            
        if (!prevCmp || !nextCmp) {
            return false;
        }

        if (collapseEl && target === me.getSplitter().collapseEl.dom) {
            return false;
        }

        
        if (nextCmp.collapsed || prevCmp.collapsed) {
            return false;
        }

        
        me.prevBox  = prevCmp.getEl().getBox();
        me.nextBox  = nextCmp.getEl().getBox();
        me.constrainTo = box = me.calculateConstrainRegion();

        if (!box) {
            return false;
        }

        return box;
    },

    
    onStart: function(e) {
        var splitter = this.getSplitter();
        this.createDragOverlay();
        splitter.addCls(splitter.baseCls + '-active');
    },

    
    calculateConstrainRegion: function() {
        var me         = this,
            splitter   = me.getSplitter(),
            splitWidth = splitter.getWidth(),
            defaultMin = splitter.defaultSplitMin,
            orient     = splitter.orientation,
            prevBox    = me.prevBox,
            prevCmp    = me.getPrevCmp(),
            nextBox    = me.nextBox,
            nextCmp    = me.getNextCmp(),
            
            
            
            prevConstrainRegion, nextConstrainRegion, constrainOptions;

        
        if (orient === 'vertical') {
            constrainOptions = {
                prevCmp: prevCmp,
                nextCmp: nextCmp,
                prevBox: prevBox,
                nextBox: nextBox,
                defaultMin: defaultMin,
                splitWidth: splitWidth
            };
            
            
            prevConstrainRegion = new Ext.util.Region(
                prevBox.y,
                me.getVertPrevConstrainRight(constrainOptions),
                prevBox.bottom,
                me.getVertPrevConstrainLeft(constrainOptions)
            );
            
            nextConstrainRegion = new Ext.util.Region(
                nextBox.y,
                me.getVertNextConstrainRight(constrainOptions),
                nextBox.bottom,
                me.getVertNextConstrainLeft(constrainOptions)
            );
        } else {
            
            prevConstrainRegion = new Ext.util.Region(
                prevBox.y + (prevCmp.minHeight || defaultMin),
                prevBox.right,
                
                
                (prevCmp.maxHeight ? prevBox.y + prevCmp.maxHeight : nextBox.bottom - (nextCmp.minHeight || defaultMin)) + splitWidth,
                prevBox.x
            );
            
            nextConstrainRegion = new Ext.util.Region(
                
                
                (nextCmp.maxHeight ? nextBox.bottom - nextCmp.maxHeight : prevBox.y + (prevCmp.minHeight || defaultMin)) - splitWidth,
                nextBox.right,
                nextBox.bottom - (nextCmp.minHeight || defaultMin),
                nextBox.x
            );
        }

        
        return prevConstrainRegion.intersect(nextConstrainRegion);
    },

    
    performResize: function(e, offset) {
        var me        = this,
            splitter  = me.getSplitter(),
            orient    = splitter.orientation,
            prevCmp   = me.getPrevCmp(),
            nextCmp   = me.getNextCmp(),
            owner     = splitter.ownerCt,
            flexedSiblings = owner.query('>[flex]'),
            len       = flexedSiblings.length,
            vertical  = orient === 'vertical',
            i         = 0,
            dimension = vertical ? 'width' : 'height',
            totalFlex = 0,
            item, size;

        
        for (; i < len; i++) {
            item = flexedSiblings[i];
            size = vertical ? item.getWidth() : item.getHeight();
            totalFlex += size;
            item.flex = size;
        }

        offset = vertical ? offset[0] : offset[1];

        if (prevCmp) {
            size = me.prevBox[dimension] + offset;
            if (prevCmp.flex) {
                prevCmp.flex = size;
            } else {
                prevCmp[dimension] = size;
            }
        }
        if (nextCmp) {
            size = me.nextBox[dimension] - offset;
            if (nextCmp.flex) {
                nextCmp.flex = size;
            } else {
                nextCmp[dimension] = size;
            }
        }

        owner.updateLayout();
    },

    
    
    
    endDrag: function () {
        var me = this;

        if (me.overlay) {
             me.overlay.remove();
             delete me.overlay;
        }

        me.callParent(arguments); 
    },

    
    onEnd: function(e) {
        var me = this,
            splitter = me.getSplitter();
            
        splitter.removeCls(splitter.baseCls + '-active');
        me.performResize(e, me.getResizeOffset());
    },

    
    
    onDrag: function(e) {
        var me        = this,
            offset    = me.getOffset('dragTarget'),
            splitter  = me.getSplitter(),
            splitEl   = splitter.getEl(),
            orient    = splitter.orientation;

        if (orient === "vertical") {
            splitEl.setX(me.startRegion.left + offset[0]);
        } else {
            splitEl.setY(me.startRegion.top + offset[1]);
        }
    },

    getSplitter: function() {
        return this.splitter;
    },

    getVertPrevConstrainRight: function(o) {
        
        
        return (o.prevCmp.maxWidth ? o.prevBox.x + o.prevCmp.maxWidth :
            o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin)) + o.splitWidth;
    },

    getVertPrevConstrainLeft: function(o) {
        return o.prevBox.x + (o.prevCmp.minWidth || o.defaultMin);
    },


    getVertNextConstrainRight: function(o) {
        return o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin);
    },

    getVertNextConstrainLeft: function(o) {
        
        
        return (o.nextCmp.maxWidth ? o.nextBox.right - o.nextCmp.maxWidth :
            o.prevBox.x + (o.prevBox.minWidth || o.defaultMin)) - o.splitWidth;
    },

    getResizeOffset: function() {
        return this.getOffset('dragTarget');
    }
});


Ext.define('Ext.resizer.BorderSplitterTracker', {
    extend:  Ext.resizer.SplitterTracker ,
                                  

    getPrevCmp: null,
    getNextCmp: null,

    
    calculateConstrainRegion: function() {
        var me = this,
            splitter = me.splitter,
            collapseTarget = splitter.collapseTarget,
            defaultSplitMin = splitter.defaultSplitMin,
            sizePropCap = splitter.vertical ? 'Width' : 'Height',
            minSizeProp = 'min' + sizePropCap,
            maxSizeProp = 'max' + sizePropCap,
            getSizeMethod = 'get' + sizePropCap,
            neighbors = splitter.neighbors,
            length = neighbors.length,
            box = collapseTarget.el.getBox(),
            left = box.x,
            top = box.y,
            right = box.right,
            bottom = box.bottom,
            size = splitter.vertical ? (right - left) : (bottom - top),
            
            i, neighbor, minRange, maxRange, maxGrowth, maxShrink, targetSize;

        
        minRange = (collapseTarget[minSizeProp] || Math.min(size,defaultSplitMin)) - size;

        
        maxRange = collapseTarget[maxSizeProp];
        if (!maxRange) {
            maxRange = 1e9;
        } else {
            maxRange -= size;
        }
        targetSize = size;

        for (i = 0; i < length; ++i) {
            neighbor = neighbors[i];
            size = neighbor[getSizeMethod]();
            

            maxGrowth = size - neighbor[maxSizeProp]; 
            maxShrink = size - (neighbor[minSizeProp] || Math.min(size,defaultSplitMin));

            if (!isNaN(maxGrowth)) {
                
                
                if (minRange < maxGrowth) {
                    minRange = maxGrowth;
                }
            }

            
            
            if (maxRange > maxShrink) {
                maxRange = maxShrink;
            }
        }

        if (maxRange - minRange < 2) {
            return null;
        }

        box = new Ext.util.Region(top, right, bottom, left);

        me.constraintAdjusters[me.getCollapseDirection()](box, minRange, maxRange, splitter);

        me.dragInfo = {
            minRange: minRange,
            maxRange: maxRange,
            
            targetSize: targetSize
        };

        return box;
    },

    constraintAdjusters: {
        
        left: function (box, minRange, maxRange, splitter) {
            box[0] = box.x = box.left = box.right + minRange;
            box.right += maxRange + splitter.getWidth();
        },

        
        top: function (box, minRange, maxRange, splitter) {
            box[1] = box.y = box.top = box.bottom + minRange;
            box.bottom += maxRange + splitter.getHeight();
        },

        
        bottom: function (box, minRange, maxRange, splitter) {
            box.bottom = box.top - minRange;
            box.top -= maxRange + splitter.getHeight();
        },

        
        right: function (box, minRange, maxRange, splitter) {
            box.right = box.left - minRange;
            box[0] = box.x = box.left = box.x - maxRange + splitter.getWidth();
        }
    },

    onBeforeStart: function(e) {
        var me = this,
            splitter = me.splitter,
            collapseTarget = splitter.collapseTarget,
            neighbors = splitter.neighbors,
            collapseEl = me.getSplitter().collapseEl,
            target = e.getTarget(),
            length = neighbors.length,
            i, neighbor;

        if (collapseEl && target === splitter.collapseEl.dom) {
            return false;
        }

        if (collapseTarget.collapsed) {
            return false;
        }

        
        for (i = 0; i < length; ++i) {
            neighbor = neighbors[i];

            if (neighbor.collapsed && neighbor.isHorz === collapseTarget.isHorz) {
                return false;
            }
        }

        if (!(me.constrainTo = me.calculateConstrainRegion())) {
            return false;
        }

        return true;
    },

    performResize: function(e, offset) {
        var me = this,
            splitter = me.splitter,
            collapseDirection = splitter.getCollapseDirection(),
            collapseTarget = splitter.collapseTarget,
            
            adjusters = me.splitAdjusters[splitter.vertical ? 'horz' : 'vert'],
            delta = offset[adjusters.index],
            dragInfo = me.dragInfo,
            
            
            
            
            
            owner;

        if (collapseDirection == 'right' || collapseDirection == 'bottom') {
            
            delta = -delta;
        }

        
        delta = Math.min(Math.max(dragInfo.minRange, delta), dragInfo.maxRange);

        if (delta) {
            (owner = splitter.ownerCt).suspendLayouts();

            adjusters.adjustTarget(collapseTarget, dragInfo.targetSize, delta);

            
            
            
            
            
            
            

            owner.resumeLayouts(true);
        }
    },

    splitAdjusters: {
        horz: {
            index: 0,
            
            
            
            adjustTarget: function (target, size, delta) {
                target.flex = null;
                target.setSize(size + delta);
            }
        },
        vert: {
            index: 1,
            
            
            
            adjustTarget: function (target, targetSize, delta) {
                target.flex = null;
                target.setSize(undefined, targetSize + delta);
            }
        }
    },

    getCollapseDirection: function() {
        return this.splitter.getCollapseDirection();
    }
});


Ext.define('Ext.resizer.Handle', {
    extend:  Ext.Component ,
    handleCls: '',
    baseHandleCls: Ext.baseCSSPrefix + 'resizable-handle',
    
    
    region: '',

    beforeRender: function() {
        var me = this;

        me.callParent();

        me.protoEl.unselectable();

        me.addCls(
            me.baseHandleCls,
            me.baseHandleCls + '-' + me.region,
            me.handleCls
        );
    }
});


Ext.define('Ext.resizer.ResizeTracker', {
    extend:  Ext.dd.DragTracker ,
    dynamic: true,
    preserveRatio: false,

    
    constrainTo: null,
    
    proxyCls:  Ext.baseCSSPrefix + 'resizable-proxy',

    constructor: function(config) {
        var me = this,
            widthRatio, heightRatio,
            throttledResizeFn;

        if (!config.el) {
            if (config.target.isComponent) {
                me.el = config.target.getEl();
            } else {
                me.el = config.target;
            }
        }
        this.callParent(arguments);

        
        if (me.preserveRatio && me.minWidth && me.minHeight) {
            widthRatio = me.minWidth / me.el.getWidth();
            heightRatio = me.minHeight / me.el.getHeight();

            
            
            
            if (heightRatio > widthRatio) {
                me.minWidth = me.el.getWidth() * heightRatio;
            } else {
                me.minHeight = me.el.getHeight() * widthRatio;
            }
        }

        
        
        if (me.throttle) {
            throttledResizeFn = Ext.Function.createThrottled(function() {
                    Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments);
                }, me.throttle);

            me.resize = function(box, direction, atEnd) {
                if (atEnd) {
                    Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments);
                } else {
                    throttledResizeFn.apply(null, arguments);
                }
            };
        }
    },

    onBeforeStart: function(e) {
        
        this.startBox = this.target.getBox();
    },

    
    getDynamicTarget: function() {
        var me = this,
            target = me.target;
            
        if (me.dynamic) {
            return target;
        } else if (!me.proxy) {
            me.proxy = me.createProxy(target);
        }
        me.proxy.show();
        return me.proxy;
    },

    
    createProxy: function(target){
        var proxy,
            cls = this.proxyCls;

        if (target.isComponent) {
            proxy = target.getProxy().addCls(cls);
        } else {
            proxy = target.createProxy({
                tag: 'div',
                cls: cls,
                id: target.id + '-rzproxy'
            }, Ext.getBody());
        }
        proxy.removeCls(Ext.baseCSSPrefix + 'proxy-el');
        return proxy;
    },

    onStart: function(e) {
        
        this.activeResizeHandle = Ext.get(this.getDragTarget().id);

        
        if (!this.dynamic) {
            this.resize(this.startBox, {
                horizontal: 'none',
                vertical: 'none'
            });
        }
    },

    onDrag: function(e) {
        
        if (this.dynamic || this.proxy) {
            this.updateDimensions(e);
        }
    },

    updateDimensions: function(e, atEnd) {
        var me = this,
            region = me.activeResizeHandle.region,
            offset = me.getOffset(me.constrainTo ? 'dragTarget' : null),
            box = me.startBox,
            ratio,
            widthAdjust = 0,
            heightAdjust = 0,
            snappedWidth,
            snappedHeight,
            adjustX = 0,
            adjustY = 0,
            dragRatio,
            horizDir = offset[0] < 0 ? 'right' : 'left',
            vertDir = offset[1] < 0 ? 'down' : 'up',
            oppositeCorner,
            axis, 
            newBox,
            newHeight, newWidth;

        region = me.convertRegionName(region);

        switch (region) {
            case 'south':
                heightAdjust = offset[1];
                axis = 2;
                break;
            case 'north':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                axis = 2;
                break;
            case 'east':
                widthAdjust = offset[0];
                axis = 1;
                break;
            case 'west':
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                axis = 1;
                break;
            case 'northeast':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                widthAdjust = offset[0];
                oppositeCorner = [box.x, box.y + box.height];
                axis = 3;
                break;
            case 'southeast':
                heightAdjust = offset[1];
                widthAdjust = offset[0];
                oppositeCorner = [box.x, box.y];
                axis = 3;
                break;
            case 'southwest':
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                heightAdjust = offset[1];
                oppositeCorner = [box.x + box.width, box.y];
                axis = 3;
                break;
            case 'northwest':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                oppositeCorner = [box.x + box.width, box.y + box.height];
                axis = 3;
                break;
        }

        newBox = {
            width: box.width + widthAdjust,
            height: box.height + heightAdjust,
            x: box.x + adjustX,
            y: box.y + adjustY
        };

        
        snappedWidth = Ext.Number.snap(newBox.width, me.widthIncrement);
        snappedHeight = Ext.Number.snap(newBox.height, me.heightIncrement);
        if (snappedWidth != newBox.width || snappedHeight != newBox.height){
            switch (region) {
                case 'northeast':
                    newBox.y -= snappedHeight - newBox.height;
                    break;
                case 'north':
                    newBox.y -= snappedHeight - newBox.height;
                    break;
                case 'southwest':
                    newBox.x -= snappedWidth - newBox.width;
                    break;
                case 'west':
                    newBox.x -= snappedWidth - newBox.width;
                    break;
                case 'northwest':
                    newBox.x -= snappedWidth - newBox.width;
                    newBox.y -= snappedHeight - newBox.height;
            }
            newBox.width = snappedWidth;
            newBox.height = snappedHeight;
        }

        
        if (newBox.width < me.minWidth || newBox.width > me.maxWidth) {
            newBox.width = Ext.Number.constrain(newBox.width, me.minWidth, me.maxWidth);

            
            if (adjustX) {
                newBox.x = box.x + (box.width - newBox.width);
            }
        } else {
            me.lastX = newBox.x;
        }
        if (newBox.height < me.minHeight || newBox.height > me.maxHeight) {
            newBox.height = Ext.Number.constrain(newBox.height, me.minHeight, me.maxHeight);

            
            if (adjustY) {
                newBox.y = box.y + (box.height - newBox.height);
            }
        } else {
            me.lastY = newBox.y;
        }

        
        if (me.preserveRatio || e.shiftKey) {
            ratio = me.startBox.width / me.startBox.height;

            
            newHeight = Math.min(Math.max(me.minHeight, newBox.width / ratio), me.maxHeight);
            newWidth = Math.min(Math.max(me.minWidth, newBox.height * ratio), me.maxWidth);

            
            if (axis == 1) {
                newBox.height = newHeight;
            }

            
            else if (axis == 2) {
                newBox.width = newWidth;
            }

            
            else {
                
                
                dragRatio = Math.abs(oppositeCorner[0] - this.lastXY[0]) / Math.abs(oppositeCorner[1] - this.lastXY[1]);

                
                if (dragRatio > ratio) {
                    newBox.height = newHeight;
                } else {
                    newBox.width = newWidth;
                }

                
                if (region == 'northeast') {
                    newBox.y = box.y - (newBox.height - box.height);
                } else if (region == 'northwest') {
                    newBox.y = box.y - (newBox.height - box.height);
                    newBox.x = box.x - (newBox.width - box.width);
                } else if (region == 'southwest') {
                    newBox.x = box.x - (newBox.width - box.width);
                }
            }
        }

        if (heightAdjust === 0) {
            vertDir = 'none';
        }
        if (widthAdjust === 0) {
            horizDir = 'none';
        }
        me.resize(newBox, {
            horizontal: horizDir,
            vertical: vertDir
        }, atEnd);
    },

    getResizeTarget: function(atEnd) {
        return atEnd ? this.target : this.getDynamicTarget();
    },

    resize: function(box, direction, atEnd) {
        var me = this,
            target = me.getResizeTarget(atEnd);

        target.setBox(box);

        
        if (me.originalTarget && (me.dynamic || atEnd)) {
            me.originalTarget.setBox(box);
        }
    },

    onEnd: function(e) {
        this.updateDimensions(e, true);
        if (this.proxy) {
            this.proxy.hide();
        }
    },

    convertRegionName: function(name) {
        return name;
    }
});


Ext.define('Ext.resizer.Resizer', {
    mixins: {
        observable:  Ext.util.Observable 
    },
                                                         

    alternateClassName: 'Ext.Resizable',

    handleCls: Ext.baseCSSPrefix + 'resizable-handle',
    pinnedCls: Ext.baseCSSPrefix + 'resizable-pinned',
    overCls:   Ext.baseCSSPrefix + 'resizable-over',
    wrapCls:   Ext.baseCSSPrefix + 'resizable-wrap',
    delimiterRe: /(?:\s*[,;]\s*)|\s+/,

    
    dynamic: true,

    
    handles: 's e se',

    
    height : null,

    
    width : null,

    
    heightIncrement : 0,

    
    widthIncrement : 0,

    
    minHeight : 20,

    
    minWidth : 20,

    
    maxHeight : 10000,

    
    maxWidth : 10000,

    
    pinned: false,

    
    preserveRatio: false,

    
    transparent: false,

    

    possiblePositions: {
        n:  'north',
        s:  'south',
        e:  'east',
        w:  'west',
        se: 'southeast',
        sw: 'southwest',
        nw: 'northwest',
        ne: 'northeast'
    },

    

    

    constructor: function(config) {
        var me = this,
            target,
            targetEl,
            tag,
            handles = me.handles,
            handleCls,
            possibles,
            len,
            i = 0,
            pos, 
            handleEls = [],
            eastWestStyle, style,
            box, targetBaseCls,
            unselectableCls = Ext.dom.Element.unselectableCls;

        me.addEvents(
            
            'beforeresize',
            
            'resizedrag',
            
            'resize'
        );

        if (Ext.isString(config) || Ext.isElement(config) || config.dom) {
            target = config;
            config = arguments[1] || {};
            config.target = target;
        }
        
        me.mixins.observable.constructor.call(me, config);

        
        
        target = me.target;
        if (target) {
            if (target.isComponent) {

                
                
                
                target.addClsWithUI('resizable');

                me.el = target.getEl();
                if (target.minWidth) {
                    me.minWidth = target.minWidth;
                }
                if (target.minHeight) {
                    me.minHeight = target.minHeight;
                }
                if (target.maxWidth) {
                    me.maxWidth = target.maxWidth;
                }
                if (target.maxHeight) {
                    me.maxHeight = target.maxHeight;
                }
                if (target.floating) {
                    if (!me.hasOwnProperty('handles')) {
                        me.handles = 'n ne e se s sw w nw';
                    }
                }
            } else {
                me.el = me.target = Ext.get(target);
            }
        }
        
        else {
            me.target = me.el = Ext.get(me.el);
        }

        
        
        
        tag = me.el.dom.tagName.toUpperCase();
        if (tag == 'TEXTAREA' || tag == 'IMG' || tag == 'TABLE') {
            
            me.originalTarget = me.target;
            targetEl = me.el;
            box = targetEl.getBox();
            me.target = me.el = me.el.wrap({
                cls: me.wrapCls,
                id: me.el.id + '-rzwrap',
                style: targetEl.getStyles('margin-top', 'margin-bottom')
            });

            
            me.el.setPositioning(targetEl.getPositioning());
            targetEl.clearPositioning();
            me.el.setBox(box);

            
            targetEl.setStyle('position', 'absolute');
        }

        
        
        me.el.position();
        if (me.pinned) {
            me.el.addCls(me.pinnedCls);
        }

        
        me.resizeTracker = new Ext.resizer.ResizeTracker({
            disabled: me.disabled,
            target: me.target,
            constrainTo: me.constrainTo,
            overCls: me.overCls,
            throttle: me.throttle,
            originalTarget: me.originalTarget,
            delegate: '.' + me.handleCls,
            dynamic: me.dynamic,
            preserveRatio: me.preserveRatio,
            heightIncrement: me.heightIncrement,
            widthIncrement: me.widthIncrement,
            minHeight: me.minHeight,
            maxHeight: me.maxHeight,
            minWidth: me.minWidth,
            maxWidth: me.maxWidth
        });

        
        me.resizeTracker.on({
            mousedown: me.onBeforeResize,
            drag: me.onResize,
            dragend: me.onResizeEnd,
            scope: me
        });

        if (me.handles == 'all') {
            me.handles = 'n s e w ne nw se sw';
        }

        handles = me.handles = me.handles.split(me.delimiterRe);
        possibles = me.possiblePositions;
        len = handles.length;

        handleCls = me.handleCls + ' ' + me.handleCls + '-{0}';
        if (me.target.isComponent) {
            targetBaseCls = me.target.baseCls
            handleCls += ' ' + targetBaseCls + '-handle ' + targetBaseCls + '-handle-{0}';
            if (Ext.supports.CSS3BorderRadius) {
                handleCls += ' ' + targetBaseCls + '-handle-{0}-br';
            }
        }

        
        eastWestStyle = Ext.isIE6 ? ' style="height:' + me.el.getHeight() + 'px"' : '';

        for (; i < len; i++){
            
            if (handles[i] && possibles[handles[i]]) {
                pos = possibles[handles[i]];
                if (pos === 'east' || pos === 'west') {
                    style = eastWestStyle;
                } else {
                    style = '';
                }

                handleEls.push(
                    '<div id="', me.el.id, '-', pos, '-handle"',
                        ' class="', Ext.String.format(handleCls, pos), ' ', unselectableCls, '"',
                        ' unselectable="on"',
                        style,
                    '></div>'
                );
            }
        }
        Ext.DomHelper.append(me.el, handleEls.join(''));

        
        for (i = 0; i < len; i++){
            
            if (handles[i] && possibles[handles[i]]) {
                pos = possibles[handles[i]];
                me[pos] = me.el.getById(me.el.id + '-' + pos + '-handle');
                me[pos].region = pos;

                if (me.transparent) {
                    me[pos].setOpacity(0);
                }
            }
        }

        
        if (Ext.isNumber(me.width)) {
            me.width = Ext.Number.constrain(me.width, me.minWidth, me.maxWidth);
        }
        if (Ext.isNumber(me.height)) {
            me.height = Ext.Number.constrain(me.height, me.minHeight, me.maxHeight);
        }

        
        if (me.width !== null || me.height !== null) {
            if (me.originalTarget) {
                me.originalTarget.setWidth(me.width);
                me.originalTarget.setHeight(me.height);
            }
            me.resizeTo(me.width, me.height);
        }

        me.forceHandlesHeight();
    },

    disable: function() {
        this.resizeTracker.disable();
    },

    enable: function() {
        this.resizeTracker.enable();
    },

    
    onBeforeResize: function(tracker, e) {
        var box = this.el.getBox();
        return this.fireEvent('beforeresize', this, box.width, box.height, e);
    },

    
    onResize: function(tracker, e) {
        var me = this,
            box = me.el.getBox();
            
        me.forceHandlesHeight();
        return me.fireEvent('resizedrag', me, box.width, box.height, e);
    },

    
    onResizeEnd: function(tracker, e) {
        var me = this,
            box = me.el.getBox();
            
        me.forceHandlesHeight();
        return me.fireEvent('resize', me, box.width, box.height, e);
    },

    
    resizeTo : function(width, height) {
        var me = this;
        me.target.setSize(width, height);
        me.fireEvent('resize', me, width, height, null);
    },

    
    getEl : function() {
        return this.el;
    },

    
    getTarget: function() {
        return this.target;
    },

    destroy: function() {
        var me = this,
            i,
            handles = me.handles,
            len = handles.length,
            positions = me.possiblePositions,
            handle;

        me.resizeTracker.destroy();
        for (i = 0; i < len; i++) {
            if (handle = me[positions[handles[i]]]) {
                handle.remove();
            }
        }
    },

    
    forceHandlesHeight : function() {
        var me = this,
            handle;
        if (Ext.isIE6) {
            handle = me.east;
            if (handle) {
                handle.setHeight(me.el.getHeight());
            }
            handle = me.west;
            if (handle) {
                handle.setHeight(me.el.getHeight());
            }
            me.el.repaint();
        }
    }
});


Ext.define('Ext.selection.CellModel', {
    extend:  Ext.selection.Model ,
    alias: 'selection.cellmodel',
               
                               
                         
      

    


    isCellModel: true,

    
    enableKeyNav: true,

    
    preventWrap: false,

    
    noSelection: {
        row: -1,
        column: -1
    },

    constructor: function() {
        this.addEvents(
            
            'deselect',

            
            'select'
        );
        this.callParent(arguments);
    },

    bindComponent: function(view) {
        var me = this,
            grid = view.ownerCt;
        me.primaryView = view;
        me.views = me.views || [];
        me.views.push(view);
        me.bindStore(view.getStore(), true);

        view.on({
            cellmousedown: me.onMouseDown,
            refresh: me.onViewRefresh,
            scope: me
        });
        if (grid.optimizedColumnMove !== false) {
            grid.on('columnmove', me.onColumnMove, me);
        }

        if (me.enableKeyNav) {
            me.initKeyNav(view);
        }
    },

    initKeyNav: function(view) {
        var me = this;

        if (!view.rendered) {
            view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});
            return;
        }

        view.el.set({
            tabIndex: -1
        });

        
        
        me.keyNav = new Ext.util.KeyNav({
            target: view.el,
            ignoreInputFields: true,
            up: me.onKeyUp,
            down: me.onKeyDown,
            right: me.onKeyRight,
            left: me.onKeyLeft,
            tab: me.onKeyTab,
            scope: me
        });
    },

    getHeaderCt: function() {
        var selection = this.getCurrentPosition(),
            view = selection ? selection.view : this.primaryView;

        return view.headerCt;
    },

    onKeyUp: function(e) {
        this.doMove('up', e);
    },

    onKeyDown: function(e) {
        this.doMove('down', e);
    },

    onKeyLeft: function(e) {
        this.doMove('left', e);
    },

    onKeyRight: function(e) {
        this.doMove('right', e);
    },
    
    doMove: function(direction, e){
        this.keyNavigation = true;
        this.move(direction, e);
        this.keyNavigation = false;
    },
    
    onVetoUIEvent: Ext.emptyFn,
    
    select: function(pos, keepExisting, suppressEvent) {
        var me = this,
            row,
            oldPos = me.getCurrentPosition(),
            store = me.view.store;
        
        if (pos || pos === 0) {
            if (pos.isModel) {
                row = store.indexOf(pos);
                if (row !== -1) {
                    pos = {
                        row: row,
                        column: oldPos ? oldPos.column : 0
                    };
                } else {
                    pos = null;
                } 
            } else if (typeof pos === 'number') {
                pos = {
                    row: pos,
                    column: 0
                }
            }
        } 
        
        if (pos) {
            me.selectByPosition(pos, suppressEvent);   
        } else {
            me.deselect();
        }
    },
    
    deselect: function(record, suppressEvent){
        this.selectByPosition(null, suppressEvent);    
    },

    move: function(dir, e) {
        var me = this,
            pos = me.getCurrentPosition(),
            newPos;

        if (pos) {
            
            newPos = pos.view.walkCells(pos, dir, e, me.preventWrap);
            
            if (newPos) {
                newPos.view = pos.view;
                return me.setCurrentPosition(newPos);
            }
        }
    },

    
    getCurrentPosition: function() {
        
        
        return this.selecting ? this.nextSelection : this.selection;
    },

    
    setCurrentPosition: function(pos, suppressEvent) {
        var me = this,
            last = me.selection;

        
        me.lastSelection = last;
        if (last) {
            
            if (pos && (pos.record === last.record && pos.columnHeader === last.columnHeader && pos.view === last.view)) {
                pos = null;
            } else {
                me.onCellDeselect(me.selection, suppressEvent);
            }
        }

        if (pos) {
            me.nextSelection = new Ext.grid.CellContext(me.primaryView).setPosition(pos);
            
            
            me.selecting = true;
            me.onCellSelect(me.nextSelection, suppressEvent);
            me.selecting = false;
            
            return (me.selection = me.nextSelection);
        }
    },

    isCellSelected: function(view, row, column) {
        var me = this,
            testPos,
            pos = me.getCurrentPosition();

        if (pos && pos.view === view) {
            testPos = new Ext.grid.CellContext(view).setPosition({
                row: row,
                column: column
            });
            return (testPos.record === pos.record) && (testPos.columnHeader === pos.columnHeader);
        }
    },

    
    onStoreRemove: function(store, records, indexes) {
        var me = this,
            pos = me.getCurrentPosition(),
            i, length = records.length,
            index, shuffleCount = 0;

        me.callParent(arguments);
        if (pos) {
            
            if (indexes[0] > pos.row) {
                return;
            }

            for (i = 0; i < length; i++) {
                index = indexes[i];
                
                
                if (index < pos.row) {
                    shuffleCount++;
                }
                
                else {
                    break;
                }
            }

            
            if (shuffleCount) {
                pos.setRow(pos.row - shuffleCount);
            }
        }
    },

    
    onMouseDown: function(view, cell, cellIndex, record, row, recordIndex, e) {

        
        if (recordIndex !== -1) {
            this.setCurrentPosition({
                view: view,
                row: row,
                column: cellIndex
            });
        }
    },

    
    
    onCellSelect: function(position, supressEvent) {
        if (position && position.row !== undefined && position.row > -1) {
            this.doSelect(position.record, false, supressEvent);
        }
    },

    
    
    onCellDeselect: function(position, supressEvent) {
        if (position && position.row !== undefined) {
            this.doDeselect(position.record, supressEvent);
        }
    },

    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me = this,
            pos,
            eventName,
            view;

        if (isSelected) {
            pos = me.nextSelection;
            eventName = 'select';
        } else {
            pos = me.lastSelection || me.noSelection;
            eventName = 'deselect';
        }

        
        
        
        view = pos.view || me.primaryView;

        if ((suppressEvent || me.fireEvent('before' + eventName, me, record, pos.row, pos.column)) !== false &&
                commitFn() !== false) {

            if (isSelected) {
                view.focusRow(record, true);
                view.onCellSelect(pos);
            } else {
                view.onCellDeselect(pos);
                delete me.selection;
            }

            if (!suppressEvent) {
                me.fireEvent(eventName, me, record, pos.row, pos.column);
            }
        }
    },

    
    onKeyTab: function(e, t) {
        var me = this,
            pos = me.getCurrentPosition(),
            editingPlugin;

        if (pos) {
            editingPlugin = pos.view.editingPlugin;
            
            if (editingPlugin && me.wasEditing) {
                me.onEditorTab(editingPlugin, e)
            } else {
                me.move(e.shiftKey ? 'left' : 'right', e);
            }
        }
    },

    onEditorTab: function(editingPlugin, e) {
        var me = this,
            direction = e.shiftKey ? 'left' : 'right',
            position  = me.move(direction, e);

        
        if (position) {
            
            if (editingPlugin.startEdit(position.record, position.columnHeader)) {
                me.wasEditing = false;
            }
            
            
            else {
                me.wasEditing = true;
            }
        }
    },

    refresh: function() {
        var pos = this.getCurrentPosition(),
            selRowIdx;

        
        if (pos && (selRowIdx = this.store.indexOf(this.selected.last())) !== -1) {
            pos.row = selRowIdx;
        }
    },

    
    onColumnMove: function(headerCt, header, fromIdx, toIdx) {
        var grid = headerCt.up('tablepanel');
        if (grid) {
            this.onViewRefresh(grid.view);
        }
    },
    
    onUpdate: function(record) {
        var me = this,
            pos;
            
        if (me.isSelected(record)) {
            pos = me.selecting ? me.nextSelection : me.selection; 
            me.view.onCellSelect(pos);
        }
    },

    onViewRefresh: function(view) {
        var me = this,
            pos = me.getCurrentPosition(),
            headerCt = view.headerCt,
            record, columnHeader;

        
        
        if (pos && pos.view === view) {
            record = pos.record;
            columnHeader = pos.columnHeader;

            
            if (!columnHeader.isDescendantOf(headerCt)) {
                
                
                
                columnHeader = headerCt.queryById(columnHeader.id) || 
                               headerCt.down('[text="' + columnHeader.text + '"]') ||
                               headerCt.down('[dataIndex="' + columnHeader.dataIndex + '"]');
            }

            
            
            
            
            if (columnHeader && (view.store.indexOfId(record.getId()) !== -1)) {
                me.setCurrentPosition({
                    row: record,
                    column: columnHeader,
                    view: view
                });
            }
        }
    },

    selectByPosition: function(position, suppressEvent) {
        this.setCurrentPosition(position, suppressEvent);
    }
});


Ext.define('Ext.selection.RowModel', {
    extend:  Ext.selection.Model ,
    alias: 'selection.rowmodel',
                                  

    
    deltaScroll: 5,

    
    enableKeyNav: true,

    
    ignoreRightMouseSelection: false,

    constructor: function() {
        this.addEvents(
            
            'beforedeselect',

            
            'beforeselect',

            
            'deselect',

            
            'select'
        );
        this.views = [];
        this.callParent(arguments);
    },

    bindComponent: function(view) {
        var me = this;

        view.on({
            itemmousedown: me.onRowMouseDown,
            itemclick: me.onRowClick,
            scope: me
        });

        if (me.enableKeyNav) {
            me.initKeyNav(view);
        }
    },

    initKeyNav: function(view) {
        var me = this;

        if (!view.rendered) {
            view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});
            return;
        }

        
        
        view.el.set({
            tabIndex: -1
        });

        
        me.keyNav = new Ext.util.KeyNav({
            target: view,
            ignoreInputFields: true,
            eventName: 'itemkeydown',
            processEvent: function(view, record, node, index, event) {
                event.record = record;
                event.recordIndex = index;
                return event;
            },
            up: me.onKeyUp,
            down: me.onKeyDown,
            right: me.onKeyRight,
            left: me.onKeyLeft,
            pageDown: me.onKeyPageDown,
            pageUp: me.onKeyPageUp,
            home: me.onKeyHome,
            end: me.onKeyEnd,
            space: me.onKeySpace,
            enter: me.onKeyEnter,
            scope: me
        });
    },

    onUpdate: function(record) {
        var me = this,
            view = me.view,
            index;
        
        if (view && me.isSelected(record)) {
            index = view.indexOf(record);
            view.onRowSelect(index);
            if (record === me.lastFocused) {
                view.onRowFocus(index, true);
            }
        }
    },

    
    
    
    getRowsVisible: function() {
        var rowsVisible = false,
            view = this.views[0],
            firstRow = view.all.first(),
            rowHeight, gridViewHeight;

        if (firstRow) {
            rowHeight = firstRow.getHeight();
            gridViewHeight = view.el.getHeight();
            rowsVisible = Math.floor(gridViewHeight / rowHeight);
        }

        return rowsVisible;
    },

    
    onKeyEnd: function(e) {
        var me = this,
            view = me.views[0];

        if (view.bufferedRenderer) {
            
            
            
            view.bufferedRenderer.scrollTo(me.store.getCount() - 1, false, function(newIdx, newRecord) {
                me.afterKeyNavigate(e, newRecord)
            });
        } else {
            me.afterKeyNavigate(e, view.getRecord(view.all.getCount() - 1))
        }
    },

    
    onKeyHome: function(e) {
        var me = this,
            view = me.views[0];

        if (view.bufferedRenderer) {
            
            
            
            view.bufferedRenderer.scrollTo(0, false, function(newIdx, newRecord) {
                me.afterKeyNavigate(e, newRecord)
            });
        } else {
            me.afterKeyNavigate(e, view.getRecord(0));
        }
    },

    
    onKeyPageUp: function(e) {
        var me = this,
            view = me.views[0],
            rowsVisible = me.getRowsVisible(),
            newIdx,
            newRecord;

        if (rowsVisible) {
            
            
            
            if (view.bufferedRenderer) {
                newIdx = Math.max(e.recordIndex - rowsVisible, 0);
                (me.lastKeyEvent || (me.lastKeyEvent = new Ext.EventObjectImpl())).setEvent(e.browserEvent);
                view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me);
            } else {
                newRecord = view.walkRecs(e.record, -rowsVisible);
                me.afterKeyNavigate(e, newRecord);
            }
        }
    },

    
    onKeyPageDown: function(e) {
        var me = this,
            view = me.views[0],
            rowsVisible = me.getRowsVisible(),
            newIdx,
            newRecord;

        if (rowsVisible) {
            
            
            
            if (view.bufferedRenderer) {
                newIdx = Math.min(e.recordIndex + rowsVisible, me.store.getCount() - 1);
                (me.lastKeyEvent || (me.lastKeyEvent = new Ext.EventObjectImpl())).setEvent(e.browserEvent);
                view.bufferedRenderer.scrollTo(newIdx, false, me.afterBufferedScrollTo, me);
            } else {
                newRecord = view.walkRecs(e.record, rowsVisible);
                me.afterKeyNavigate(e, newRecord);
            }
        }
    },

    
    onKeySpace: function(e) {
        var record = this.lastFocused;

        if (record) {
            this.afterKeyNavigate(e, record);
        }
    },

    onKeyEnter: Ext.emptyFn,

    
    
    
    onKeyUp: function(e) {
        var newRecord = this.views[0].walkRecs(e.record, -1);

        if (newRecord) {
            this.afterKeyNavigate(e, newRecord);
        }
    },

    
    
    
    onKeyDown: function(e) {
        var newRecord = this.views[0].walkRecs(e.record, 1);

        if (newRecord) {
            this.afterKeyNavigate(e, newRecord);
        }
    },

    afterBufferedScrollTo: function(newIdx, newRecord) {
        this.afterKeyNavigate(this.lastKeyEvent, newRecord)
    },

    scrollByDeltaX: function(delta) {
        var view    = this.views[0],
            section = view.up(),
            hScroll = section.horizontalScroller;

        if (hScroll) {
            hScroll.scrollByDeltaX(delta);
        }
    },

    onKeyLeft: function(e) {
        this.scrollByDeltaX(-this.deltaScroll);
    },

    onKeyRight: function(e) {
        this.scrollByDeltaX(this.deltaScroll);
    },

    
    
    onRowMouseDown: function(view, record, item, index, e) {
        var me = this;
        
        
        if (index !== -1) {
            if (!me.allowRightMouseSelection(e)) {
                return;
            }

            if (!me.isSelected(record)) {
                me.mousedownAction = true;
                me.processSelection(view, record, item, index, e);
            } else {
                me.mousedownAction = false;
            }
        }
    },
    
    
    
    
    onVetoUIEvent: function(type, view, cell, rowIndex, cellIndex, e, record){
        if (type == 'mousedown') {
            this.mousedownAction = !this.isSelected(record);
        }
    },

    onRowClick: function(view, record, item, index, e) {
        if (this.mousedownAction) {
            this.mousedownAction = false;
        } else {
            this.processSelection(view, record, item, index, e);
        }
    },
    
    processSelection: function(view, record, item, index, e) {
        this.selectWithEvent(record, e);
    },

    
    allowRightMouseSelection: function(e) {
        var disallow = this.ignoreRightMouseSelection && e.button !== 0;
        if (disallow) {
            disallow = this.hasSelection();
        }
        return !disallow;
    },

    
    
    onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
        var me      = this,
            views   = me.views,
            viewsLn = views.length,
            rowIdx  = views[0].indexOf(record),
            eventName = isSelected ? 'select' : 'deselect',
            i = 0;

        if ((suppressEvent || me.fireEvent('before' + eventName, me, record, rowIdx)) !== false &&
                commitFn() !== false) {

            for (; i < viewsLn; i++) {
                if (isSelected) {
                    views[i].onRowSelect(rowIdx, suppressEvent);
                } else {
                    views[i].onRowDeselect(rowIdx, suppressEvent);
                }
            }

            if (!suppressEvent) {
                me.fireEvent(eventName, me, record, rowIdx);
            }
        }
    },

    
    
    onLastFocusChanged: function(oldFocused, newFocused, supressFocus) {
        var views   = this.views,
            viewsLn = views.length,
            rowIdx,
            i = 0;

        if (oldFocused) {
            rowIdx = views[0].indexOf(oldFocused);
            if (rowIdx != -1) {
                for (; i < viewsLn; i++) {
                    views[i].onRowFocus(rowIdx, false, true);
                }
            }
        }

        if (newFocused) {
            rowIdx = views[0].indexOf(newFocused);
            if (rowIdx != -1) {
                for (i = 0; i < viewsLn; i++) {
                    views[i].onRowFocus(rowIdx, true, supressFocus);
                }
            }
        }
        this.callParent(arguments);
    },

    onEditorTab: function(editingPlugin, e) {
        var me = this,
            view = me.views[0],
            record = editingPlugin.getActiveRecord(),
            header = editingPlugin.getActiveColumn(),
            position = view.getPosition(record, header),
            direction = e.shiftKey ? 'left' : 'right';

        
        
        
        

        do {
            position  = view.walkCells(position, direction, e, me.preventWrap);
        } while (position && (!position.columnHeader.getEditor(record) || !editingPlugin.startEditByPosition(position)));
    },

    
    getCurrentPosition: function() {
        var firstSelection = this.selected.items[0];
        if (firstSelection) {
            return new Ext.grid.CellContext(this.view).setPosition(this.store.indexOf(firstSelection), 0);
        }
    },

    selectByPosition: function(position) {
        this.select(this.store.getAt(position.row));
    },

    
    selectNext: function(keepExisting, suppressEvent) {
        var me = this,
            store = me.store,
            selection = me.getSelection(),
            record = selection[selection.length - 1],
            index = me.views[0].indexOf(record) + 1,
            success;

        if (index === store.getCount() || index === 0) {
            success = false;
        } else {
            me.doSelect(index, keepExisting, suppressEvent);
            success = true;
        }
        return success;
    },

    
    selectPrevious: function(keepExisting, suppressEvent) {
        var me = this,
            selection = me.getSelection(),
            record = selection[0],
            index = me.views[0].indexOf(record) - 1,
            success;

        if (index < 0) {
            success = false;
        } else {
            me.doSelect(index, keepExisting, suppressEvent);
            success = true;
        }
        return success;
    },

    isRowSelected: function(record, index) {
        return this.isSelected(record);
    }
});


Ext.define('Ext.selection.TreeModel', {
    extend:  Ext.selection.RowModel ,
    alias: 'selection.treemodel',

    

    constructor: function(config) {
        this.callParent(arguments);

        
        
        if (this.pruneRemoved) {
            this.pruneRemoved = false;
            this.pruneRemovedNodes = true;
        }
    },

    
    bindStore: function(store, initial) {
        var me = this;
        me.callParent(arguments);

        
        
        if (me.pruneRemovedNodes) {
            me.view.mon(me.treeStore, {
                remove: me.onNodeRemove,
                scope: me
            });
        }
    },

    onNodeRemove: function(parent, node, isMove) {
        
        if (!isMove) {
            this.deselectDeletedRecords([node]);
        }
    },

    onKeyRight: function(e, t) {
        this.navExpand(e, t);
    },
    
    navExpand: function(e, t) {
        var focused = this.getLastFocused(),
            view    = this.view;

        if (focused) {
            
            
            
            if (focused.isExpanded()) {
                this.onKeyDown(e, t);
            
            } else if (focused.isExpandable()) {
                
                if (!view.isTreeView) {
                    view = view.lockingPartner;
                }

                view.expand(focused);
            }
        }
    },

    onKeyLeft: function(e, t) {
        this.navCollapse(e, t);
    },
    
    navCollapse: function(e, t) {
        var me = this,
            focused = this.getLastFocused(),
            view    = this.view,
            parentNode;

        if (focused) {
            parentNode = focused.parentNode;
            
            if (focused.isExpanded()) {
                
                if (!view.isTreeView) {
                    view = view.lockingPartner;
                }

                view.collapse(focused);
            
            
            } else if (parentNode && !parentNode.isRoot()) {
                
                if (e.shiftKey) {
                    me.selectRange(parentNode, focused, e.ctrlKey, 'up');
                    me.setLastFocused(parentNode);
                
                } else if (e.ctrlKey) {
                    me.setLastFocused(parentNode);
                
                } else {
                    me.select(parentNode);
                }
            }
        }
    },

    onKeySpace: function(e, t) {
        if (e.record.data.checked != null) {
            this.toggleCheck(e);
        } else {
            this.callParent(arguments);
        }
    },

    onKeyEnter: function(e, t) {
        if (e.record.data.checked != null) {
            this.toggleCheck(e);
        } else {
            this.callParent(arguments);
        }
    },

    toggleCheck: function(e) {
        var view = this.view,
            selected = this.getLastSelected();

        e.stopEvent();
        if (selected) {
            
            if (!view.isTreeView) {
                view = view.lockingPartner;
            }

            view.onCheckChange(selected);
        }
    }
});


Ext.define('Ext.slider.Thumb', {
                                                        
    
    topZIndex: 10000,

    

    
    constructor: function(config) {
        var me = this;

        
        Ext.apply(me, config || {}, {
            cls: Ext.baseCSSPrefix + 'slider-thumb',

            
            constrain: false
        });
        me.callParent([config]);
    },

    
    render: function() {
        var me = this;
        me.el = me.slider.innerEl.insertFirst(me.getElConfig());
        me.onRender();
    },
    
    onRender: function() {
        if (this.disabled) {
            this.disable();
        }
        this.initEvents();
    },

    getElConfig: function() {
        var me = this,
            slider = me.slider,
            style = {};

        style[slider.vertical ? 'bottom' : slider.horizontalProp] = slider.calculateThumbPosition(slider.normalizeValue(me.value)) + '%';
        return {
            style: style,
            id  : this.id,
            cls : this.cls
        };
    },

    
    move: function(v, animate) {
        var me = this,
            el = me.el,
            slider = me.slider,
            styleProp = slider.vertical ? 'bottom' : slider.horizontalProp,
            to,
            from;

        v += '%';
        
        if (!animate) {
            el.dom.style[styleProp] = v;
        } else {
            to = {};
            to[styleProp] = v;
            
            if (!Ext.supports.GetPositionPercentage) {
                from = {};
                from[styleProp] = el.dom.style[styleProp];
            }
            
            new Ext.fx.Anim({
                target: el,
                duration: 350,
                from: from,
                to: to
            });
        }
    },

    
    bringToFront: function() {
        this.el.setStyle('zIndex', this.topZIndex);
    },

    
    sendToBack: function() {
        this.el.setStyle('zIndex', '');
    },

    
    enable: function() {
        var me = this;

        me.disabled = false;
        if (me.el) {
            me.el.removeCls(me.slider.disabledCls);
        }
    },

    
    disable: function() {
        var me = this;

        me.disabled = true;
        if (me.el) {
            me.el.addCls(me.slider.disabledCls);
        }
    },

    
    initEvents: function() {
        var me = this,
            el = me.el;

        me.tracker = new Ext.dd.DragTracker({
            onBeforeStart: Ext.Function.bind(me.onBeforeDragStart, me),
            onStart      : Ext.Function.bind(me.onDragStart, me),
            onDrag       : Ext.Function.bind(me.onDrag, me),
            onEnd        : Ext.Function.bind(me.onDragEnd, me),
            tolerance    : 3,
            autoStart    : 300,
            overCls      : Ext.baseCSSPrefix + 'slider-thumb-over'
        });

        me.tracker.initEl(el);
    },

    
    onBeforeDragStart : function(e) {
        if (this.disabled) {
            return false;
        } else {
            this.slider.promoteThumb(this);
            return true;
        }
    },

    
    onDragStart: function(e){
        var me = this,
            slider = me.slider;

        slider.onDragStart(me, e);
        me.el.addCls(Ext.baseCSSPrefix + 'slider-thumb-drag');
        me.dragging = me.slider.dragging = true;
        me.dragStartValue = me.value;

        slider.fireEvent('dragstart', slider, e, me);
    },

    
    onDrag: function(e) {
        var me       = this,
            slider   = me.slider,
            index    = me.index,
            newValue = me.getValueFromTracker(),
            above,
            below;

        
        if (newValue !== undefined) {
            if (me.constrain) {
                above = slider.thumbs[index + 1];
                below = slider.thumbs[index - 1];

                if (below !== undefined && newValue <= below.value) {
                    newValue = below.value;
                }

                if (above !== undefined && newValue >= above.value) {
                    newValue = above.value;
                }
            }
            slider.setValue(index, newValue, false);
            slider.fireEvent('drag', slider, e, me);
        }
    },

    getValueFromTracker: function() {
        var slider = this.slider,
            trackPoint = slider.getTrackpoint(this.tracker.getXY());

        
        if (trackPoint !== undefined) {
            return slider.reversePixelValue(trackPoint);
        }
    },

    
    onDragEnd: function(e) {
        var me     = this,
            slider = me.slider,
            value  = me.value;

        slider.onDragEnd(me, e);
        me.el.removeCls(Ext.baseCSSPrefix + 'slider-thumb-drag');

        me.dragging = slider.dragging = false;
        slider.fireEvent('dragend', slider, e);

        if (me.dragStartValue != value) {
            slider.fireEvent('changecomplete', slider, value, me);
        }
    },

    destroy: function() {
        Ext.destroy(this.tracker);
    }
});


Ext.define('Ext.slider.Tip', {
    extend:  Ext.tip.Tip ,
    minWidth: 10,
    alias: 'widget.slidertip',
    
    
    offsets : null,
    
    
    align: null,
    
    
    position: '',
    
    defaultVerticalPosition: 'left',
    
    defaultHorizontalPosition: 'top',

    isSliderTip: true,

    init: function(slider) {
        var me = this,
            align,
            offsets;
        
        if (!me.position) {
            me.position = slider.vertical ? me.defaultVerticalPosition : me.defaultHorizontalPosition;
        }
            
        switch (me.position) {
            case 'top':
                offsets = [0, -10];
                align = 'b-t?';
                break;
            case 'bottom':
                offsets = [0, 10];
                align = 't-b?';
                break;
            case 'left':
                offsets = [-10, 0];
                align = 'r-l?';
                break;
            case 'right':
                offsets = [10, 0];
                align = 'l-r?';
        }
        
        if (!me.align) {
            me.align = align;
        }
        
        if (!me.offsets) {
            me.offsets = offsets;
        }

        slider.on({
            scope    : me,
            dragstart: me.onSlide,
            drag     : me.onSlide,
            dragend  : me.hide,
            destroy  : me.destroy
        });
    },
    
    onSlide : function(slider, e, thumb) {
        var me = this;
        me.show();
        me.update(me.getText(thumb));
        me.el.alignTo(thumb.el, me.align, me.offsets);
    },

    
    getText : function(thumb) {
        return String(thumb.value);
    }
});


Ext.define('Ext.slider.Multi', {
    extend:  Ext.form.field.Base ,
    alias: 'widget.multislider',
    alternateClassName: 'Ext.slider.MultiSlider',

               
                           
                         
                     
                          
                       
                                           
      

    childEls: [
        'endEl', 'innerEl'
    ],

    
    fieldSubTpl: [
        '<div id="{id}" class="' + Ext.baseCSSPrefix + 'slider {fieldCls} {vertical}',
        '{childElCls}',
        '" aria-valuemin="{minValue}" aria-valuemax="{maxValue}" aria-valuenow="{value}" aria-valuetext="{value}">',
            '<div id="{cmpId}-endEl" class="' + Ext.baseCSSPrefix + 'slider-end" role="presentation">',
                '<div id="{cmpId}-innerEl" class="' + Ext.baseCSSPrefix + 'slider-inner" role="presentation">',
                    '{%this.renderThumbs(out, values)%}',
                '</div>',
            '</div>',
        '</div>',
        {
            renderThumbs: function(out, values) {
                var me = values.$comp,
                    i = 0,
                    thumbs = me.thumbs,
                    len = thumbs.length,
                    thumb,
                    thumbConfig;

                for (; i < len; i++) {
                    thumb = thumbs[i];
                    thumbConfig = thumb.getElConfig();
                    thumbConfig.id = me.id + '-thumb-' + i;
                    Ext.DomHelper.generateMarkup(thumbConfig, out);
                }
            },
            disableFormats: true
        }
    ],
    
    horizontalProp: 'left',

    

    

    
    vertical: false,

    
    minValue: 0,

    
    maxValue: 100,

    
    decimalPrecision: 0,

    
    keyIncrement: 1,

    
    increment: 0,

    

    
    clickRange: [5,15],

    
    clickToChange : true,

    
    animate: true,

    
    dragging: false,

    
    constrainThumbs: true,

    componentLayout: 'sliderfield',

    
    useTips : true,

    
    tipText : null,

    ariaRole: 'slider',

    
    initValue: function() {
        var me = this,
            extValue = Ext.value,
            
            values = extValue(me.values, [extValue(me.value, extValue(me.minValue, 0))]),
            i = 0,
            len = values.length;

        
        me.originalValue = values;

        
        for (; i < len; i++) {
            me.addThumb(values[i]);
        }
    },

    
    initComponent : function() {
        var me = this,
            tipPlug,
            hasTip,
            p, pLen, plugins;

        
        me.thumbs = [];

        me.keyIncrement = Math.max(me.increment, me.keyIncrement);

        me.addEvents(
            
            'beforechange',

            
            'change',

            
            'changecomplete',

            
            'dragstart',

            
            'drag',

            
            'dragend'
        );

        me.callParent();

        
        if (me.useTips) {
            if (Ext.isObject(me.useTips)) {
                tipPlug = Ext.apply({}, me.useTips);
            } else {
                tipPlug = me.tipText ? {getText: me.tipText} : {};    
            }

            plugins = me.plugins = me.plugins || [];
            pLen    = plugins.length;
            
            for (p = 0; p < pLen; p++) {
                if (plugins[p].isSliderTip) {
                    hasTip = true;
                    break;
                }
            }

            if (!hasTip) {
                me.plugins.push(new Ext.slider.Tip(tipPlug));
            }
        }
    },

    
    addThumb: function(value) {
        var me = this,
            thumb = new Ext.slider.Thumb({
                ownerCt     : me,
                ownerLayout : me.getComponentLayout(),
                value       : value,
                slider      : me,
                index       : me.thumbs.length,
                constrain   : me.constrainThumbs,
                disabled    : !!me.readOnly
            });

        me.thumbs.push(thumb);

        
        if (me.rendered) {
            thumb.render();
        }

        return thumb;
    },

    
    promoteThumb: function(topThumb) {
        var thumbs = this.thumbs,
            ln = thumbs.length,
            zIndex, thumb, i;

        for (i = 0; i < ln; i++) {
            thumb = thumbs[i];

            if (thumb == topThumb) {
                thumb.bringToFront();
            } else {
                thumb.sendToBack();
            }
        }
    },

    
    getSubTplData : function() {
        var me = this;

        return Ext.apply(me.callParent(), {
            $comp: me,
            vertical: me.vertical ? Ext.baseCSSPrefix + 'slider-vert' : Ext.baseCSSPrefix + 'slider-horz',
            minValue: me.minValue,
            maxValue: me.maxValue,
            value: me.value,
            childElCls: ''
        });
    },

    onRender : function() {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            i = 0,
            thumb;

        me.callParent(arguments);

        for (i = 0; i < len; i++) {
            thumb = thumbs[i];
            thumb.el = me.el.getById(me.id + '-thumb-' + i);
            thumb.onRender();
        }
    },

    
    initEvents : function() {
        var me = this;
        me.mon(me.el, {
            scope    : me,
            mousedown: me.onMouseDown,
            keydown  : me.onKeyDown
        });
    },
    
    onDragStart: Ext.emptyFn,
    onDragEnd: Ext.emptyFn,

    
    getTrackpoint : function(xy) {
        var me = this,
            vertical = me.vertical,
            sliderTrack = me.innerEl,
            trackLength, result,
            positionProperty;

        if (vertical) {
            positionProperty = 'top';
            trackLength = sliderTrack.getHeight();
        } else {
            positionProperty = me.horizontalProp;
            trackLength = sliderTrack.getWidth();
        }
        xy = me.transformTrackPoints(sliderTrack.translatePoints(xy));
        result = Ext.Number.constrain(xy[positionProperty], 0, trackLength);
        return vertical ? trackLength - result : result;
    },
    
    transformTrackPoints: Ext.identityFn,

    
    onMouseDown : function(e) {
        var me = this,
            thumbClicked = false,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            trackPoint;

        if (me.disabled) {
            return;
        }

        
        for (; i < len; i++) {
            thumbClicked = thumbClicked || e.target == thumbs[i].el.dom;
        }

        if (me.clickToChange && !thumbClicked) {
            trackPoint = me.getTrackpoint(e.getXY());
            if (trackPoint !== undefined) {
                me.onClickChange(trackPoint);
            }
        }
        me.focus();
    },

    
    onClickChange : function(trackPoint) {
        var me = this,
            thumb, index;

        
        

        
        thumb = me.getNearest(trackPoint);
        if (!thumb.disabled) {
            index = thumb.index;
            me.setValue(index, Ext.util.Format.round(me.reversePixelValue(trackPoint), me.decimalPrecision), undefined, true);
        }
    },

    
    getNearest: function(trackPoint) {
        var me = this,
            clickValue = me.reversePixelValue(trackPoint),
            nearestDistance = me.getRange() + 5, 
            nearest = null,
            thumbs = me.thumbs,
            i = 0,
            len = thumbs.length,
            thumb,
            value,
            dist;

        for (; i < len; i++) {
            thumb = me.thumbs[i];
            value = thumb.value;
            dist  = Math.abs(value - clickValue);

            if (Math.abs(dist <= nearestDistance)) {
                nearest = thumb;
                nearestDistance = dist;
            }
        }
        return nearest;
    },

    
    onKeyDown : function(e) {
        
        var me = this,
            k,
            val;

        if(me.disabled || me.thumbs.length !== 1) {
            e.preventDefault();
            return;
        }
        k = e.getKey();

        switch(k) {
            case e.UP:
            case e.RIGHT:
                e.stopEvent();
                val = e.ctrlKey ? me.maxValue : me.getValue(0) + me.keyIncrement;
                me.setValue(0, val, undefined, true);
            break;
            case e.DOWN:
            case e.LEFT:
                e.stopEvent();
                val = e.ctrlKey ? me.minValue : me.getValue(0) - me.keyIncrement;
                me.setValue(0, val, undefined, true);
            break;
            default:
                e.preventDefault();
        }
    },

    
    normalizeValue : function(v) {
        var me = this,
            snapFn = me.zeroBasedSnapping ? 'snap' : 'snapInRange';

        v = Ext.Number[snapFn](v, me.increment, me.minValue, me.maxValue);
        v = Ext.util.Format.round(v, me.decimalPrecision);
        v = Ext.Number.constrain(v, me.minValue, me.maxValue);
        return v;
    },

    
    setMinValue : function(val) {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb, i;

        me.minValue = val;
        if (me.rendered) {
            me.inputEl.dom.setAttribute('aria-valuemin', val);
        }

        for (i = 0; i < len; ++i) {
            thumb = thumbs[i];
            if (thumb.value < val) {
                me.setValue(i, val, false);    
            }
        }
        me.syncThumbs();
    },

    
    setMaxValue : function(val) {
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb, i;

        me.maxValue = val;
        if (me.rendered) {
            me.inputEl.dom.setAttribute('aria-valuemax', val);
        }

        for (i = 0; i < len; ++i) {
            thumb = thumbs[i];
            if (thumb.value > val) {
                me.setValue(i, val, false);
            }
        }
        me.syncThumbs();
    },

    
    setValue : function(index, value, animate, changeComplete) {
        var me = this,
            thumbs = me.thumbs,
            thumb, len, i, values;
            
        if (Ext.isArray(index)) {
            values = index;
            animate = value;
            
            for (i = 0, len = values.length; i < len; ++i) {
                thumb = thumbs[i];
                if (thumb) {
                    me.setValue(i, values[i], animate);
                }    
            }
            return me;
        }

        thumb = me.thumbs[index];
        
        value = me.normalizeValue(value);

        if (value !== thumb.value && me.fireEvent('beforechange', me, value, thumb.value, thumb) !== false) {
            thumb.value = value;
            if (me.rendered) {
                
                
                me.inputEl.set({
                    'aria-valuenow': value,
                    'aria-valuetext': value
                });

                thumb.move(me.calculateThumbPosition(value), Ext.isDefined(animate) ? animate !== false : me.animate);

                me.fireEvent('change', me, value, thumb);
                me.checkDirty();
                if (changeComplete) {
                    me.fireEvent('changecomplete', me, value, thumb);
                }
            }
        }
        return me;
    },

    
    calculateThumbPosition : function(v) {
        var me = this,
            minValue = me.minValue,
            pos = (v - minValue) / me.getRange() * 100;

        
        if (isNaN(pos)) {
            pos = minValue;
        }

        return pos;
    },

    
    getRatio : function() {
        var me = this,
            innerEl = me.innerEl,
            trackLength = me.vertical ? innerEl.getHeight() : innerEl.getWidth(),
            valueRange = me.getRange();
            
        return valueRange === 0 ? trackLength : (trackLength / valueRange);
    },
    
    getRange: function(){
        return this.maxValue - this.minValue;
    },

    
    reversePixelValue : function(pos) {
        return this.minValue + (pos / this.getRatio());
    },

    
    reversePercentageValue : function(pos) {
        return this.minValue + this.getRange() * (pos / 100);
    },

    
    onDisable: function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb,
            el,
            xy;

        me.callParent();

        for (; i < len; i++) {
            thumb = thumbs[i];
            el = thumb.el;

            thumb.disable();

            if(Ext.isIE) {
                
                
                xy = el.getXY();
                el.hide();

                me.innerEl.addCls(me.disabledCls).dom.disabled = true;

                if (!me.thumbHolder) {
                    me.thumbHolder = me.endEl.createChild({cls: Ext.baseCSSPrefix + 'slider-thumb ' + me.disabledCls});
                }

                me.thumbHolder.show().setXY(xy);
            }
        }
    },

    
    onEnable: function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb,
            el;

        this.callParent();

        for (; i < len; i++) {
            thumb = thumbs[i];
            el = thumb.el;

            thumb.enable();

            if (Ext.isIE) {
                me.innerEl.removeCls(me.disabledCls).dom.disabled = false;

                if (me.thumbHolder) {
                    me.thumbHolder.hide();
                }

                el.show();
                me.syncThumbs();
            }
        }
    },

    
    syncThumbs : function() {
        if (this.rendered) {
            var thumbs = this.thumbs,
                length = thumbs.length,
                i = 0;

            for (; i < length; i++) {
                thumbs[i].move(this.calculateThumbPosition(thumbs[i].value));
            }
        }
    },

    
    getValue : function(index) {
        return Ext.isNumber(index) ? this.thumbs[index].value : this.getValues();
    },

    
    getValues: function() {
        var values = [],
            i = 0,
            thumbs = this.thumbs,
            len = thumbs.length;

        for (; i < len; i++) {
            values.push(thumbs[i].value);
        }

        return values;
    },

    getSubmitValue: function() {
        var me = this;
        return (me.disabled || !me.submitValue) ? null : me.getValue();
    },

    reset: function() {
        var me = this,
            arr = [].concat(me.originalValue),
            a     = 0,
            aLen  = arr.length,
            val;

        for (; a < aLen; a++) {
            val = arr[a];

            me.setValue(a, val);
        }

        me.clearInvalid();
        
        delete me.wasValid;
    },
    
    setReadOnly: function(readOnly){
        var me = this,
            thumbs = me.thumbs,
            len = thumbs.length,
            i = 0;
            
        me.callParent(arguments); 
        readOnly = me.readOnly;
        
        for (; i < len; ++i) {
            if (readOnly) {
                thumbs[i].disable();
            } else {
                thumbs[i].enable();
            }
            
        }
           
    },

    
    beforeDestroy : function() {
        var me     = this,
            thumbs = me.thumbs,
            t      = 0,
            tLen   = thumbs.length,
            thumb;

        Ext.destroy(me.innerEl, me.endEl, me.focusEl);

        for (; t < tLen; t++) {
            thumb = thumbs[t];

            Ext.destroy(thumb);
        }

        me.callParent();
    }
});


Ext.define('Ext.tab.Tab', {
    extend:  Ext.button.Button ,
    alias: 'widget.tab',

               
                         
      

    
    isTab: true,

    baseCls: Ext.baseCSSPrefix + 'tab',
    closeElOverCls: Ext.baseCSSPrefix + 'tab-close-btn-over',

    
    activeCls: 'active',

    

    
    closableCls: 'closable',

    
    closable: true,

    
    
    closeText: 'Close Tab',
    

    
    active: false,

    

    childEls: [
        'closeEl'
    ],

    scale: false,

    position: 'top',

    initComponent: function() {
        var me = this;

        me.addEvents(
            
            'activate',

            
            'deactivate',

            
            'beforeclose',

            
            'close'
        );

        me.callParent(arguments);

        if (me.card) {
            me.setCard(me.card);
        }

        me.overCls = ['over', me.position + '-over'];
    },

    getTemplateArgs: function() {
        var me = this,
            result = me.callParent();

        result.closable = me.closable;
        result.closeText = me.closeText;

        return result;
    },
    
    getFramingInfoCls: function(){
        return this.baseCls + '-' + this.ui + '-' + this.position;
    },

    beforeRender: function() {
        var me = this,
            tabBar = me.up('tabbar'),
            tabPanel = me.up('tabpanel');
        
        me.callParent();
        
        me.addClsWithUI(me.position);

        if (me.active) {
            me.addClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);
        }

        
        

        me.syncClosableUI();

        
        if (!me.minWidth) {
            me.minWidth = (tabBar) ? tabBar.minTabWidth : me.minWidth;
            if (!me.minWidth && tabPanel) {
                me.minWidth = tabPanel.minTabWidth;
            }
            if (me.minWidth && me.iconCls) {
                me.minWidth += 25;
            }
        }
        if (!me.maxWidth) {
            me.maxWidth = (tabBar) ? tabBar.maxTabWidth : me.maxWidth;
            if (!me.maxWidth && tabPanel) {
                me.maxWidth = tabPanel.maxTabWidth;
            }
        }
    },

    onRender: function() {
        var me = this;

        me.setElOrientation();

        me.callParent(arguments);

        if (me.closable) {
            me.closeEl.addClsOnOver(me.closeElOverCls);
        }

        me.keyNav = new Ext.util.KeyNav(me.el, {
            enter: me.onEnterKey,
            del: me.onDeleteKey,
            scope: me
        });
    },

    setElOrientation: function() {
        var position = this.position;

        if (position === 'left' || position === 'right') {
            this.el.setVertical(position === 'right' ? 90 : 270);
        }
    },

    
    enable : function(silent) {
        var me = this;

        me.callParent(arguments);

        me.removeClsWithUI(me.position + '-disabled');

        return me;
    },

    
    disable : function(silent) {
        var me = this;

        me.callParent(arguments);

        me.addClsWithUI(me.position + '-disabled');

        return me;
    },

    onDestroy: function() {
        var me = this;

        Ext.destroy(me.keyNav);
        delete me.keyNav;

        me.callParent(arguments);
    },

    
    setClosable: function(closable) {
        var me = this;

        
        closable = (!arguments.length || !!closable);

        if (me.closable != closable) {
            me.closable = closable;

            
            if (me.card) {
                me.card.closable = closable;
            }

            me.syncClosableUI();

            if (me.rendered) {
                me.syncClosableElements();

                
                me.updateLayout();
            }
        }
    },

    
    syncClosableElements: function () {
        var me = this,
            closeEl = me.closeEl;

        if (me.closable) {
            if (!closeEl) {
                closeEl = me.closeEl = me.btnWrap.insertSibling({
                    tag: 'a',
                    cls: me.baseCls + '-close-btn',
                    href: '#',
                    title: me.closeText
                }, 'after');
            }
            closeEl.addClsOnOver(me.closeElOverCls);
        } else if (closeEl) {
            closeEl.remove();
            delete me.closeEl;
        }
    },

    
    syncClosableUI: function () {
        var me = this,
            classes = [me.closableCls, me.closableCls + '-' + me.position];

        if (me.closable) {
            me.addClsWithUI(classes);
        } else {
            me.removeClsWithUI(classes);
        }
    },

    
    setCard: function(card) {
        var me = this;

        me.card = card;
        me.setText(me.title || card.title);
        me.setIconCls(me.iconCls || card.iconCls);
        me.setIcon(me.icon || card.icon);
        me.setGlyph(me.glyph || card.glyph);
    },

    
    onCloseClick: function() {
        var me = this;

        if (me.fireEvent('beforeclose', me) !== false) {
            if (me.tabBar) {
                if (me.tabBar.closeTab(me) === false) {
                    
                    return;
                }
            } else {
                
                me.fireClose();
            }
        }
    },

    
    fireClose: function(){
        this.fireEvent('close', this);
    },

    
    onEnterKey: function(e) {
        var me = this;

        if (me.tabBar) {
            me.tabBar.onClick(e, me.el);
        }
    },

   
    onDeleteKey: function(e) {
        if (this.closable) {
            this.onCloseClick();
        }
    },

    
    activate : function(supressEvent) {
        var me = this;

        me.active = true;
        me.addClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);

        if (supressEvent !== true) {
            me.fireEvent('activate', me);
        }
    },

    
    deactivate : function(supressEvent) {
        var me = this;

        me.active = false;
        me.removeClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);

        if (supressEvent !== true) {
            me.fireEvent('deactivate', me);
        }
    }
});


Ext.define('Ext.util.Point', {

    
    extend:  Ext.util.Region ,

    statics: {

        
        fromEvent: function(e) {
            e = e.browserEvent || e;
            e = (e.changedTouches && e.changedTouches.length > 0) ? e.changedTouches[0] : e;
            return new this(e.pageX, e.pageY);
        }
    },

    

    
    constructor: function(x, y) {
        this.callParent([y, x, y, x]);
    },

    
    toString: function() {
        return "Point[" + this.x + "," + this.y + "]";
    },

    
    equals: function(p) {
        return (this.x == p.x && this.y == p.y);
    },

    
    isWithin: function(p, threshold) {
        if (!Ext.isObject(threshold)) {
            threshold = {
                x: threshold,
                y: threshold
            };
        }

        return (this.x <= p.x + threshold.x && this.x >= p.x - threshold.x &&
                this.y <= p.y + threshold.y && this.y >= p.y - threshold.y);
    },

    
    isContainedBy: function(region) {
        if (!(region instanceof Ext.util.Region)) {
            region = Ext.get(region.el || region).getRegion();
        }
        return region.contains(this);
    },

    
    roundedEquals: function(p) {
        return (Math.round(this.x) == Math.round(p.x) && Math.round(this.y) == Math.round(p.y));
    }
}, function() {
    
    this.prototype.translate = Ext.util.Region.prototype.translateBy;
});


Ext.define('Ext.tab.Bar', {
    extend:  Ext.panel.Header ,
    alias: 'widget.tabbar',
    baseCls: Ext.baseCSSPrefix + 'tab-bar',

               
                      
                        
      

    
    isTabBar: true,
    
    
    
    

    
    defaultType: 'tab',

    
    plain: false,

    childEls: [
        'body', 'strip'
    ],

    
    renderTpl: [
        '<div id="{id}-body" class="{baseCls}-body {bodyCls} {bodyTargetCls}{childElCls}<tpl if="ui"> {baseCls}-body-{ui}<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl></tpl>"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
            '{%this.renderContainer(out,values)%}',
        '</div>',
        '<div id="{id}-strip" class="{baseCls}-strip {baseCls}-strip-{dock}{childElCls}',
            '<tpl if="ui"> {baseCls}-strip-{ui}',
                '<tpl for="uiCls"> {parent.baseCls}-strip-{parent.ui}-{.}</tpl>',
            '</tpl>">',
        '</div>'
    ],

    

    

    _reverseDockNames: {
        left: 'right',
        right: 'left'
    },

    
    initComponent: function() {
        var me = this;

        if (me.plain) {
            me.addCls(me.baseCls + '-plain');
        }

        me.addClsWithUI(me.orientation);

        me.addEvents(
            
            'change'
        );

        
        me.callParent(arguments);
        Ext.merge(me.layout, me.initialConfig.layout);

        
        me.layout.align = (me.orientation == 'vertical') ? 'left' : 'top';
        me.layout.overflowHandler = new Ext.layout.container.boxOverflow.Scroller(me.layout);

        me.remove(me.titleCmp);
        delete me.titleCmp;

        Ext.apply(me.renderData, {
            bodyCls: me.bodyCls,
            dock: me.dock
        });
    },

    onRender: function() {
        var me = this;

        me.callParent();

        if (me.orientation === 'vertical' && (Ext.isIE8 || Ext.isIE9) && Ext.isStrict) {
            me.el.on({
                mousemove: me.onMouseMove, 
                scope: me
            });
        }
    },

    afterRender: function() {
        var layout = this.layout;

        this.callParent();
        if (Ext.isIE9 && Ext.isStrict && this.orientation === 'vertical') {
            
            
            layout.innerCt.on('scroll', function() {
                layout.innerCt.dom.scrollLeft = 0;
            });
        }
    },

    afterLayout: function() {
        this.adjustTabPositions();
        this.callParent(arguments);
    },

    adjustTabPositions: function() {
        var items = this.items.items,
            i = items.length,
            tab;

        
        
        
        
        
        if (!Ext.isIE9m) {
            if (this.dock === 'right') {
                
                
                while (i--) {
                    tab = items[i];
                    if (tab.isVisible()) {
                        tab.el.setStyle('left', tab.lastBox.width + 'px');
                    }
                }
            } else if (this.dock === 'left') {
                
                
                while (i--) {
                    tab = items[i];
                    if (tab.isVisible()) {
                        tab.el.setStyle('left', -tab.lastBox.height + 'px');
                    }
                }
            }
        }
    },

    getLayout: function() {
        var me = this;
        me.layout.type = (me.orientation === 'horizontal') ? 'hbox' : 'vbox';
        return me.callParent(arguments);
    },

    
    onAdd: function(tab) {
        tab.position = this.dock;
        this.callParent(arguments);
    },
    
    onRemove: function(tab) {
        var me = this;
        
        if (tab === me.previousTab) {
            me.previousTab = null;
        }
        me.callParent(arguments);    
    },

    afterComponentLayout : function(width) {
        var me = this,
            needsScroll = me.needsScroll;
        
        me.callParent(arguments);
            
        if (needsScroll) {
            me.layout.overflowHandler.scrollToItem(me.activeTab);
        }    
        delete me.needsScroll;
    },

    
    onClick: function(e, target) {
        var me = this,
            tabPanel = me.tabPanel,
            tabEl, tab, isCloseClick, tabInfo;

        if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) {
            return;
        }

        if (me.orientation === 'vertical' && (Ext.isIE8 || Ext.isIE9) && Ext.isStrict) {
            tabInfo = me.getTabInfoFromPoint(e.getXY());
            tab = tabInfo.tab;
            isCloseClick = tabInfo.close;
        } else {
            
            tabEl = e.getTarget('.' + Ext.tab.Tab.prototype.baseCls);
            tab = tabEl && Ext.getCmp(tabEl.id);
            isCloseClick = tab && tab.closeEl && (target === tab.closeEl.dom);
        }

        if (isCloseClick) {
            e.preventDefault();
        }
        if (tab && tab.isDisabled && !tab.isDisabled()) {
            if (tab.closable && isCloseClick) {
                tab.onCloseClick();
            } else {
                if (tabPanel) {
                    
                    tabPanel.setActiveTab(tab.card);
                } else {
                    me.setActiveTab(tab);
                }
                tab.focus();
            }
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            overTab = me._overTab,
            tabInfo, tab;

        if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) {
            return;
        }

        tabInfo = me.getTabInfoFromPoint(e.getXY());
        tab = tabInfo.tab;

        if (tab !== overTab) {
            if (overTab && overTab.rendered) {
                overTab.onMouseLeave(e);
                me._overTab = null;
            }
            if (tab) {
                tab.onMouseEnter(e);
                me._overTab = tab;
                if (!tab.disabled) {
                    me.el.setStyle('cursor', 'pointer');
                }
            } else {
                me.el.setStyle('cursor', 'default');
            }
        }
    },

    onMouseLeave: function(e) {
        var overTab = this._overTab;

        if (overTab && overTab.rendered) {
            overTab.onMouseLeave(e);
        }
    },

    
    
    
    
    
    
    getTabInfoFromPoint: function(xy) {
        var me = this,
            tabs = me.items.items,
            length = tabs.length,
            innerCt = me.layout.innerCt,
            innerCtXY = innerCt.getXY(),
            point = new Ext.util.Point(xy[0], xy[1]),
            i = 0,
            lastBox, tabRegion, closeEl, close, closeXY, closeX, closeY, closeWidth,
            closeHeight, tabX, tabY, tabWidth, tabHeight, closeRegion, isTabReversed,
            direction, tab;

        for (; i < length; i++) {
            lastBox = tabs[i].lastBox;
            tabX = innerCtXY[0] + lastBox.x;
            tabY = innerCtXY[1] - innerCt.dom.scrollTop + lastBox.y;
            tabWidth = lastBox.width;
            tabHeight = lastBox.height;
            tabRegion = new Ext.util.Region(
                tabY,
                tabX + tabWidth,
                tabY + tabHeight,
                tabX
            );
            if (tabRegion.contains(point)) {
                tab = tabs[i];
                closeEl = tab.closeEl;
                if (closeEl) {
                    closeXY = closeEl.getXY();
                    closeWidth = closeEl.getWidth();
                    closeHeight = closeEl.getHeight();
                    
                    
                    
                    if (me._isTabReversed === undefined) {
                        me._isTabReversed = isTabReversed =
                        
                        
                        (tab.btnWrap.dom.currentStyle.filter.indexOf('rotation=2') !== -1);
                    }

                    direction = isTabReversed ? this._reverseDockNames[me.dock] : me.dock;

                    if (direction === 'right') {
                        closeX = tabX + tabWidth - ((closeXY[1] - tabY) + closeEl.getHeight()); 
                        closeY = tabY + (closeXY[0] - tabX); 
                    } else {
                        closeX = tabX + (closeXY[1] - tabY);
                        closeY = tabY + tabX + tabHeight - closeXY[0] - closeEl.getWidth();
                    }
                        
                    closeRegion = new Ext.util.Region(
                        closeY,
                        closeX + closeWidth,
                        closeY + closeHeight,
                        closeX
                    );

                    close = closeRegion.contains(point);
                }
                break;
            }
        }
            
        return {
            tab: tab,
            close: close
        };
    },

    
    closeTab: function(toClose) {
        var me = this,
            card = toClose.card,
            tabPanel = me.tabPanel,
            toActivate;

        if (card && card.fireEvent('beforeclose', card) === false) {
            return false;
        }
        
        
        
        
        toActivate = me.findNextActivatable(toClose);

        
        
        Ext.suspendLayouts();

        if (tabPanel && card) {
            
            
            delete toClose.ownerCt;
            
            
            
            card.fireEvent('close', card);
            tabPanel.remove(card);
            
            
            if (!tabPanel.getComponent(card)) {
                
                toClose.fireClose();
                me.remove(toClose);
            } else {
                
                toClose.ownerCt = me;
                Ext.resumeLayouts(true);
                return false;
            }
        }

        
        if (toActivate) {
            
            
            if (tabPanel) {
                tabPanel.setActiveTab(toActivate.card);
            } else {
                me.setActiveTab(toActivate);
            }
            toActivate.focus();
        }
        Ext.resumeLayouts(true);
    },

    
    
    findNextActivatable: function(toClose) {
        var me = this;
        if (toClose.active && me.items.getCount() > 1) {
            return (me.previousTab && me.previousTab !== toClose && !me.previousTab.disabled) ? me.previousTab : (toClose.next('tab[disabled=false]') || toClose.prev('tab[disabled=false]'));
        }
    },

    
    setActiveTab: function(tab, initial) {
        var me = this;

        if (!tab.disabled && tab !== me.activeTab) {
            if (me.activeTab) {
                if (me.activeTab.isDestroyed) {
                    me.previousTab = null;
                } else {
                    me.previousTab = me.activeTab;
                    me.activeTab.deactivate();
                }
            }
            tab.activate();

            me.activeTab = tab;
            me.needsScroll = true;
            
            
            
            if (!initial) {
                me.fireEvent('change', me, tab, tab.card);
                
                me.updateLayout();
            }
        }
    }
});


Ext.define('Ext.tree.Column', {
    extend:  Ext.grid.column.Column ,
    alias: 'widget.treecolumn',

    tdCls: Ext.baseCSSPrefix + 'grid-cell-treecolumn',

    autoLock: true,
    lockable: false,
    draggable: false,
    hideable: false,

    iconCls: Ext.baseCSSPrefix + 'tree-icon',
    checkboxCls: Ext.baseCSSPrefix + 'tree-checkbox',
    elbowCls: Ext.baseCSSPrefix + 'tree-elbow',
    expanderCls: Ext.baseCSSPrefix + 'tree-expander',
    textCls: Ext.baseCSSPrefix + 'tree-node-text',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-treecolumn',
    isTreeColumn: true,

    cellTpl: [
        '<tpl for="lines">',
            '<img src="{parent.blankUrl}" class="{parent.childCls} {parent.elbowCls}-img ',
            '{parent.elbowCls}-<tpl if=".">line<tpl else>empty</tpl>"/>',
        '</tpl>',
        '<img src="{blankUrl}" class="{childCls} {elbowCls}-img {elbowCls}',
            '<tpl if="isLast">-end</tpl><tpl if="expandable">-plus {expanderCls}</tpl>"/>',
        '<tpl if="checked !== null">',
            '<input type="button" role="checkbox" <tpl if="checked">aria-checked="true" </tpl>',
                'class="{childCls} {checkboxCls}<tpl if="checked"> {checkboxCls}-checked</tpl>"/>',
        '</tpl>',
        '<img src="{blankUrl}" class="{childCls} {baseIconCls} ',
            '{baseIconCls}-<tpl if="leaf">leaf<tpl else>parent</tpl> {iconCls}"',
            '<tpl if="icon">style="background-image:url({icon})"</tpl>/>',
        '<tpl if="href">',
            '<a href="{href}" target="{hrefTarget}" class="{textCls} {childCls}">{value}</a>',
        '<tpl else>',
            '<span class="{textCls} {childCls}">{value}</span>',
        '</tpl>'
    ],

    initComponent: function() {
        var me = this;

        me.origRenderer = me.renderer;
        me.origScope = me.scope || window;

        me.renderer = me.treeRenderer;
        me.scope = me;

        me.callParent();
    },

    treeRenderer: function(value, metaData, record, rowIdx, colIdx, store, view){
        var me = this,
            cls = record.get('cls'),
            renderer = me.origRenderer,
            data = record.data,
            parent = record.parentNode,
            rootVisible = view.rootVisible,
            lines = [],
            parentData;

        if (cls) {
            metaData.tdCls += ' ' + cls;
        }

        while (parent && (rootVisible || parent.data.depth > 0)) {
            parentData = parent.data;
            lines[rootVisible ? parentData.depth : parentData.depth - 1] =
                    parentData.isLast ? 0 : 1;
            parent = parent.parentNode;
        }

        return me.getTpl('cellTpl').apply({
            record: record,
            baseIconCls: me.iconCls,
            iconCls: data.iconCls,
            icon: data.icon,
            checkboxCls: me.checkboxCls,
            checked: data.checked,
            elbowCls: me.elbowCls,
            expanderCls: me.expanderCls,
            textCls: me.textCls,
            leaf: data.leaf,
            expandable: record.isExpandable(),
            isLast: data.isLast,
            blankUrl: Ext.BLANK_IMAGE_URL,
            href: data.href,
            hrefTarget: data.hrefTarget,
            lines: lines,
            metaData: metaData,
            
            
            
            
            childCls: me.getChildCls ? me.getChildCls() + ' ' : '',
            value: renderer ? renderer.apply(me.origScope, arguments) : value
        });
    }
});


Ext.define('Ext.selection.CheckboxModel', {
    alias: 'selection.checkboxmodel',
    extend:  Ext.selection.RowModel ,

    
    mode: 'MULTI',

    
    injectCheckbox: 0,

    
    checkOnly: false,
    
    
    showHeaderCheckbox: undefined,
    
    
    checkSelector: '.' + Ext.baseCSSPrefix + 'grid-row-checker',

    headerWidth: 24,

    
    checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on',
    
    constructor: function(){
        var me = this;
        me.callParent(arguments);   
        
        
        
        if (me.mode === 'SINGLE' && me.showHeaderCheckbox !== true) {
            me.showHeaderCheckbox = false;
        } 
    },

    beforeViewRender: function(view) {
        var me = this,
            owner;
            
        me.callParent(arguments);

        
        if (!me.hasLockedHeader() || view.headerCt.lockedCt) {
            if (me.showHeaderCheckbox !== false) {
                view.headerCt.on('headerclick', me.onHeaderClick, me);
            }
            me.addCheckbox(view, true);
            owner = view.ownerCt;
            
            if (view.headerCt.lockedCt) {
                owner = owner.ownerCt;
            }
            me.mon(owner, 'reconfigure', me.onReconfigure, me);
        }
    },

    bindComponent: function(view) {
        var me = this;
        me.sortable = false;
        me.callParent(arguments);
    },

    hasLockedHeader: function(){
        var views     = this.views,
            vLen      = views.length,
            v;

        for (v = 0; v < vLen; v++) {
            if (views[v].headerCt.lockedCt) {
                return true;
            }
        }
        return false;
    },

    
    addCheckbox: function(view, initial){
        var me = this,
            checkbox = me.injectCheckbox,
            headerCt = view.headerCt;

        
        if (checkbox !== false) {
            if (checkbox == 'first') {
                checkbox = 0;
            } else if (checkbox == 'last') {
                checkbox = headerCt.getColumnCount();
            }
            Ext.suspendLayouts();
            if (view.getStore().buffered) {
                me.showHeaderCheckbox = false;
            }
            headerCt.add(checkbox,  me.getHeaderConfig());
            Ext.resumeLayouts();
        }

        if (initial !== true) {
            view.refresh();
        }
    },

    
    onReconfigure: function(grid, store, columns) {
        if(columns) {
            this.addCheckbox(this.views[0]);
        }
    },

    
    toggleUiHeader: function(isChecked) {
        var view     = this.views[0],
            headerCt = view.headerCt,
            checkHd  = headerCt.child('gridcolumn[isCheckerHd]'),
            cls = this.checkerOnCls;

        if (checkHd) {
            if (isChecked) {
                checkHd.addCls(cls);
            } else {
                checkHd.removeCls(cls);
            }
        }
    },

    
    onHeaderClick: function(headerCt, header, e) {
        if (header.isCheckerHd) {
            e.stopEvent();
            var me = this,
                isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on');
                
            
            me.preventFocus = true;
            if (isChecked) {
                me.deselectAll();
            } else {
                me.selectAll();
            }
            delete me.preventFocus;
        }
    },

    
    getHeaderConfig: function() {
        var me = this,
            showCheck = me.showHeaderCheckbox !== false;     

        return {
            isCheckerHd: showCheck,
            text : '&#160;',
            clickTargetName: 'el',
            width: me.headerWidth,
            sortable: false,
            draggable: false,
            resizable: false,
            hideable: false,
            menuDisabled: true,
            dataIndex: '',
            cls: showCheck ? Ext.baseCSSPrefix + 'column-header-checkbox ' : '',
            renderer: Ext.Function.bind(me.renderer, me),
            editRenderer: me.editRenderer || me.renderEmpty,
            locked: me.hasLockedHeader()
        };
    },

    renderEmpty: function() {
        return '&#160;';
    },

    
    refresh: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },

    
    renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
        var baseCSSPrefix = Ext.baseCSSPrefix;
        metaData.tdCls = baseCSSPrefix + 'grid-cell-special ' + baseCSSPrefix + 'grid-cell-row-checker';
        return '<div class="' + baseCSSPrefix + 'grid-row-checker">&#160;</div>';
    },
    
    processSelection: function(view, record, item, index, e){
        var me = this,
            checker = e.getTarget(me.checkSelector),
            mode;
            
        
        if (me.checkOnly && !checker) {
            return;
        }

        if (checker) {
            mode = me.getSelectionMode();
            
            
            if (mode !== 'SINGLE') {
                me.setSelectionMode('SIMPLE');
            }
            me.selectWithEvent(record, e);
            me.setSelectionMode(mode);
        } else {
            me.selectWithEvent(record, e);
        }
    },

    
    onSelectChange: function() {
        this.callParent(arguments);
        if (!this.suspendChange) {
            this.updateHeaderState();
        }
    },

    
    onStoreLoad: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },

    onStoreAdd: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },

    onStoreRemove: function() {
        this.callParent(arguments);
        this.updateHeaderState();
    },
    
    onStoreRefresh: function(){
        this.callParent(arguments);    
        this.updateHeaderState();
    },
    
    maybeFireSelectionChange: function(fireEvent) {
        if (fireEvent && !this.suspendChange) {
            this.updateHeaderState();
        }
        this.callParent(arguments);
    },
    
    resumeChanges: function(){
        this.callParent();
        if (!this.suspendChange) {
            this.updateHeaderState();
        }
    },

    
    updateHeaderState: function() {
        
        var me = this,
            store = me.store,
            storeCount = store.getCount(),
            views = me.views,
            hdSelectStatus = false,
            selectedCount = 0,
            selected, len, i;
            
        if (!store.buffered && storeCount > 0) {
            selected = me.selected;
            hdSelectStatus = true;
            for (i = 0, len = selected.getCount(); i < len; ++i) {
                if (!me.storeHasSelected(selected.getAt(i))) {
                    break;
                }
                ++selectedCount;
            }
            hdSelectStatus = storeCount === selectedCount;
        }
            
        if (views && views.length) {
            me.toggleUiHeader(hdSelectStatus);
        }
    }
});


Ext.define('Ext.slider.Single', {
    extend:  Ext.slider.Multi ,
    alias: ['widget.slider', 'widget.sliderfield'],
    alternateClassName: ['Ext.Slider', 'Ext.form.SliderField', 'Ext.slider.SingleSlider', 'Ext.slider.Slider'],

    
    getValue: function() {
        
        return this.callParent([0]);
    },

    
    setValue: function(value, animate) {
        var args = arguments,
            len  = args.length;

        
        
        
        if (len == 1 || (len <= 3 && typeof args[1] != 'number')) {
            args = Ext.toArray(args);
            args.unshift(0);
        }
        return this.callParent(args);
    },

    
    getNearest : function(){
        
        return this.thumbs[0];
    }
});


Ext.define('Ext.state.CookieProvider', {
    extend:  Ext.state.Provider ,

    

    

    

    

    
    constructor : function(config){
        var me = this;
        me.path = "/";
        me.expires = new Date(Ext.Date.now() + (1000*60*60*24*7)); 
        me.domain = null;
        me.secure = false;
        me.callParent(arguments);
        me.state = me.readCookies();
    },

    
    set : function(name, value){
        var me = this;

        if(typeof value == "undefined" || value === null){
            me.clear(name);
            return;
        }
        me.setCookie(name, value);
        me.callParent(arguments);
    },

    
    clear : function(name){
        this.clearCookie(name);
        this.callParent(arguments);
    },

    
    readCookies : function(){
        var cookies = {},
            c = document.cookie + ";",
            re = /\s?(.*?)=(.*?);/g,
            prefix = this.prefix,
            len = prefix.length,
            matches,
            name,
            value;

        while((matches = re.exec(c)) != null){
            name = matches[1];
            value = matches[2];
            if (name && name.substring(0, len) == prefix){
                cookies[name.substr(len)] = this.decodeValue(value);
            }
        }
        return cookies;
    },

    
    setCookie : function(name, value){
        var me = this;

        document.cookie = me.prefix + name + "=" + me.encodeValue(value) +
           ((me.expires == null) ? "" : ("; expires=" + me.expires.toGMTString())) +
           ((me.path == null) ? "" : ("; path=" + me.path)) +
           ((me.domain == null) ? "" : ("; domain=" + me.domain)) +
           ((me.secure == true) ? "; secure" : "");
    },

    
    clearCookie : function(name){
        var me = this;

        document.cookie = me.prefix + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
           ((me.path == null) ? "" : ("; path=" + me.path)) +
           ((me.domain == null) ? "" : ("; domain=" + me.domain)) +
           ((me.secure == true) ? "; secure" : "");
    }
});



Ext.define('Ext.state.LocalStorageProvider', {
    
    
    extend:  Ext.state.Provider ,
    
    alias: 'state.localstorage',
    
    
   
    constructor: function(){
        var me = this;
        me.callParent(arguments);
        me.store = me.getStorageObject();
        if (me.store) {
            me.state = me.readLocalStorage();
        } else {
            me.state = {};
        }
    },
    
    readLocalStorage: function(){
        var store = this.store,
            i = 0,
            len = store.length,
            prefix = this.prefix,
            prefixLen = prefix.length,
            data = {},
            key;
            
        for (; i < len; ++i) {
            key = store.key(i);
            if (key.substring(0, prefixLen) == prefix) {
                data[key.substr(prefixLen)] = this.decodeValue(store.getItem(key));
            }            
        }
        return data;
    },
    
    set : function(name, value){
        var me = this;
        
        me.clear(name);
        if (typeof value == "undefined" || value === null) {
            return;
        }
        me.store.setItem(me.prefix + name, me.encodeValue(value));
        me.callParent(arguments);
    },

    
    clear : function(name){
        this.store.removeItem(this.prefix + name);
        this.callParent(arguments);
    },
    
    getStorageObject: function(){
        if (Ext.supports.LocalStorage) {
            return window.localStorage;
        }
        return false;
    }    
});


Ext.define('Ext.tab.Panel', {
    extend:  Ext.panel.Panel ,
    alias: 'widget.tabpanel',
    alternateClassName: ['Ext.TabPanel'],

                                                           

    
    tabPosition : 'top',

    

    

    

    

    
    removePanelHeader: true,

    
    plain: false,

    
    itemCls: Ext.baseCSSPrefix + 'tabpanel-child',

    
    minTabWidth: undefined,

    
    maxTabWidth: undefined,

    
    deferredRender : true,

    
    initComponent: function() {
        var me = this,
            dockedItems = [].concat(me.dockedItems || []),
            activeTab = me.activeTab || (me.activeTab = 0),
            tabPosition = me.tabPosition;

        
        me.layout = new Ext.layout.container.Card(Ext.apply({
            owner: me,
            deferredRender: me.deferredRender,
            itemCls: me.itemCls,
            activeItem: activeTab
        }, me.layout));

        
        me.tabBar = new Ext.tab.Bar(Ext.apply({
            ui: me.ui,
            dock: me.tabPosition,
            orientation: (tabPosition == 'top' || tabPosition == 'bottom') ? 'horizontal' : 'vertical',
            plain: me.plain,
            cardLayout: me.layout,
            tabPanel: me
        }, me.tabBar));

        dockedItems.push(me.tabBar);
        me.dockedItems = dockedItems;

        me.addEvents(
            
            'beforetabchange',

            
            'tabchange'
        );

        me.callParent(arguments);

        
        activeTab = me.activeTab = me.getComponent(activeTab);

        
        if (activeTab) {
        	me.tabBar.setActiveTab(activeTab.tab, true);
        }
    },

    
    setActiveTab: function(card) {
        var me = this,
            previous;

        card = me.getComponent(card);
        if (card) {
            previous = me.getActiveTab();

            if (previous !== card && me.fireEvent('beforetabchange', me, card, previous) === false) {
                return false;
            }

            
            
            if (!card.isComponent) {
                Ext.suspendLayouts();
                card = me.add(card);
                Ext.resumeLayouts();
            }

            
            
            me.activeTab = card;

            
            
            
            Ext.suspendLayouts();
            me.layout.setActiveItem(card);

            
            card = me.activeTab = me.layout.getActiveItem();

            
            if (card && card !== previous) {

                
                me.tabBar.setActiveTab(card.tab);
                Ext.resumeLayouts(true);

                
                if (previous !== card) {
                    me.fireEvent('tabchange', me, card, previous);
                }
            }
            
            else {
                Ext.resumeLayouts(true);
            }
            return card;
        }
    },

    
    getActiveTab: function() {
        var me = this,
            
            result = me.getComponent(me.activeTab);

        
        if (result && me.items.indexOf(result) != -1) {
            me.activeTab = result;
        } else {
            me.activeTab = null;
        }

        return me.activeTab;
    },

    
    getTabBar: function() {
        return this.tabBar;
    },

    
    onAdd: function(item, index) {
        var me = this,
            cfg = item.tabConfig || {},
            defaultConfig = {
                xtype: 'tab',
                ui: me.tabBar.ui,
                card: item,
                disabled: item.disabled,
                closable: item.closable,
                hidden: item.hidden && !item.hiddenByLayout, 
                tooltip: item.tooltip,
                tabBar: me.tabBar,
                position: me.tabPosition,
                closeText: item.closeText
            };

        cfg = Ext.applyIf(cfg, defaultConfig);

        
        item.tab = me.tabBar.insert(index, cfg);

        item.on({
            scope : me,
            enable: me.onItemEnable,
            disable: me.onItemDisable,
            beforeshow: me.onItemBeforeShow,
            iconchange: me.onItemIconChange,
            iconclschange: me.onItemIconClsChange,
            titlechange: me.onItemTitleChange
        });

        if (item.isPanel) {
            if (me.removePanelHeader) {
                if (item.rendered) {
                    if (item.header) {
                        item.header.hide();
                    }
                } else {
                    item.header = false;
                }
            }
            if (item.isPanel && me.border) {
                item.setBorder(false);
            }
        }
    },

    
    onItemEnable: function(item){
        item.tab.enable();
    },

    
    onItemDisable: function(item){
        item.tab.disable();
    },

    
    onItemBeforeShow: function(item) {
        if (item !== this.activeTab) {
            this.setActiveTab(item);
            return false;
        }
    },

    
    onItemIconChange: function(item, newIcon) {
        item.tab.setIcon(newIcon);
    },
    
    
    onItemIconClsChange: function(item, newIconCls) {
        item.tab.setIconCls(newIconCls);
    },

    
    onItemTitleChange: function(item, newTitle) {
        item.tab.setText(newTitle);
    },

    
    doRemove: function(item, autoDestroy) {
        var me = this,
            toActivate;

        
        if (me.destroying || me.items.getCount() == 1) {
            me.activeTab = null;
        }

        
        
        else if ((toActivate = me.tabBar.items.indexOf(me.tabBar.findNextActivatable(item.tab))) !== -1) {
             me.setActiveTab(toActivate);
        }
        this.callParent(arguments);

        
        delete item.tab.card;
        delete item.tab;
    },

    
    onRemove: function(item, destroying) {
        var me = this;

        item.un({
            scope : me,
            enable: me.onItemEnable,
            disable: me.onItemDisable,
            beforeshow: me.onItemBeforeShow
        });
        if (!me.destroying && item.tab.ownerCt === me.tabBar) {
            me.tabBar.remove(item.tab);
        }
    }
});


Ext.define('Ext.toolbar.Spacer', {
    extend:  Ext.Component ,
    alias: 'widget.tbspacer',
    alternateClassName: 'Ext.Toolbar.Spacer',
    baseCls: Ext.baseCSSPrefix + 'toolbar-spacer',
    focusable: false
});


Ext.define('Ext.tree.Panel', {
    extend:  Ext.panel.Table ,
    alias: 'widget.treepanel',
    alternateClassName: ['Ext.tree.TreePanel', 'Ext.TreePanel'],
                                                                                                    
    viewType: 'treeview',
    selType: 'treemodel',

    treeCls: Ext.baseCSSPrefix + 'tree-panel',

    deferRowRender: false,

    
    rowLines: false,

    
    lines: true,

    
    useArrows: false,

    
    singleExpand: false,

    ddConfig: {
        enableDrag: true,
        enableDrop: true
    },

    

    
    rootVisible: true,

    
    displayField: 'text',

    
    root: null,

    
    
    normalCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible', 'scroll'],
    lockedCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible'],
    
    isTree: true,

    

    
     
    
    
    arrowCls: Ext.baseCSSPrefix + 'tree-arrows',
    linesCls: Ext.baseCSSPrefix + 'tree-lines',
    noLinesCls: Ext.baseCSSPrefix + 'tree-no-lines',
    autoWidthCls: Ext.baseCSSPrefix + 'autowidth-table',

    constructor: function(config) {
        config = config || {};
        if (config.animate === undefined) {
            config.animate = Ext.isBoolean(this.animate) ? this.animate : Ext.enableFx;
        }
        this.enableAnimations = config.animate;
        delete config.animate;

        this.callParent([config]);
    },

    initComponent: function() {
        var me = this,
            cls = [me.treeCls],
            store = me.store,
            view;

        if (me.useArrows) {
            cls.push(me.arrowCls);
            me.lines = false;
        }

        if (me.lines) {
            cls.push(me.linesCls);
        } else if (!me.useArrows) {
            cls.push(me.noLinesCls);
        }

        if (Ext.isString(store)) {
            store = me.store = Ext.StoreMgr.lookup(store);
        } else if (!store || Ext.isObject(store) && !store.isStore) {
            store = me.store = new Ext.data.TreeStore(Ext.apply({
                root: me.root,
                fields: me.fields,
                model: me.model,
                folderSort: me.folderSort
            }, store));
        } else if (me.root) {
            store = me.store = Ext.data.StoreManager.lookup(store);
            store.setRootNode(me.root);
            if (me.folderSort !== undefined) {
                store.folderSort = me.folderSort;
                store.sort();
            }
        }

        
        
        
        

        me.viewConfig = Ext.apply({
            rootVisible: me.rootVisible,
            animate: me.enableAnimations,
            singleExpand: me.singleExpand,
            node: store.getRootNode(),
            hideHeaders: me.hideHeaders
        }, me.viewConfig);

        
        if (!me.columns) {
            if (me.initialConfig.hideHeaders === undefined) {
                me.hideHeaders = true;
            }
            me.addCls(me.autoWidthCls);
            me.columns = [{
                xtype    : 'treecolumn',
                text     : 'Name',
                width    : Ext.isIE6 ? '100%' : 10000, 
                dataIndex: me.displayField         
            }];
        }

        if (me.cls) {
            cls.push(me.cls);
        }
        me.cls = cls.join(' ');

        me.callParent();

        
        
        me.selModel.treeStore = me.store;

        view = me.getView();

        
        
        me.relayEvents(view, [
            
            'checkchange',
            
            'afteritemexpand',
            
            'afteritemcollapse'
        ]);

        
        if (!view.isLockingView) {
            
            if (!view.rootVisible && !me.getRootNode()) {
                me.setRootNode({
                    expanded: true
                });
            }
        }
    },

    
    
    
    
    bindStore: function(store, initial) {
        var me = this;

        me.store = store;

        
        me.storeListeners = me.mon(store, {
            destroyable: true,
            load: me.onStoreLoad,
            rootchange: me.onRootChange,
            clear: me.onClear,
            scope: me
        });

        
        me.storeRelayers = me.relayEvents(store, [
            
            'beforeload',

            
            'load'
        ]);

        
        me.storeRelayers1 = me.mon(store, {
            destroyable: true,

            
            append: me.createRelayer('itemappend'),

            
            remove: me.createRelayer('itemremove'),

            
            move: me.createRelayer('itemmove', [0, 4]),

            
            insert: me.createRelayer('iteminsert'),

            
            beforeappend: me.createRelayer('beforeitemappend'),

            
            beforeremove: me.createRelayer('beforeitemremove'),

            
            beforemove: me.createRelayer('beforeitemmove'),

            
            beforeinsert: me.createRelayer('beforeiteminsert'),

            
            expand: me.createRelayer('itemexpand', [0, 1]),

            
            collapse: me.createRelayer('itemcollapse', [0, 1]),

            
            beforeexpand: me.createRelayer('beforeitemexpand', [0, 1]),

            
            beforecollapse: me.createRelayer('beforeitemcollapse', [0, 1])
        });

        
        store.ownerTree = me;
        
        if (!initial) {
            me.view.setRootNode(me.getRootNode());
        }
    },

    
    
    unbindStore: function() {
        var me = this,
            store = me.store;

        if (store) {
            Ext.destroy(me.storeListeners, me.storeRelayers, me.storeRelayers1);
            delete store.ownerTree;
        }
    },

    onClear: function(){
        this.view.onClear();
    },

    
    setRootNode: function() {
        return this.store.setRootNode.apply(this.store, arguments);
    },

    
    getRootNode: function() {
        return this.store.getRootNode();
    },

    onRootChange: function(root) {
        this.view.setRootNode(root);
    },

    
    getChecked: function() {
        return this.getView().getChecked();
    },

    isItemChecked: function(rec) {
        return rec.get('checked');
    },
    
    
    expandNode: function(record, deep, callback, scope) {
        return this.getView().expand(record, deep, callback, scope || this);
    },

    
    collapseNode: function(record, deep, callback, scope) {
        return this.getView().collapse(record, deep, callback, scope || this);
    },

    
    expandAll : function(callback, scope) {
        var me = this,
            root = me.getRootNode(),
            animate = me.enableAnimations;
        if (root) {
            if (!animate) {
                Ext.suspendLayouts();
            }
            root.expand(true, callback, scope || me);
            if (!animate) {
                Ext.resumeLayouts(true);
            }
        }
    },

    
    collapseAll : function(callback, scope) {
        var me = this,
            root = me.getRootNode(),
            animate = me.enableAnimations,
            view = me.getView();

        if (root) {
            if (!animate) {
                Ext.suspendLayouts();
            }
            scope = scope || me;
            if (view.rootVisible) {
                root.collapse(true, callback, scope);
            } else {
                root.collapseChildren(true, callback, scope);
            }
            if (!animate) {
                Ext.resumeLayouts(true);
            }
        }
    },

    
    expandPath: function(path, field, separator, callback, scope) {
        var me = this,
            current = me.getRootNode(),
            index = 1,
            view = me.getView(),
            keys,
            expander;

        field = field || me.getRootNode().idProperty;
        separator = separator || '/';

        if (Ext.isEmpty(path)) {
            Ext.callback(callback, scope || me, [false, null]);
            return;
        }

        keys = path.split(separator);
        if (current.get(field) != keys[1]) {
            
            Ext.callback(callback, scope || me, [false, current]);
            return;
        }

        expander = function(){
            if (++index === keys.length) {
                Ext.callback(callback, scope || me, [true, current]);
                return;
            }
            var node = current.findChild(field, keys[index]);
            if (!node) {
                Ext.callback(callback, scope || me, [false, current]);
                return;
            }
            current = node;
            current.expand(false, expander);
        };
        current.expand(false, expander);
    },

    
    selectPath: function(path, field, separator, callback, scope) {
        var me = this,
            root,
            keys,
            last;

        field = field || me.getRootNode().idProperty;
        separator = separator || '/';

        keys = path.split(separator);
        last = keys.pop();
        if (keys.length > 1) {
            me.expandPath(keys.join(separator), field, separator, function(success, node){
                var lastNode = node;
                if (success && node) {
                    node = node.findChild(field, last);
                    if (node) {
                        me.getSelectionModel().select(node);
                        Ext.callback(callback, scope || me, [true, node]);
                        return;
                    }
                }
                Ext.callback(callback, scope || me, [false, lastNode]);
            }, me);
        } else {
            root = me.getRootNode();
            if (root.getId() === last) {
                me.getSelectionModel().select(root);
                Ext.callback(callback, scope || me, [true, root]);
            } else {
                Ext.callback(callback, scope || me, [false, null]);
            }
        }
    }
});


Ext.define('Ext.view.DragZone', {
    extend:  Ext.dd.DragZone ,
    containerScroll: false,

    constructor: function(config) {
        var me = this,
            view,
            ownerCt,
            el;

        Ext.apply(me, config);

        
        
        
        
        
        if (!me.ddGroup) {
            me.ddGroup = 'view-dd-zone-' + me.view.id;
        }

        
        
        
        
        
        
        
        view = me.view;
        ownerCt = view.ownerCt;
        
        
        if (ownerCt) {
            el = ownerCt.getTargetEl().dom;
        } else {
            el = view.el.dom.parentNode;
        }
        me.callParent([el]);

        me.ddel = Ext.get(document.createElement('div'));
        me.ddel.addCls(Ext.baseCSSPrefix + 'grid-dd-wrap');
    },

    init: function(id, sGroup, config) {
        this.initTarget(id, sGroup, config);
        this.view.mon(this.view, {
            itemmousedown: this.onItemMouseDown,
            scope: this
        });
    },

    onValidDrop: function(target, e, id) {
        this.callParent();
        
        target.el.focus();
    },

    onItemMouseDown: function(view, record, item, index, e) {
        if (!this.isPreventDrag(e, record, item, index)) {
            
            
            
            if (view.focusRow) {
                view.focusRow(record);
            }
            this.handleMouseDown(e);
        }
    },

    
    isPreventDrag: function(e) {
        return false;
    },

    getDragData: function(e) {
        var view = this.view,
            item = e.getTarget(view.getItemSelector());

        if (item) {
            return {
                copy: view.copy || (view.allowCopy && e.ctrlKey),
                event: new Ext.EventObjectImpl(e),
                view: view,
                ddel: this.ddel,
                item: item,
                records: view.getSelectionModel().getSelection(),
                fromPosition: Ext.fly(item).getXY()
            };
        }
    },

    onInitDrag: function(x, y) {
        var me = this,
            data = me.dragData,
            view = data.view,
            selectionModel = view.getSelectionModel(),
            record = view.getRecord(data.item);

        
        
        if (!selectionModel.isSelected(record)) {
            selectionModel.select(record, true);
        }
        data.records = selectionModel.getSelection();

        me.ddel.update(me.getDragText());
        me.proxy.update(me.ddel.dom);
        me.onStartDrag(x, y);
        return true;
    },

    getDragText: function() {
        var count = this.dragData.records.length;
        return Ext.String.format(this.dragText, count, count == 1 ? '' : 's');
    },

    getRepairXY : function(e, data){
        return data ? data.fromPosition : false;
    }
});


Ext.define('Ext.tree.ViewDragZone', {
    extend:  Ext.view.DragZone ,

    isPreventDrag: function(e, record) {
        return (record.get('allowDrag') === false) || !!e.getTarget(this.view.expanderSelector);
    },

    getDragText: function() {
        var records = this.dragData.records,
            count = records.length,
            text = records[0].get(this.displayField),
            suffix = 's';
            
        if (count === 1 && text) {
            return text;    
        } else if (!text) {
            suffix = '';
        }
        return Ext.String.format(this.dragText, count, suffix);
    },

    afterRepair: function() {
        var me = this,
            view = me.view,
            selectedRowCls = view.selectedItemCls,
            records = me.dragData.records,
            r,
            rLen    = records.length,
            fly = Ext.fly,
            item;

        if (Ext.enableFx && me.repairHighlight) {
            
            for (r = 0; r < rLen; r++) {
                
                
                item = view.getNode(records[r]);

                
                
                fly(item.firstChild).highlight(me.repairHighlightColor, {
                    listeners: {
                        beforeanimate: function() {
                            if (view.isSelected(item)) {
                                fly(item).removeCls(selectedRowCls);
                            }
                        },
                        afteranimate: function() {
                            if (view.isSelected(item)) {
                                fly(item).addCls(selectedRowCls);
                            }
                        }
                    }
                });
            }

        }
        me.dragging = false;
    }
});


Ext.define('Ext.tree.ViewDropZone', {
    extend:  Ext.view.DropZone ,

    
    allowParentInserts: false,
 
    
    allowContainerDrops: false,

    
    appendOnly: false,

    
    expandDelay : 500,

    indicatorCls: Ext.baseCSSPrefix + 'tree-ddindicator',

    
    expandNode : function(node) {
        var view = this.view;
        this.expandProcId = false;
        if (!node.isLeaf() && !node.isExpanded()) {
            view.expand(node);
            this.expandProcId = false;
        }
    },

    
    queueExpand : function(node) {
        this.expandProcId = Ext.Function.defer(this.expandNode, this.expandDelay, this, [node]);
    },

    
    cancelExpand : function() {
        if (this.expandProcId) {
            clearTimeout(this.expandProcId);
            this.expandProcId = false;
        }
    },

    getPosition: function(e, node) {
        var view = this.view,
            record = view.getRecord(node),
            y = e.getPageY(),
            noAppend = record.isLeaf(),
            noBelow = false,
            region = Ext.fly(node).getRegion(),
            fragment;

        
        if (record.isRoot()) {
            return 'append';
        }

        
        if (this.appendOnly) {
            return noAppend ? false : 'append';
        }

        if (!this.allowParentInserts) {
            noBelow = record.hasChildNodes() && record.isExpanded();
        }

        fragment = (region.bottom - region.top) / (noAppend ? 2 : 3);
        if (y >= region.top && y < (region.top + fragment)) {
            return 'before';
        }
        else if (!noBelow && (noAppend || (y >= (region.bottom - fragment) && y <= region.bottom))) {
            return 'after';
        }
        else {
            return 'append';
        }
    },

    isValidDropPoint : function(node, position, dragZone, e, data) {
        if (!node || !data.item) {
            return false;
        }

        var view = this.view,
            targetNode = view.getRecord(node),
            draggedRecords = data.records,
            dataLength = draggedRecords.length,
            ln = draggedRecords.length,
            i, record;

        
        if (!(targetNode && position && dataLength)) {
            return false;
        }

        
        for (i = 0; i < ln; i++) {
            record = draggedRecords[i];
            if (record.isNode && record.contains(targetNode)) {
                return false;
            }
        }
        
        
        if (position === 'append' && targetNode.get('allowDrop') === false) {
            return false;
        }
        else if (position != 'append' && targetNode.parentNode.get('allowDrop') === false) {
            return false;
        }

        
        if (Ext.Array.contains(draggedRecords, targetNode)) {
             return false;
        }
        return view.fireEvent('nodedragover', targetNode, position, data, e) !== false;
    },

    onNodeOver : function(node, dragZone, e, data) {
        var position = this.getPosition(e, node),
            returnCls = this.dropNotAllowed,
            view = this.view,
            targetNode = view.getRecord(node),
            indicator = this.getIndicator(),
            indicatorY = 0;

        
        this.cancelExpand();
        if (position == 'append' && !this.expandProcId && !Ext.Array.contains(data.records, targetNode) && !targetNode.isLeaf() && !targetNode.isExpanded()) {
            this.queueExpand(targetNode);
        }
            
            
        if (this.isValidDropPoint(node, position, dragZone, e, data)) {
            this.valid = true;
            this.currentPosition = position;
            this.overRecord = targetNode;

            indicator.setWidth(Ext.fly(node).getWidth());
            indicatorY = Ext.fly(node).getY() - Ext.fly(view.el).getY() - 1;

            
            if (position == 'before') {
                returnCls = targetNode.isFirst() ? Ext.baseCSSPrefix + 'tree-drop-ok-above' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
                indicator.showAt(0, indicatorY);
                dragZone.proxy.show();
            } else if (position == 'after') {
                returnCls = targetNode.isLast() ? Ext.baseCSSPrefix + 'tree-drop-ok-below' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
                indicatorY += Ext.fly(node).getHeight();
                indicator.showAt(0, indicatorY);
                dragZone.proxy.show();
            } else {
                returnCls = Ext.baseCSSPrefix + 'tree-drop-ok-append';
                
                indicator.hide();
            }
        } else {
            this.valid = false;
        }

        this.currentCls = returnCls;
        return returnCls;
    },

    
    onNodeOut : function(n, dd, e, data){
        this.valid = false;
        this.getIndicator().hide();
    },

    onContainerOver : function(dd, e, data) {
        return e.getTarget('.' + this.indicatorCls) ? this.currentCls : this.dropNotAllowed;
    },
    
    notifyOut: function() {
        this.callParent(arguments);
        this.cancelExpand();
    },

    handleNodeDrop : function(data, targetNode, position) {
        var me = this,
            targetView = me.view,
            parentNode = targetNode ? targetNode.parentNode : targetView.panel.getRootNode(),
            Model = targetView.getStore().treeStore.model,
            records, i, len, record,
            insertionMethod, argList,
            needTargetExpand,
            transferData;

        
        if (data.copy) {
            records = data.records;
            data.records = [];
            for (i = 0, len = records.length; i < len; i++) {
                record = records[i];
                if (record.isNode) {
                    data.records.push(record.copy(undefined, true));
                } else {
                    
                    data.records.push(new Model(record.data, record.getId()));
                }
            }
        }

        
        me.cancelExpand();

        
        
        
        
        if (position == 'before') {
            insertionMethod = parentNode.insertBefore;
            argList = [null, targetNode];
            targetNode = parentNode;
        }
        else if (position == 'after') {
            if (targetNode.nextSibling) {
                insertionMethod = parentNode.insertBefore;
                argList = [null, targetNode.nextSibling];
            }
            else {
                insertionMethod = parentNode.appendChild;
                argList = [null];
            }
            targetNode = parentNode;
        }
        else {
            if (!(targetNode.isExpanded() || targetNode.isLoading())) {
                needTargetExpand = true;
            }
            insertionMethod = targetNode.appendChild;
            argList = [null];
        }
        
        
        transferData = function() {
            var color,
                n;

            
            Ext.suspendLayouts();

            targetView.getSelectionModel().clearSelections();

            
            for (i = 0, len = data.records.length; i < len; i++) {
                record = data.records[i];
                if (!record.isNode) {
                    if (record.isModel) {
                        record = new Model(record.data, record.getId());
                    } else {
                        record = new Model(record);
                    }
                    data.records[i] = record;
                }
                argList[0] = record;
                insertionMethod.apply(targetNode, argList);
            }

            
            if (me.sortOnDrop) {
                targetNode.sort(targetNode.getOwnerTree().store.generateComparator());
            }
            
            Ext.resumeLayouts(true);

            
            
            
            if (Ext.enableFx && me.dropHighlight) {
                color = me.dropHighlightColor;

                for (i = 0; i < len; i++) {
                    n = targetView.getNode(data.records[i]);
                    if (n) {
                        Ext.fly(n).highlight(color);
                    }
                }
            }
        };

        
        if (needTargetExpand) {
            targetNode.expand(false, transferData);
        }
        
        
        
        
        
        else if (targetNode.isLoading()) {
            targetNode.on({
                expand: transferData,
                delay: 1,
                single: true
            });
        }
        
        else {
            transferData();
        }
    }    
});


Ext.define('Ext.tree.plugin.TreeViewDragDrop', {
    extend:  Ext.AbstractPlugin ,
    alias: 'plugin.treeviewdragdrop',

           
                                
                               
      

    

    

    
    
    dragText : '{0} selected node{1}',
    

    
    allowParentInserts: false,

    
    allowContainerDrops: false,

    
    appendOnly: false,

    
    ddGroup : "TreeDD",
    
    
    containerScroll: false,

    

    

    

    
    expandDelay : 1000,

    
    enableDrop: true,

    
    enableDrag: true,

    
    nodeHighlightColor: 'c3daf9',

    
    nodeHighlightOnDrop: Ext.enableFx,

    
    nodeHighlightOnRepair: Ext.enableFx,
    
    
    displayField: 'text',

    init : function(view) {
        view.on('render', this.onViewRender, this, {single: true});
    },

    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },

    onViewRender : function(view) {
        var me = this,
            scrollEl;

        if (me.enableDrag) {
            if (me.containerScroll) {
                scrollEl = view.getEl();
            }
            me.dragZone = new Ext.tree.ViewDragZone({
                view: view,
                ddGroup: me.dragGroup || me.ddGroup,
                dragText: me.dragText,
                displayField: me.displayField,
                repairHighlightColor: me.nodeHighlightColor,
                repairHighlight: me.nodeHighlightOnRepair,
                scrollEl: scrollEl
            });
        }

        if (me.enableDrop) {
            me.dropZone = new Ext.tree.ViewDropZone({
                view: view,
                ddGroup: me.dropGroup || me.ddGroup,
                allowContainerDrops: me.allowContainerDrops,
                appendOnly: me.appendOnly,
                allowParentInserts: me.allowParentInserts,
                expandDelay: me.expandDelay,
                dropHighlightColor: me.nodeHighlightColor,
                dropHighlight: me.nodeHighlightOnDrop,
                sortOnDrop: me.sortOnDrop,
                containerScroll: me.containerScroll
            });
        }
    }
}, function(){
    var proto = this.prototype;
    proto.nodeHighlightOnDrop = proto.nodeHighlightOnRepair = Ext.enableFx;
});


Ext.define('Ext.util.Cookies', {
    singleton: true,
    
    
    set : function(name, value){
        var argv = arguments,
            argc = arguments.length,
            expires = (argc > 2) ? argv[2] : null,
            path = (argc > 3) ? argv[3] : '/',
            domain = (argc > 4) ? argv[4] : null,
            secure = (argc > 5) ? argv[5] : false;
            
        document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
    },

    
    get : function(name){
        var arg = name + "=",
            alen = arg.length,
            clen = document.cookie.length,
            i = 0,
            j = 0;
            
        while(i < clen){
            j = i + alen;
            if(document.cookie.substring(i, j) == arg){
                return this.getCookieVal(j);
            }
            i = document.cookie.indexOf(" ", i) + 1;
            if(i === 0){
                break;
            }
        }
        return null;
    },

    
    clear : function(name, path){
        if(this.get(name)){
            path = path || '/';
            document.cookie = name + '=' + '; expires=Thu, 01-Jan-70 00:00:01 GMT; path=' + path;
        }
    },
    
    
    getCookieVal : function(offset){
        var endstr = document.cookie.indexOf(";", offset);
        if(endstr == -1){
            endstr = document.cookie.length;
        }
        return unescape(document.cookie.substring(offset, endstr));
    }
});


 
Ext.define('Ext.util.Grouper', {

    

    extend:  Ext.util.Sorter ,

    
   
   isGrouper: true,

    
    getGroupString: function(instance) {
        return instance.get(this.property);
    }
});


Ext.define('Ext.util.History', {
    singleton: true,
    alternateClassName: 'Ext.History',
    mixins: {
        observable:  Ext.util.Observable 
    },

    
    useTopWindow: true,

    
    fieldId: Ext.baseCSSPrefix + 'history-field',
    
    iframeId: Ext.baseCSSPrefix + 'history-frame',

    constructor: function() {
        var me = this;
        me.oldIEMode = Ext.isIE7m || !Ext.isStrict && Ext.isIE8;
        me.iframe = null;
        me.hiddenField = null;
        me.ready = false;
        me.currentToken = null;
        me.mixins.observable.constructor.call(me);
    },

    getHash: function() {
        var href = window.location.href,
            i = href.indexOf("#");

        return i >= 0 ? href.substr(i + 1) : null;
    },

    setHash: function (hash) {
        var me = this,
            win = me.useTopWindow ? window.top : window;
        try {
            win.location.hash = hash;
        } catch (e) {
            
        }
    },

    doSave: function() {
        this.hiddenField.value = this.currentToken;
    },


    handleStateChange: function(token) {
        this.currentToken = token;
        this.fireEvent('change', token);
    },

    updateIFrame: function(token) {
        var html = '<html><body><div id="state">' +
                    Ext.util.Format.htmlEncode(token) +
                    '</div></body></html>',
            doc;

        try {
            doc = this.iframe.contentWindow.document;
            doc.open();
            doc.write(html);
            doc.close();
            return true;
        } catch (e) {
            return false;
        }
    },

    checkIFrame: function () {
        var me = this,
            contentWindow = me.iframe.contentWindow,
            doc, elem, oldToken, oldHash;

        if (!contentWindow || !contentWindow.document) {
            Ext.Function.defer(this.checkIFrame, 10, this);
            return;
        }

        doc = contentWindow.document;
        elem = doc.getElementById("state");
        oldToken = elem ? elem.innerText : null;
        oldHash = me.getHash();

        Ext.TaskManager.start({
            run: function () {
                var doc = contentWindow.document,
                    elem = doc.getElementById("state"),
                    newToken = elem ? elem.innerText : null,
                    newHash = me.getHash();

                if (newToken !== oldToken) {
                    oldToken = newToken;
                    me.handleStateChange(newToken);
                    me.setHash(newToken);
                    oldHash = newToken;
                    me.doSave();
                } else if (newHash !== oldHash) {
                    oldHash = newHash;
                    me.updateIFrame(newHash);
                }
            },
            interval: 50,
            scope: me
        });
        me.ready = true;
        me.fireEvent('ready', me);
    },

    startUp: function () {
        var me = this,
            hash;

        me.currentToken = me.hiddenField.value || this.getHash();

        if (me.oldIEMode) {
            me.checkIFrame();
        } else {
            hash = me.getHash();
            Ext.TaskManager.start({
                run: function () {
                    var newHash = me.getHash();
                    if (newHash !== hash) {
                        hash = newHash;
                        me.handleStateChange(hash);
                        me.doSave();
                    }
                },
                interval: 50,
                scope: me
            });
            me.ready = true;
            me.fireEvent('ready', me);
        }

    },

    
    init: function (onReady, scope) {
        var me = this,
            DomHelper = Ext.DomHelper;

        if (me.ready) {
            Ext.callback(onReady, scope, [me]);
            return;
        }

        if (!Ext.isReady) {
            Ext.onReady(function() {
                me.init(onReady, scope);
            });
            return;
        }

        
        me.hiddenField = Ext.getDom(me.fieldId);
        if (!me.hiddenField) {
            me.hiddenField = Ext.getBody().createChild({
                id: Ext.id(),
                tag: 'form',
                cls: Ext.baseCSSPrefix + 'hide-display',
                children: [{
                    tag: 'input',
                    type: 'hidden',
                    id: me.fieldId
                }]
            }, false, true).firstChild;
        }

        if (me.oldIEMode) {
            me.iframe = Ext.getDom(me.iframeId);
            if (!me.iframe) {
                me.iframe = DomHelper.append(me.hiddenField.parentNode, {
                    tag: 'iframe',
                    id: me.iframeId,
                    src: Ext.SSL_SECURE_URL
                });
            }
        }

        me.addEvents(
            
            'ready',
            
            'change'
        );

        if (onReady) {
            me.on('ready', onReady, scope, {single: true});
        }
        me.startUp();
    },

    
    add: function (token, preventDup) {
        var me = this;

        if (preventDup !== false) {
            if (me.getToken() === token) {
                return true;
            }
        }

        if (me.oldIEMode) {
            return me.updateIFrame(token);
        } else {
            me.setHash(token);
            return true;
        }
    },

    
    back: function() {
        window.history.go(-1);
    },

    
    forward: function(){
        window.history.go(1);
    },

    
    getToken: function() {
        return this.ready ? this.currentToken : this.getHash();
    }
});
/*
This file is part of Ext JS 4.2

Copyright (c) 2011-2013 Sencha Inc

Contact:  http://www.sencha.com/contact

GNU General Public License Usage
This file may be used under the terms of the GNU General Public License version 3.0 as
published by the Free Software Foundation and appearing in the file LICENSE included in the
packaging of this file.

Please review the following information to ensure the GNU General Public License version 3.0
requirements will be met: http://www.gnu.org/copyleft/gpl.html.

If you are unsure which license is appropriate for your use, please contact the sales department
at http://www.sencha.com/contact.

Build date: 2013-05-16 14:36:50 (f9be68accb407158ba2b1be2c226a6ce1f649314)
*/
/**
 * Italian translation
 * 28 Maggio 2012   updated by Fabio De Paolis (many changes, update to 4.1.0)
 * 21 Dicembre 2007 updated by Federico Grilli
 * 04 Ottobre 2007  updated by eric_void
 */
Ext.onReady(function() {

    if (Ext.Date) {
        Ext.Date.monthNames = ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"];

        Ext.Date.getShortMonthName = function(month) {
            return Ext.Date.monthNames[month].substring(0, 3);
        };

        Ext.Date.monthNumbers = {
            Gen: 0,
            Feb: 1,
            Mar: 2,
            Apr: 3,
            Mag: 4,
            Giu: 5,
            Lug: 6,
            Ago: 7,
            Set: 8,
            Ott: 9,
            Nov: 10,
            Dic: 11
        };

        Ext.Date.getMonthNumber = function(name) {
            return Ext.Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
        };

        Ext.Date.dayNames = ["Domenica", "Lunedi", "Martedi", "Mercoledi", "Giovedi", "Venerdi", "Sabato"];

        Ext.Date.getShortDayName = function(day) {
            return Ext.Date.dayNames[day].substring(0, 3);
        };
    }

    if (Ext.util && Ext.util.Format) {
        Ext.apply(Ext.util.Format, {
            thousandSeparator: '.',
            decimalSeparator: ',',
            currencySign: '\u20ac',  // Euro
            dateFormat: 'd/m/Y'
        });
    }
});

Ext.define("Ext.locale.it.view.View", {
    override: "Ext.view.View",
    emptyText: ""
});

Ext.define("Ext.locale.it.grid.plugin.DragDrop", {
    override: "Ext.grid.plugin.DragDrop",
    dragText: "{0} Righe selezionate"
});

Ext.define("Ext.locale.it.tab.Tab", {
    override: "Ext.tab.Tab",
    closeText: "Chiudi scheda"
});

Ext.define("Ext.locale.it.form.Basic", {
    override: "Ext.form.Basic",
    waitTitle: "Attendere..."
});

Ext.define("Ext.locale.it.form.field.Base", {
    override: "Ext.form.field.Base",
//        invalidText: "The value in this field is invalid"
    invalidText: "Valore non valido"
});

// changing the msg text below will affect the LoadMask
Ext.define("Ext.locale.it.view.AbstractView", {
    override: "Ext.view.AbstractView",
    loadingText: "Caricamento..."
});

Ext.define("Ext.locale.it.picker.Date", {
    override: "Ext.picker.Date",
    todayText: "Oggi",
    minText: "Data precedente alla data minima",
    maxText: "Data successiva alla data massima",
    disabledDaysText: "",
    disabledDatesText: "",
    nextText: 'Mese successivo (Control+Destra)',
    prevText: 'Mese precedente (Control+Sinistra)',
    monthYearText: 'Scegli un mese (Control+Sopra/Sotto per cambiare anno)',
    todayTip: "{0} (Barra spaziatrice)",
    format: "d/m/Y",
    startDay: 1
});

Ext.define("Ext.locale.it.picker.Month", {
    override: "Ext.picker.Month",
    okText: "&#160;OK&#160;",
    cancelText: "Annulla"
});

Ext.define("Ext.locale.it.toolbar.Paging", {
    override: "Ext.PagingToolbar",
    beforePageText: "Pagina",
    afterPageText: "di {0}",
    firstText: "Prima pagina",
    prevText: "Pagina precedente",
    nextText: "Pagina successiva",
    lastText: "Ultima pagina",
    refreshText: "Aggiorna",
    displayMsg: "Mostrati {0} - {1} di {2}",
    emptyMsg: 'Non ci sono dati da mostrare'
});

Ext.define("Ext.locale.it.form.field.Text", {
    override: "Ext.form.field.Text",
    minLengthText: "La lunghezza minima \u00E8 {0}",
    maxLengthText: "La lunghezza massima \u00E8 {0}",
    blankText: "Campo obbligatorio",
    regexText: "",
    emptyText: null
});

Ext.define("Ext.locale.it.form.field.Number", {
    override: "Ext.form.field.Number",
    decimalSeparator: ",",
    decimalPrecision: 2,
    minText: "Il valore minimo \u00E8 {0}",
    maxText: "Il valore massimo \u00E8 {0}",
    nanText: "{0} non \u00E8 un valore numerico valido"
});

Ext.define("Ext.locale.it.form.field.Date", {
    override: "Ext.form.field.Date",
    disabledDaysText: "Disabilitato",
    disabledDatesText: "Disabilitato",
    minText: "La data deve essere maggiore o uguale a {0}",
    maxText: "La data deve essere minore o uguale a {0}",
    invalidText: "{0} non \u00E8 una data valida. Deve essere nel formato {1}",
    format: "d/m/Y",
//        altFormats: "d/m/Y|d-m-y|d-m-Y|d/m|d-m|dm|dmy|dmY|d|Y-m-d"
    altFormats: "d-m-y|d-m-Y|d/m|d-m|dm|dmy|dmY|d|Y-m-d"
});

Ext.define("Ext.locale.it.form.field.ComboBox", {
    override: "Ext.form.field.ComboBox",
    valueNotFoundText: undefined
}, function() {
    Ext.apply(Ext.form.field.ComboBox.prototype.defaultListConfig, {
        loadingText: "Caricamento..."
    });
});

Ext.define("Ext.locale.it.form.field.VTypes", {
    override: "Ext.form.field.VTypes",
    emailText: 'Il campo deve essere un indirizzo e-mail nel formato "utente@esempio.com"',
    urlText: 'Il campo deve essere un indirizzo web nel formato "http:/' + '/www.esempio.com"',
    alphaText: 'Il campo deve contenere solo lettere e _',
    alphanumText: 'Il campo deve contenere solo lettere, numeri e _'
});

Ext.define("Ext.locale.it.form.field.HtmlEditor", {
    override: "Ext.form.field.HtmlEditor",
    createLinkText: 'Inserire un URL per il link:'
}, function() {
    Ext.apply(Ext.form.field.HtmlEditor.prototype, {
        buttonTips: {
            bold: {
                title: 'Grassetto (Ctrl+B)',
                text: 'Rende il testo selezionato in grassetto.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            italic: {
                title: 'Corsivo (Ctrl+I)',
                text: 'Rende il testo selezionato in corsivo.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            underline: {
                title: 'Sottolinea (Ctrl+U)',
                text: 'Sottolinea il testo selezionato.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            increasefontsize: {
                title: 'Ingrandisci testo',
                text: 'Aumenta la dimensione del carattere.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            decreasefontsize: {
                title: 'Rimpicciolisci testo',
                text: 'Diminuisce la dimensione del carattere.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            backcolor: {
                title: 'Colore evidenziatore testo',
                text: 'Modifica il colore di sfondo del testo selezionato.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            forecolor: {
                title: 'Colore carattere',
                text: 'Modifica il colore del testo selezionato.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            justifyleft: {
                title: 'Allinea a sinistra',
                text: 'Allinea il testo a sinistra.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            justifycenter: {
                title: 'Centra',
                text: 'Centra il testo.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            justifyright: {
                title: 'Allinea a destra',
                text: 'Allinea il testo a destra.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            insertunorderedlist: {
                title: 'Elenco puntato',
                text: 'Elenco puntato.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            insertorderedlist: {
                title: 'Elenco numerato',
                text: 'Elenco numerato.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            createlink: {
                title: 'Collegamento',
                text: 'Trasforma il testo selezionato in un collegamanto.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            },
            sourceedit: {
                title: 'Sorgente',
                text: 'Passa alla modalit\u00E0 editing del sorgente.',
                cls: Ext.baseCSSPrefix + 'html-editor-tip'
            }
        }
    });
});

Ext.define("Ext.locale.it.grid.header.Container", {
    override: "Ext.grid.header.Container",
    sortAscText: "Ordinamento crescente",
    sortDescText: "Ordinamento decrescente",
    lockText: "Blocca colonna",
    unlockText: "Sblocca colonna",
    columnsText: "Colonne"
});

Ext.define("Ext.locale.it.grid.GroupingFeature", {
    override: "Ext.grid.GroupingFeature",
    emptyGroupText: '(Nessun dato)',
    groupByText: 'Raggruppa per questo campo',
    showGroupsText: 'Mostra nei gruppi'
});

Ext.define("Ext.locale.it.grid.PropertyColumnModel", {
    override: "Ext.grid.PropertyColumnModel",
    nameText: "Name",
    valueText: "Value",
    dateFormat: "j/m/Y",
    trueText: "true",
    falseText: "false"
});

Ext.define("Ext.locale.it.grid.BooleanColumn", {
    override: "Ext.grid.BooleanColumn",
    trueText: "vero",
    falseText: "falso",
    undefinedText: '&#160;'
});

Ext.define("Ext.locale.it.grid.NumberColumn", {
    override: "Ext.grid.NumberColumn",
    format: '0.000,00'
});

Ext.define("Ext.locale.it.grid.DateColumn", {
    override: "Ext.grid.DateColumn",
    format: 'd/m/Y'
});

Ext.define("Ext.locale.it.form.field.Time", {
    override: "Ext.form.field.Time",
    minText: "L'Ora deve essere maggiore o uguale a {0}",
    maxText: "L'Ora deve essere mainore o uguale a {0}",
    invalidText: "{0} non \u00E8 un Orario valido",
//        format: "g:i A",
    format: "H:i"
//        altFormats: "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H"
});

Ext.define("Ext.locale.it.form.CheckboxGroup", {
    override: "Ext.form.CheckboxGroup",
    blankText: "Devi selezionare almeno un elemento nel gruppo"
});

Ext.define("Ext.locale.it.form.RadioGroup", {
    override: "Ext.form.RadioGroup",
    blankText: "Devi selezionare un elemento nel gruppo"
});

Ext.define("Ext.locale.it.window.MessageBox", {
    override: "Ext.window.MessageBox",
    buttonText: {
        ok: "OK",
        cancel: "Annulla",
        yes: "Si",
        no: "No"
    }    
});

// This is needed until we can refactor all of the locales into individual files
Ext.define("Ext.locale.it.Component", {	
    override: "Ext.Component"
});

// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
// This work is free. You can redistribute it and/or modify it
// under the terms of the WTFPL, Version 2
// For more information see LICENSE.txt or http://www.wtfpl.net/
//
// For more information, the home page:
// http://pieroxy.net/blog/pages/lz-string/testing.html
//
// LZ-based compression algorithm, version 1.3.3
var LZString = {
  
  
  // private property
  _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
  _f : String.fromCharCode,
  
  compressToBase64 : function (input) {
    if (input == null) return "";
    var output = "";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;
    
    input = LZString.compress(input);
    
    while (i < input.length*2) {
      
      if (i%2==0) {
        chr1 = input.charCodeAt(i/2) >> 8;
        chr2 = input.charCodeAt(i/2) & 255;
        if (i/2+1 < input.length) 
          chr3 = input.charCodeAt(i/2+1) >> 8;
        else 
          chr3 = NaN;
      } else {
        chr1 = input.charCodeAt((i-1)/2) & 255;
        if ((i+1)/2 < input.length) {
          chr2 = input.charCodeAt((i+1)/2) >> 8;
          chr3 = input.charCodeAt((i+1)/2) & 255;
        } else 
          chr2=chr3=NaN;
      }
      i+=3;
      
      enc1 = chr1 >> 2;
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      enc4 = chr3 & 63;
      
      if (isNaN(chr2)) {
        enc3 = enc4 = 64;
      } else if (isNaN(chr3)) {
        enc4 = 64;
      }
      
      output = output +
        LZString._keyStr.charAt(enc1) + LZString._keyStr.charAt(enc2) +
          LZString._keyStr.charAt(enc3) + LZString._keyStr.charAt(enc4);
      
    }
    
    return output;
  },
  
  decompressFromBase64 : function (input) {
    if (input == null) return "";
    var output = "",
        ol = 0, 
        output_,
        chr1, chr2, chr3,
        enc1, enc2, enc3, enc4,
        i = 0, f=LZString._f;
    
    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    
    while (i < input.length) {
      
      enc1 = LZString._keyStr.indexOf(input.charAt(i++));
      enc2 = LZString._keyStr.indexOf(input.charAt(i++));
      enc3 = LZString._keyStr.indexOf(input.charAt(i++));
      enc4 = LZString._keyStr.indexOf(input.charAt(i++));
      
      chr1 = (enc1 << 2) | (enc2 >> 4);
      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
      chr3 = ((enc3 & 3) << 6) | enc4;
      
      if (ol%2==0) {
        output_ = chr1 << 8;
        
        if (enc3 != 64) {
          output += f(output_ | chr2);
        }
        if (enc4 != 64) {
          output_ = chr3 << 8;
        }
      } else {
        output = output + f(output_ | chr1);
        
        if (enc3 != 64) {
          output_ = chr2 << 8;
        }
        if (enc4 != 64) {
          output += f(output_ | chr3);
        }
      }
      ol+=3;
    }
    
    return LZString.decompress(output);
    
  },

  compressToUTF16 : function (input) {
    if (input == null) return "";
    var output = "",
        i,c,
        current,
        status = 0,
        f = LZString._f;
    
    input = LZString.compress(input);
    
    for (i=0 ; i<input.length ; i++) {
      c = input.charCodeAt(i);
      switch (status++) {
        case 0:
          output += f((c >> 1)+32);
          current = (c & 1) << 14;
          break;
        case 1:
          output += f((current + (c >> 2))+32);
          current = (c & 3) << 13;
          break;
        case 2:
          output += f((current + (c >> 3))+32);
          current = (c & 7) << 12;
          break;
        case 3:
          output += f((current + (c >> 4))+32);
          current = (c & 15) << 11;
          break;
        case 4:
          output += f((current + (c >> 5))+32);
          current = (c & 31) << 10;
          break;
        case 5:
          output += f((current + (c >> 6))+32);
          current = (c & 63) << 9;
          break;
        case 6:
          output += f((current + (c >> 7))+32);
          current = (c & 127) << 8;
          break;
        case 7:
          output += f((current + (c >> 8))+32);
          current = (c & 255) << 7;
          break;
        case 8:
          output += f((current + (c >> 9))+32);
          current = (c & 511) << 6;
          break;
        case 9:
          output += f((current + (c >> 10))+32);
          current = (c & 1023) << 5;
          break;
        case 10:
          output += f((current + (c >> 11))+32);
          current = (c & 2047) << 4;
          break;
        case 11:
          output += f((current + (c >> 12))+32);
          current = (c & 4095) << 3;
          break;
        case 12:
          output += f((current + (c >> 13))+32);
          current = (c & 8191) << 2;
          break;
        case 13:
          output += f((current + (c >> 14))+32);
          current = (c & 16383) << 1;
          break;
        case 14:
          output += f((current + (c >> 15))+32, (c & 32767)+32);
          status = 0;
          break;
      }
    }
    
    return output + f(current + 32);
  },
  

  decompressFromUTF16 : function (input) {
    if (input == null) return "";
    var output = "",
        current,c,
        status=0,
        i = 0,
        f = LZString._f;
    
    while (i < input.length) {
      c = input.charCodeAt(i) - 32;
      
      switch (status++) {
        case 0:
          current = c << 1;
          break;
        case 1:
          output += f(current | (c >> 14));
          current = (c&16383) << 2;
          break;
        case 2:
          output += f(current | (c >> 13));
          current = (c&8191) << 3;
          break;
        case 3:
          output += f(current | (c >> 12));
          current = (c&4095) << 4;
          break;
        case 4:
          output += f(current | (c >> 11));
          current = (c&2047) << 5;
          break;
        case 5:
          output += f(current | (c >> 10));
          current = (c&1023) << 6;
          break;
        case 6:
          output += f(current | (c >> 9));
          current = (c&511) << 7;
          break;
        case 7:
          output += f(current | (c >> 8));
          current = (c&255) << 8;
          break;
        case 8:
          output += f(current | (c >> 7));
          current = (c&127) << 9;
          break;
        case 9:
          output += f(current | (c >> 6));
          current = (c&63) << 10;
          break;
        case 10:
          output += f(current | (c >> 5));
          current = (c&31) << 11;
          break;
        case 11:
          output += f(current | (c >> 4));
          current = (c&15) << 12;
          break;
        case 12:
          output += f(current | (c >> 3));
          current = (c&7) << 13;
          break;
        case 13:
          output += f(current | (c >> 2));
          current = (c&3) << 14;
          break;
        case 14:
          output += f(current | (c >> 1));
          current = (c&1) << 15;
          break;
        case 15:
          output += f(current | c);
          status=0;
          break;
      }
      
      
      i++;
    }
    
    return LZString.decompress(output);
    //return output;
    
  },


  
  compress: function (uncompressed) {
    if (uncompressed == null) return "";
    var i, value,
        context_dictionary= {},
        context_dictionaryToCreate= {},
        context_c="",
        context_wc="",
        context_w="",
        context_enlargeIn= 2, // Compensate for the first entry which should not count
        context_dictSize= 3,
        context_numBits= 2,
        context_data_string="", 
        context_data_val=0, 
        context_data_position=0,
        ii,
        f=LZString._f;
    
    for (ii = 0; ii < uncompressed.length; ii += 1) {
      context_c = uncompressed.charAt(ii);
      if (!Object.prototype.hasOwnProperty.call(context_dictionary,context_c)) {
        context_dictionary[context_c] = context_dictSize++;
        context_dictionaryToCreate[context_c] = true;
      }
      
      context_wc = context_w + context_c;
      if (Object.prototype.hasOwnProperty.call(context_dictionary,context_wc)) {
        context_w = context_wc;
      } else {
        if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {
          if (context_w.charCodeAt(0)<256) {
            for (i=0 ; i<context_numBits ; i++) {
              context_data_val = (context_data_val << 1);
              if (context_data_position == 15) {
                context_data_position = 0;
                context_data_string += f(context_data_val);
                context_data_val = 0;
              } else {
                context_data_position++;
              }
            }
            value = context_w.charCodeAt(0);
            for (i=0 ; i<8 ; i++) {
              context_data_val = (context_data_val << 1) | (value&1);
              if (context_data_position == 15) {
                context_data_position = 0;
                context_data_string += f(context_data_val);
                context_data_val = 0;
              } else {
                context_data_position++;
              }
              value = value >> 1;
            }
          } else {
            value = 1;
            for (i=0 ; i<context_numBits ; i++) {
              context_data_val = (context_data_val << 1) | value;
              if (context_data_position == 15) {
                context_data_position = 0;
                context_data_string += f(context_data_val);
                context_data_val = 0;
              } else {
                context_data_position++;
              }
              value = 0;
            }
            value = context_w.charCodeAt(0);
            for (i=0 ; i<16 ; i++) {
              context_data_val = (context_data_val << 1) | (value&1);
              if (context_data_position == 15) {
                context_data_position = 0;
                context_data_string += f(context_data_val);
                context_data_val = 0;
              } else {
                context_data_position++;
              }
              value = value >> 1;
            }
          }
          context_enlargeIn--;
          if (context_enlargeIn == 0) {
            context_enlargeIn = Math.pow(2, context_numBits);
            context_numBits++;
          }
          delete context_dictionaryToCreate[context_w];
        } else {
          value = context_dictionary[context_w];
          for (i=0 ; i<context_numBits ; i++) {
            context_data_val = (context_data_val << 1) | (value&1);
            if (context_data_position == 15) {
              context_data_position = 0;
              context_data_string += f(context_data_val);
              context_data_val = 0;
            } else {
              context_data_position++;
            }
            value = value >> 1;
          }
          
          
        }
        context_enlargeIn--;
        if (context_enlargeIn == 0) {
          context_enlargeIn = Math.pow(2, context_numBits);
          context_numBits++;
        }
        // Add wc to the dictionary.
        context_dictionary[context_wc] = context_dictSize++;
        context_w = String(context_c);
      }
    }
    
    // Output the code for w.
    if (context_w !== "") {
      if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {
        if (context_w.charCodeAt(0)<256) {
          for (i=0 ; i<context_numBits ; i++) {
            context_data_val = (context_data_val << 1);
            if (context_data_position == 15) {
              context_data_position = 0;
              context_data_string += f(context_data_val);
              context_data_val = 0;
            } else {
              context_data_position++;
            }
          }
          value = context_w.charCodeAt(0);
          for (i=0 ; i<8 ; i++) {
            context_data_val = (context_data_val << 1) | (value&1);
            if (context_data_position == 15) {
              context_data_position = 0;
              context_data_string += f(context_data_val);
              context_data_val = 0;
            } else {
              context_data_position++;
            }
            value = value >> 1;
          }
        } else {
          value = 1;
          for (i=0 ; i<context_numBits ; i++) {
            context_data_val = (context_data_val << 1) | value;
            if (context_data_position == 15) {
              context_data_position = 0;
              context_data_string += f(context_data_val);
              context_data_val = 0;
            } else {
              context_data_position++;
            }
            value = 0;
          }
          value = context_w.charCodeAt(0);
          for (i=0 ; i<16 ; i++) {
            context_data_val = (context_data_val << 1) | (value&1);
            if (context_data_position == 15) {
              context_data_position = 0;
              context_data_string += f(context_data_val);
              context_data_val = 0;
            } else {
              context_data_position++;
            }
            value = value >> 1;
          }
        }
        context_enlargeIn--;
        if (context_enlargeIn == 0) {
          context_enlargeIn = Math.pow(2, context_numBits);
          context_numBits++;
        }
        delete context_dictionaryToCreate[context_w];
      } else {
        value = context_dictionary[context_w];
        for (i=0 ; i<context_numBits ; i++) {
          context_data_val = (context_data_val << 1) | (value&1);
          if (context_data_position == 15) {
            context_data_position = 0;
            context_data_string += f(context_data_val);
            context_data_val = 0;
          } else {
            context_data_position++;
          }
          value = value >> 1;
        }
        
        
      }
      context_enlargeIn--;
      if (context_enlargeIn == 0) {
        context_enlargeIn = Math.pow(2, context_numBits);
        context_numBits++;
      }
    }
    
    // Mark the end of the stream
    value = 2;
    for (i=0 ; i<context_numBits ; i++) {
      context_data_val = (context_data_val << 1) | (value&1);
      if (context_data_position == 15) {
        context_data_position = 0;
        context_data_string += f(context_data_val);
        context_data_val = 0;
      } else {
        context_data_position++;
      }
      value = value >> 1;
    }
    
    // Flush the last char
    while (true) {
      context_data_val = (context_data_val << 1);
      if (context_data_position == 15) {
        context_data_string += f(context_data_val);
        break;
      }
      else context_data_position++;
    }
    return context_data_string;
  },
  
  decompress: function (compressed) {
    if (compressed == null) return "";
    if (compressed == "") return null;
    var dictionary = [],
        next,
        enlargeIn = 4,
        dictSize = 4,
        numBits = 3,
        entry = "",
        result = "",
        i,
        w,
        bits, resb, maxpower, power,
        c,
        f = LZString._f,
        data = {string:compressed, val:compressed.charCodeAt(0), position:32768, index:1};
    
    for (i = 0; i < 3; i += 1) {
      dictionary[i] = i;
    }
    
    bits = 0;
    maxpower = Math.pow(2,2);
    power=1;
    while (power!=maxpower) {
      resb = data.val & data.position;
      data.position >>= 1;
      if (data.position == 0) {
        data.position = 32768;
        data.val = data.string.charCodeAt(data.index++);
      }
      bits |= (resb>0 ? 1 : 0) * power;
      power <<= 1;
    }
    
    switch (next = bits) {
      case 0: 
          bits = 0;
          maxpower = Math.pow(2,8);
          power=1;
          while (power!=maxpower) {
            resb = data.val & data.position;
            data.position >>= 1;
            if (data.position == 0) {
              data.position = 32768;
              data.val = data.string.charCodeAt(data.index++);
            }
            bits |= (resb>0 ? 1 : 0) * power;
            power <<= 1;
          }
        c = f(bits);
        break;
      case 1: 
          bits = 0;
          maxpower = Math.pow(2,16);
          power=1;
          while (power!=maxpower) {
            resb = data.val & data.position;
            data.position >>= 1;
            if (data.position == 0) {
              data.position = 32768;
              data.val = data.string.charCodeAt(data.index++);
            }
            bits |= (resb>0 ? 1 : 0) * power;
            power <<= 1;
          }
        c = f(bits);
        break;
      case 2: 
        return "";
    }
    dictionary[3] = c;
    w = result = c;
    while (true) {
      if (data.index > data.string.length) {
        return "";
      }
      
      bits = 0;
      maxpower = Math.pow(2,numBits);
      power=1;
      while (power!=maxpower) {
        resb = data.val & data.position;
        data.position >>= 1;
        if (data.position == 0) {
          data.position = 32768;
          data.val = data.string.charCodeAt(data.index++);
        }
        bits |= (resb>0 ? 1 : 0) * power;
        power <<= 1;
      }

      switch (c = bits) {
        case 0: 
          bits = 0;
          maxpower = Math.pow(2,8);
          power=1;
          while (power!=maxpower) {
            resb = data.val & data.position;
            data.position >>= 1;
            if (data.position == 0) {
              data.position = 32768;
              data.val = data.string.charCodeAt(data.index++);
            }
            bits |= (resb>0 ? 1 : 0) * power;
            power <<= 1;
          }

          dictionary[dictSize++] = f(bits);
          c = dictSize-1;
          enlargeIn--;
          break;
        case 1: 
          bits = 0;
          maxpower = Math.pow(2,16);
          power=1;
          while (power!=maxpower) {
            resb = data.val & data.position;
            data.position >>= 1;
            if (data.position == 0) {
              data.position = 32768;
              data.val = data.string.charCodeAt(data.index++);
            }
            bits |= (resb>0 ? 1 : 0) * power;
            power <<= 1;
          }
          dictionary[dictSize++] = f(bits);
          c = dictSize-1;
          enlargeIn--;
          break;
        case 2: 
          return result;
      }
      
      if (enlargeIn == 0) {
        enlargeIn = Math.pow(2, numBits);
        numBits++;
      }
      
      if (dictionary[c]) {
        entry = dictionary[c];
      } else {
        if (c === dictSize) {
          entry = w + w.charAt(0);
        } else {
          return null;
        }
      }
      result += entry;
      
      // Add w+entry[0] to the dictionary.
      dictionary[dictSize++] = w + entry.charAt(0);
      enlargeIn--;
      
      w = entry;
      
      if (enlargeIn == 0) {
        enlargeIn = Math.pow(2, numBits);
        numBits++;
      }
      
    }
  }
};

if( typeof module !== 'undefined' && module != null ) {
  module.exports = LZString
}
//     uuid.js
//
//     Copyright (c) 2010-2012 Robert Kieffer
//     MIT License - http://opensource.org/licenses/mit-license.php

(function() {
  var _global = this;

  // Unique ID creation requires a high quality random # generator.  We feature
  // detect to determine the best RNG source, normalizing to a function that
  // returns 128-bits of randomness, since that's what's usually required
  var _rng;

  // Allow for MSIE11 msCrypto
  var _crypto = _global.crypto || _global.msCrypto;

  // Node.js crypto-based RNG - http://nodejs.org/docs/v0.6.2/api/crypto.html
  //
  // Moderately fast, high quality
  if (typeof(_global.require) == 'function') {
    try {
      var _rb = _global.require('crypto').randomBytes;
      _rng = _rb && function() {return _rb(16);};
    } catch(e) {}
  }

  if (!_rng && _crypto && _crypto.getRandomValues) {
    // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
    //
    // Moderately fast, high quality
    var _rnds8 = new Uint8Array(16);
    _rng = function whatwgRNG() {
      _crypto.getRandomValues(_rnds8);
      return _rnds8;
    };
  }

  if (!_rng) {
    // Math.random()-based (RNG)
    //
    // If all else fails, use Math.random().  It's fast, but is of unspecified
    // quality.
    var  _rnds = new Array(16);
    _rng = function() {
      for (var i = 0, r; i < 16; i++) {
        if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
        _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
      }

      return _rnds;
    };
  }

  // Buffer class to use
  var BufferClass = typeof(_global.Buffer) == 'function' ? _global.Buffer : Array;

  // Maps for number <-> hex string conversion
  var _byteToHex = [];
  var _hexToByte = {};
  for (var i = 0; i < 256; i++) {
    _byteToHex[i] = (i + 0x100).toString(16).substr(1);
    _hexToByte[_byteToHex[i]] = i;
  }

  // **`parse()` - Parse a UUID into it's component bytes**
  function parse(s, buf, offset) {
    var i = (buf && offset) || 0, ii = 0;

    buf = buf || [];
    s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
      if (ii < 16) { // Don't overflow!
        buf[i + ii++] = _hexToByte[oct];
      }
    });

    // Zero out remaining bytes if string was short
    while (ii < 16) {
      buf[i + ii++] = 0;
    }

    return buf;
  }

  // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
  function unparse(buf, offset) {
    var i = offset || 0, bth = _byteToHex;
    return  bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]];
  }

  // **`v1()` - Generate time-based UUID**
  //
  // Inspired by https://github.com/LiosK/UUID.js
  // and http://docs.python.org/library/uuid.html

  // random #'s we need to init node and clockseq
  var _seedBytes = _rng();

  // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
  var _nodeId = [
    _seedBytes[0] | 0x01,
    _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
  ];

  // Per 4.2.2, randomize (14 bit) clockseq
  var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;

  // Previous uuid creation time
  var _lastMSecs = 0, _lastNSecs = 0;

  // See https://github.com/broofa/node-uuid for API details
  function v1(options, buf, offset) {
    var i = buf && offset || 0;
    var b = buf || [];

    options = options || {};

    var clockseq = options.clockseq != null ? options.clockseq : _clockseq;

    // UUID timestamps are 100 nano-second units since the Gregorian epoch,
    // (1582-10-15 00:00).  JSNumbers aren't precise enough for this, so
    // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
    // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
    var msecs = options.msecs != null ? options.msecs : new Date().getTime();

    // Per 4.2.1.2, use count of uuid's generated during the current clock
    // cycle to simulate higher resolution clock
    var nsecs = options.nsecs != null ? options.nsecs : _lastNSecs + 1;

    // Time since last uuid creation (in msecs)
    var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;

    // Per 4.2.1.2, Bump clockseq on clock regression
    if (dt < 0 && options.clockseq == null) {
      clockseq = clockseq + 1 & 0x3fff;
    }

    // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
    // time interval
    if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
      nsecs = 0;
    }

    // Per 4.2.1.2 Throw error if too many uuids are requested
    if (nsecs >= 10000) {
      throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
    }

    _lastMSecs = msecs;
    _lastNSecs = nsecs;
    _clockseq = clockseq;

    // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
    msecs += 12219292800000;

    // `time_low`
    var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
    b[i++] = tl >>> 24 & 0xff;
    b[i++] = tl >>> 16 & 0xff;
    b[i++] = tl >>> 8 & 0xff;
    b[i++] = tl & 0xff;

    // `time_mid`
    var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
    b[i++] = tmh >>> 8 & 0xff;
    b[i++] = tmh & 0xff;

    // `time_high_and_version`
    b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
    b[i++] = tmh >>> 16 & 0xff;

    // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
    b[i++] = clockseq >>> 8 | 0x80;

    // `clock_seq_low`
    b[i++] = clockseq & 0xff;

    // `node`
    var node = options.node || _nodeId;
    for (var n = 0; n < 6; n++) {
      b[i + n] = node[n];
    }

    return buf ? buf : unparse(b);
  }

  // **`v4()` - Generate random UUID**

  // See https://github.com/broofa/node-uuid for API details
  function v4(options, buf, offset) {
    // Deprecated - 'format' argument, as supported in v1.2
    var i = buf && offset || 0;

    if (typeof(options) == 'string') {
      buf = options == 'binary' ? new BufferClass(16) : null;
      options = null;
    }
    options = options || {};

    var rnds = options.random || (options.rng || _rng)();

    // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
    rnds[6] = (rnds[6] & 0x0f) | 0x40;
    rnds[8] = (rnds[8] & 0x3f) | 0x80;

    // Copy bytes to buffer, if provided
    if (buf) {
      for (var ii = 0; ii < 16; ii++) {
        buf[i + ii] = rnds[ii];
      }
    }

    return buf || unparse(rnds);
  }

  // Export public API
  var uuid = v4;
  uuid.v1 = v1;
  uuid.v4 = v4;
  uuid.parse = parse;
  uuid.unparse = unparse;
  uuid.BufferClass = BufferClass;

  if (typeof(module) != 'undefined' && module.exports) {
    // Publish as node.js module
    module.exports = uuid;
  } else  if (typeof define === 'function' && define.amd) {
    // Publish as AMD module
    define(function() {return uuid;});
 

  } else {
    // Publish as global (in browsers)
    var _previousRoot = _global.uuid;

    // **`noConflict()` - (browser only) to reset global 'uuid' var**
    uuid.noConflict = function() {
      _global.uuid = _previousRoot;
      return uuid;
    };

    _global.uuid = uuid;
  }
}).call(this);
/*! modernizr 3.2.0 (Custom Build) | MIT *
 * http://modernizr.com/download/?-cssfilters-webgl-prefixed-prefixedcss-prefixedcssvalue-testallprops-testprop-teststyles !*/
!function(e,n,t){function r(e,n){return typeof e===n}function o(){var e,n,t,o,s,i,a;for(var f in C)if(C.hasOwnProperty(f)){if(e=[],n=C[f],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;t<n.options.aliases.length;t++)e.push(n.options.aliases[t].toLowerCase());for(o=r(n.fn,"function")?n.fn():n.fn,s=0;s<e.length;s++)i=e[s],a=i.split("."),1===a.length?Modernizr[a[0]]=o:(!Modernizr[a[0]]||Modernizr[a[0]]instanceof Boolean||(Modernizr[a[0]]=new Boolean(Modernizr[a[0]])),Modernizr[a[0]][a[1]]=o),h.push((o?"":"no-")+a.join("-"))}}function s(e){var n=x.className,t=Modernizr._config.classPrefix||"";if(w&&(n=n.baseVal),Modernizr._config.enableJSClass){var r=new RegExp("(^|\\s)"+t+"no-js(\\s|$)");n=n.replace(r,"$1"+t+"js$2")}Modernizr._config.enableClasses&&(n+=" "+t+e.join(" "+t),w?x.className.baseVal=n:x.className=n)}function i(e){return e.replace(/([a-z])-([a-z])/g,function(e,n,t){return n+t.toUpperCase()}).replace(/^-/,"")}function a(){return"function"!=typeof n.createElement?n.createElement(arguments[0]):w?n.createElementNS.call(n,"http://www.w3.org/2000/svg",arguments[0]):n.createElement.apply(n,arguments)}function f(e){return e.replace(/([A-Z])/g,function(e,n){return"-"+n.toLowerCase()}).replace(/^ms-/,"-ms-")}function l(e,n){return!!~(""+e).indexOf(n)}function u(){var e=n.body;return e||(e=a(w?"svg":"body"),e.fake=!0),e}function p(e,t,r,o){var s,i,f,l,p="modernizr",d=a("div"),c=u();if(parseInt(r,10))for(;r--;)f=a("div"),f.id=o?o[r]:p+(r+1),d.appendChild(f);return s=a("style"),s.type="text/css",s.id="s"+p,(c.fake?c:d).appendChild(s),c.appendChild(d),s.styleSheet?s.styleSheet.cssText=e:s.appendChild(n.createTextNode(e)),d.id=p,c.fake&&(c.style.background="",c.style.overflow="hidden",l=x.style.overflow,x.style.overflow="hidden",x.appendChild(c)),i=t(d,e),c.fake?(c.parentNode.removeChild(c),x.style.overflow=l,x.offsetHeight):d.parentNode.removeChild(d),!!i}function d(n,r){var o=n.length;if("CSS"in e&&"supports"in e.CSS){for(;o--;)if(e.CSS.supports(f(n[o]),r))return!0;return!1}if("CSSSupportsRule"in e){for(var s=[];o--;)s.push("("+f(n[o])+":"+r+")");return s=s.join(" or "),p("@supports ("+s+") { #modernizr { position: absolute; } }",function(e){return"absolute"==getComputedStyle(e,null).position})}return t}function c(e,n){return function(){return e.apply(n,arguments)}}function v(e,n,t){var o;for(var s in e)if(e[s]in n)return t===!1?e[s]:(o=n[e[s]],r(o,"function")?c(o,t||n):o);return!1}function m(e,n,o,s){function f(){p&&(delete k.style,delete k.modElem)}if(s=r(s,"undefined")?!1:s,!r(o,"undefined")){var u=d(e,o);if(!r(u,"undefined"))return u}for(var p,c,v,m,y,g=["modernizr","tspan"];!k.style;)p=!0,k.modElem=a(g.shift()),k.style=k.modElem.style;for(v=e.length,c=0;v>c;c++)if(m=e[c],y=k.style[m],l(m,"-")&&(m=i(m)),k.style[m]!==t){if(s||r(o,"undefined"))return f(),"pfx"==n?m:!0;try{k.style[m]=o}catch(h){}if(k.style[m]!=y)return f(),"pfx"==n?m:!0}return f(),!1}function y(e,n,t,o,s){var i=e.charAt(0).toUpperCase()+e.slice(1),a=(e+" "+T.join(i+" ")+i).split(" ");return r(n,"string")||r(n,"undefined")?m(a,n,o,s):(a=(e+" "+N.join(i+" ")+i).split(" "),v(a,n,t))}function g(e,n,r){return y(e,t,t,n,r)}var h=[],C=[],S={_version:"3.2.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,n){var t=this;setTimeout(function(){n(t[e])},0)},addTest:function(e,n,t){C.push({name:e,fn:n,options:t})},addAsyncTest:function(e){C.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=S,Modernizr=new Modernizr;var x=n.documentElement,w="svg"===x.nodeName.toLowerCase();Modernizr.addTest("webgl",function(){var n=a("canvas"),t="probablySupportsContext"in n?"probablySupportsContext":"supportsContext";return t in n?n[t]("webgl")||n[t]("experimental-webgl"):"WebGLRenderingContext"in e});var b=S._config.usePrefixes?" -webkit- -moz- -o- -ms- ".split(" "):[];S._prefixes=b;var _="CSS"in e&&"supports"in e.CSS,P="supportsCSS"in e;Modernizr.addTest("supports",_||P);var E=(S.testStyles=p,"Moz O ms Webkit"),T=S._config.usePrefixes?E.split(" "):[];S._cssomPrefixes=T;var z=function(n){var r,o=b.length,s=e.CSSRule;if("undefined"==typeof s)return t;if(!n)return!1;if(n=n.replace(/^@/,""),r=n.replace(/-/g,"_").toUpperCase()+"_RULE",r in s)return"@"+n;for(var i=0;o>i;i++){var a=b[i],f=a.toUpperCase()+"_"+r;if(f in s)return"@-"+a.toLowerCase()+"-"+n}return!1};S.atRule=z;var N=S._config.usePrefixes?E.toLowerCase().split(" "):[];S._domPrefixes=N;var j=function(e,n){var t=!1,r=a("div"),o=r.style;if(e in o){var s=N.length;for(o[e]=n,t=o[e];s--&&!t;)o[e]="-"+N[s]+"-"+n,t=o[e]}return""===t&&(t=!1),t};S.prefixedCSSValue=j;var L={elem:a("modernizr")};Modernizr._q.push(function(){delete L.elem});var k={style:L.elem.style};Modernizr._q.unshift(function(){delete k.style});S.testProp=function(e,n,r){return m([e],t,n,r)};S.testAllProps=y;var R=S.prefixed=function(e,n,t){return 0===e.indexOf("@")?z(e):(-1!=e.indexOf("-")&&(e=i(e)),n?y(e,n,t):y(e,"pfx"))};S.prefixedCSS=function(e){var n=R(e);return n&&f(n)};S.testAllProps=g,Modernizr.addTest("cssfilters",function(){if(Modernizr.supports)return g("filter","blur(2px)");var e=a("a");return e.style.cssText=b.join("filter:blur(2px); "),!!e.style.length&&(n.documentMode===t||n.documentMode>9)}),o(),s(h),delete S.addTest,delete S.addAsyncTest;for(var A=0;A<Modernizr._q.length;A++)Modernizr._q[A]();e.Modernizr=Modernizr}(window,document);/*

  OpenLayers.js -- OpenLayers Map Viewer Library

  Copyright (c) 2006-2013 by OpenLayers Contributors
  Published under the 2-clause BSD license.
  See http://openlayers.org/dev/license.txt for the full text of the license, and http://openlayers.org/dev/authors.txt for full list of contributors.

  Includes compressed code under the following licenses:

  (For uncompressed versions of the code used, please see the
  OpenLayers Github repository: <https://github.com/openlayers/openlayers>)

*/

/**
 * Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
 * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 */

/**
 * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
 * Copyright (c) 2006, Yahoo! Inc.
 * All rights reserved.
 * 
 * Redistribution and use of this software in source and binary forms, with or
 * without modification, are permitted provided that the following conditions
 * are met:
 * 
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * 
 * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission of Yahoo! Inc.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */
var OpenLayers={VERSION_NUMBER:"Release 2.13",singleFile:!0,_getScriptLocation:function(){for(var a=/(^|(.*?\/))(OpenLayers[^\/]*?\.js)(\?|$)/,b=document.getElementsByTagName("script"),c,d="",e=0,f=b.length;e<f;e++)if(c=b[e].getAttribute("src"))if(c=c.match(a)){d=c[1];break}return function(){return d}}(),ImgPath:""};OpenLayers.Class=function(){var a=arguments.length,b=arguments[0],c=arguments[a-1],d="function"==typeof c.initialize?c.initialize:function(){b.prototype.initialize.apply(this,arguments)};1<a?(a=[d,b].concat(Array.prototype.slice.call(arguments).slice(1,a-1),c),OpenLayers.inherit.apply(null,a)):d.prototype=c;return d};
OpenLayers.inherit=function(a,b){var c=function(){};c.prototype=b.prototype;a.prototype=new c;var d,e,c=2;for(d=arguments.length;c<d;c++)e=arguments[c],"function"===typeof e&&(e=e.prototype),OpenLayers.Util.extend(a.prototype,e)};OpenLayers.Util=OpenLayers.Util||{};OpenLayers.Util.extend=function(a,b){a=a||{};if(b){for(var c in b){var d=b[c];void 0!==d&&(a[c]=d)}"function"==typeof window.Event&&b instanceof window.Event||(!b.hasOwnProperty||!b.hasOwnProperty("toString"))||(a.toString=b.toString)}return a};OpenLayers.String={startsWith:function(a,b){return 0==a.indexOf(b)},contains:function(a,b){return-1!=a.indexOf(b)},trim:function(a){return a.replace(/^\s\s*/,"").replace(/\s\s*$/,"")},camelize:function(a){a=a.split("-");for(var b=a[0],c=1,d=a.length;c<d;c++)var e=a[c],b=b+(e.charAt(0).toUpperCase()+e.substring(1));return b},format:function(a,b,c){b||(b=window);return a.replace(OpenLayers.String.tokenRegEx,function(a,e){for(var f,g=e.split(/\.+/),h=0;h<g.length;h++){0==h&&(f=b);if(void 0===f)break;
f=f[g[h]]}"function"==typeof f&&(f=c?f.apply(null,c):f());return"undefined"==typeof f?"undefined":f})},tokenRegEx:/\$\{([\w.]+?)\}/g,numberRegEx:/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,isNumeric:function(a){return OpenLayers.String.numberRegEx.test(a)},numericIf:function(a,b){var c=a;!0===b&&(null!=a&&a.replace)&&(a=a.replace(/^\s*|\s*$/g,""));return OpenLayers.String.isNumeric(a)?parseFloat(a):c}};
OpenLayers.Number={decimalSeparator:".",thousandsSeparator:",",limitSigDigs:function(a,b){var c=0;0<b&&(c=parseFloat(a.toPrecision(b)));return c},format:function(a,b,c,d){b="undefined"!=typeof b?b:0;c="undefined"!=typeof c?c:OpenLayers.Number.thousandsSeparator;d="undefined"!=typeof d?d:OpenLayers.Number.decimalSeparator;null!=b&&(a=parseFloat(a.toFixed(b)));var e=a.toString().split(".");1==e.length&&null==b&&(b=0);a=e[0];if(c)for(var f=/(-?[0-9]+)([0-9]{3})/;f.test(a);)a=a.replace(f,"$1"+c+"$2");
0==b?b=a:(c=1<e.length?e[1]:"0",null!=b&&(c+=Array(b-c.length+1).join("0")),b=a+d+c);return b},zeroPad:function(a,b,c){for(a=a.toString(c||10);a.length<b;)a="0"+a;return a}};
OpenLayers.Function={bind:function(a,b){var c=Array.prototype.slice.apply(arguments,[2]);return function(){var d=c.concat(Array.prototype.slice.apply(arguments,[0]));return a.apply(b,d)}},bindAsEventListener:function(a,b){return function(c){return a.call(b,c||window.event)}},False:function(){return!1},True:function(){return!0},Void:function(){}};
OpenLayers.Array={filter:function(a,b,c){var d=[];if(Array.prototype.filter)d=a.filter(b,c);else{var e=a.length;if("function"!=typeof b)throw new TypeError;for(var f=0;f<e;f++)if(f in a){var g=a[f];b.call(c,g,f,a)&&d.push(g)}}return d}};OpenLayers.Bounds=OpenLayers.Class({left:null,bottom:null,right:null,top:null,centerLonLat:null,initialize:function(a,b,c,d){OpenLayers.Util.isArray(a)&&(d=a[3],c=a[2],b=a[1],a=a[0]);null!=a&&(this.left=OpenLayers.Util.toFloat(a));null!=b&&(this.bottom=OpenLayers.Util.toFloat(b));null!=c&&(this.right=OpenLayers.Util.toFloat(c));null!=d&&(this.top=OpenLayers.Util.toFloat(d))},clone:function(){return new OpenLayers.Bounds(this.left,this.bottom,this.right,this.top)},equals:function(a){var b=!1;null!=
a&&(b=this.left==a.left&&this.right==a.right&&this.top==a.top&&this.bottom==a.bottom);return b},toString:function(){return[this.left,this.bottom,this.right,this.top].join()},toArray:function(a){return!0===a?[this.bottom,this.left,this.top,this.right]:[this.left,this.bottom,this.right,this.top]},toBBOX:function(a,b){null==a&&(a=6);var c=Math.pow(10,a),d=Math.round(this.left*c)/c,e=Math.round(this.bottom*c)/c,f=Math.round(this.right*c)/c,c=Math.round(this.top*c)/c;return!0===b?e+","+d+","+c+","+f:d+
","+e+","+f+","+c},toGeometry:function(){return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(this.left,this.bottom),new OpenLayers.Geometry.Point(this.right,this.bottom),new OpenLayers.Geometry.Point(this.right,this.top),new OpenLayers.Geometry.Point(this.left,this.top)])])},getWidth:function(){return this.right-this.left},getHeight:function(){return this.top-this.bottom},getSize:function(){return new OpenLayers.Size(this.getWidth(),this.getHeight())},
getCenterPixel:function(){return new OpenLayers.Pixel((this.left+this.right)/2,(this.bottom+this.top)/2)},getCenterLonLat:function(){this.centerLonLat||(this.centerLonLat=new OpenLayers.LonLat((this.left+this.right)/2,(this.bottom+this.top)/2));return this.centerLonLat},scale:function(a,b){null==b&&(b=this.getCenterLonLat());var c,d;"OpenLayers.LonLat"==b.CLASS_NAME?(c=b.lon,d=b.lat):(c=b.x,d=b.y);return new OpenLayers.Bounds((this.left-c)*a+c,(this.bottom-d)*a+d,(this.right-c)*a+c,(this.top-d)*a+
d)},add:function(a,b){if(null==a||null==b)throw new TypeError("Bounds.add cannot receive null values");return new OpenLayers.Bounds(this.left+a,this.bottom+b,this.right+a,this.top+b)},extend:function(a){if(a)switch(a.CLASS_NAME){case "OpenLayers.LonLat":this.extendXY(a.lon,a.lat);break;case "OpenLayers.Geometry.Point":this.extendXY(a.x,a.y);break;case "OpenLayers.Bounds":this.centerLonLat=null;if(null==this.left||a.left<this.left)this.left=a.left;if(null==this.bottom||a.bottom<this.bottom)this.bottom=
a.bottom;if(null==this.right||a.right>this.right)this.right=a.right;if(null==this.top||a.top>this.top)this.top=a.top}},extendXY:function(a,b){this.centerLonLat=null;if(null==this.left||a<this.left)this.left=a;if(null==this.bottom||b<this.bottom)this.bottom=b;if(null==this.right||a>this.right)this.right=a;if(null==this.top||b>this.top)this.top=b},containsLonLat:function(a,b){"boolean"===typeof b&&(b={inclusive:b});b=b||{};var c=this.contains(a.lon,a.lat,b.inclusive),d=b.worldBounds;d&&!c&&(c=d.getWidth(),
d=Math.round((a.lon-(d.left+d.right)/2)/c),c=this.containsLonLat({lon:a.lon-d*c,lat:a.lat},{inclusive:b.inclusive}));return c},containsPixel:function(a,b){return this.contains(a.x,a.y,b)},contains:function(a,b,c){null==c&&(c=!0);if(null==a||null==b)return!1;a=OpenLayers.Util.toFloat(a);b=OpenLayers.Util.toFloat(b);var d=!1;return d=c?a>=this.left&&a<=this.right&&b>=this.bottom&&b<=this.top:a>this.left&&a<this.right&&b>this.bottom&&b<this.top},intersectsBounds:function(a,b){"boolean"===typeof b&&(b=
{inclusive:b});b=b||{};if(b.worldBounds){var c=this.wrapDateLine(b.worldBounds);a=a.wrapDateLine(b.worldBounds)}else c=this;null==b.inclusive&&(b.inclusive=!0);var d=!1,e=c.left==a.right||c.right==a.left||c.top==a.bottom||c.bottom==a.top;if(b.inclusive||!e)var d=a.top>=c.bottom&&a.top<=c.top||c.top>a.bottom&&c.top<a.top,e=a.left>=c.left&&a.left<=c.right||c.left>=a.left&&c.left<=a.right,f=a.right>=c.left&&a.right<=c.right||c.right>=a.left&&c.right<=a.right,d=(a.bottom>=c.bottom&&a.bottom<=c.top||c.bottom>=
a.bottom&&c.bottom<=a.top||d)&&(e||f);if(b.worldBounds&&!d){var g=b.worldBounds,e=g.getWidth(),f=!g.containsBounds(c),g=!g.containsBounds(a);f&&!g?(a=a.add(-e,0),d=c.intersectsBounds(a,{inclusive:b.inclusive})):g&&!f&&(c=c.add(-e,0),d=a.intersectsBounds(c,{inclusive:b.inclusive}))}return d},containsBounds:function(a,b,c){null==b&&(b=!1);null==c&&(c=!0);var d=this.contains(a.left,a.bottom,c),e=this.contains(a.right,a.bottom,c),f=this.contains(a.left,a.top,c);a=this.contains(a.right,a.top,c);return b?
d||e||f||a:d&&e&&f&&a},determineQuadrant:function(a){var b="",c=this.getCenterLonLat(),b=b+(a.lat<c.lat?"b":"t");return b+=a.lon<c.lon?"l":"r"},transform:function(a,b){this.centerLonLat=null;var c=OpenLayers.Projection.transform({x:this.left,y:this.bottom},a,b),d=OpenLayers.Projection.transform({x:this.right,y:this.bottom},a,b),e=OpenLayers.Projection.transform({x:this.left,y:this.top},a,b),f=OpenLayers.Projection.transform({x:this.right,y:this.top},a,b);this.left=Math.min(c.x,e.x);this.bottom=Math.min(c.y,
d.y);this.right=Math.max(d.x,f.x);this.top=Math.max(e.y,f.y);return this},wrapDateLine:function(a,b){b=b||{};var c=b.leftTolerance||0,d=b.rightTolerance||0,e=this.clone();if(a){for(var f=a.getWidth();e.left<a.left&&e.right-d<=a.left;)e=e.add(f,0);for(;e.left+c>=a.right&&e.right>a.right;)e=e.add(-f,0);c=e.left+c;c<a.right&&(c>a.left&&e.right-d>a.right)&&(e=e.add(-f,0))}return e},CLASS_NAME:"OpenLayers.Bounds"});
OpenLayers.Bounds.fromString=function(a,b){var c=a.split(",");return OpenLayers.Bounds.fromArray(c,b)};OpenLayers.Bounds.fromArray=function(a,b){return!0===b?new OpenLayers.Bounds(a[1],a[0],a[3],a[2]):new OpenLayers.Bounds(a[0],a[1],a[2],a[3])};OpenLayers.Bounds.fromSize=function(a){return new OpenLayers.Bounds(0,a.h,a.w,0)};OpenLayers.Bounds.oppositeQuadrant=function(a){var b;b=""+("t"==a.charAt(0)?"b":"t");return b+="l"==a.charAt(1)?"r":"l"};OpenLayers.Element={visible:function(a){return"none"!=OpenLayers.Util.getElement(a).style.display},toggle:function(){for(var a=0,b=arguments.length;a<b;a++){var c=OpenLayers.Util.getElement(arguments[a]),d=OpenLayers.Element.visible(c)?"none":"";c.style.display=d}},remove:function(a){a=OpenLayers.Util.getElement(a);a.parentNode.removeChild(a)},getHeight:function(a){a=OpenLayers.Util.getElement(a);return a.offsetHeight},hasClass:function(a,b){var c=a.className;return!!c&&RegExp("(^|\\s)"+b+"(\\s|$)").test(c)},
addClass:function(a,b){OpenLayers.Element.hasClass(a,b)||(a.className+=(a.className?" ":"")+b);return a},removeClass:function(a,b){var c=a.className;c&&(a.className=OpenLayers.String.trim(c.replace(RegExp("(^|\\s+)"+b+"(\\s+|$)")," ")));return a},toggleClass:function(a,b){OpenLayers.Element.hasClass(a,b)?OpenLayers.Element.removeClass(a,b):OpenLayers.Element.addClass(a,b);return a},getStyle:function(a,b){a=OpenLayers.Util.getElement(a);var c=null;if(a&&a.style){c=a.style[OpenLayers.String.camelize(b)];
c||(document.defaultView&&document.defaultView.getComputedStyle?c=(c=document.defaultView.getComputedStyle(a,null))?c.getPropertyValue(b):null:a.currentStyle&&(c=a.currentStyle[OpenLayers.String.camelize(b)]));var d=["left","top","right","bottom"];window.opera&&(-1!=OpenLayers.Util.indexOf(d,b)&&"static"==OpenLayers.Element.getStyle(a,"position"))&&(c="auto")}return"auto"==c?null:c}};OpenLayers.LonLat=OpenLayers.Class({lon:0,lat:0,initialize:function(a,b){OpenLayers.Util.isArray(a)&&(b=a[1],a=a[0]);this.lon=OpenLayers.Util.toFloat(a);this.lat=OpenLayers.Util.toFloat(b)},toString:function(){return"lon="+this.lon+",lat="+this.lat},toShortString:function(){return this.lon+", "+this.lat},clone:function(){return new OpenLayers.LonLat(this.lon,this.lat)},add:function(a,b){if(null==a||null==b)throw new TypeError("LonLat.add cannot receive null values");return new OpenLayers.LonLat(this.lon+
OpenLayers.Util.toFloat(a),this.lat+OpenLayers.Util.toFloat(b))},equals:function(a){var b=!1;null!=a&&(b=this.lon==a.lon&&this.lat==a.lat||isNaN(this.lon)&&isNaN(this.lat)&&isNaN(a.lon)&&isNaN(a.lat));return b},transform:function(a,b){var c=OpenLayers.Projection.transform({x:this.lon,y:this.lat},a,b);this.lon=c.x;this.lat=c.y;return this},wrapDateLine:function(a){var b=this.clone();if(a){for(;b.lon<a.left;)b.lon+=a.getWidth();for(;b.lon>a.right;)b.lon-=a.getWidth()}return b},CLASS_NAME:"OpenLayers.LonLat"});
OpenLayers.LonLat.fromString=function(a){a=a.split(",");return new OpenLayers.LonLat(a[0],a[1])};OpenLayers.LonLat.fromArray=function(a){var b=OpenLayers.Util.isArray(a);return new OpenLayers.LonLat(b&&a[0],b&&a[1])};OpenLayers.Pixel=OpenLayers.Class({x:0,y:0,initialize:function(a,b){this.x=parseFloat(a);this.y=parseFloat(b)},toString:function(){return"x="+this.x+",y="+this.y},clone:function(){return new OpenLayers.Pixel(this.x,this.y)},equals:function(a){var b=!1;null!=a&&(b=this.x==a.x&&this.y==a.y||isNaN(this.x)&&isNaN(this.y)&&isNaN(a.x)&&isNaN(a.y));return b},distanceTo:function(a){return Math.sqrt(Math.pow(this.x-a.x,2)+Math.pow(this.y-a.y,2))},add:function(a,b){if(null==a||null==b)throw new TypeError("Pixel.add cannot receive null values");
return new OpenLayers.Pixel(this.x+a,this.y+b)},offset:function(a){var b=this.clone();a&&(b=this.add(a.x,a.y));return b},CLASS_NAME:"OpenLayers.Pixel"});OpenLayers.Size=OpenLayers.Class({w:0,h:0,initialize:function(a,b){this.w=parseFloat(a);this.h=parseFloat(b)},toString:function(){return"w="+this.w+",h="+this.h},clone:function(){return new OpenLayers.Size(this.w,this.h)},equals:function(a){var b=!1;null!=a&&(b=this.w==a.w&&this.h==a.h||isNaN(this.w)&&isNaN(this.h)&&isNaN(a.w)&&isNaN(a.h));return b},CLASS_NAME:"OpenLayers.Size"});OpenLayers.Console={log:function(){},debug:function(){},info:function(){},warn:function(){},error:function(){},userError:function(a){alert(a)},assert:function(){},dir:function(){},dirxml:function(){},trace:function(){},group:function(){},groupEnd:function(){},time:function(){},timeEnd:function(){},profile:function(){},profileEnd:function(){},count:function(){},CLASS_NAME:"OpenLayers.Console"};
(function(){for(var a=document.getElementsByTagName("script"),b=0,c=a.length;b<c;++b)if(-1!=a[b].src.indexOf("firebug.js")&&console){OpenLayers.Util.extend(OpenLayers.Console,console);break}})();OpenLayers.Lang={code:null,defaultCode:"en",getCode:function(){OpenLayers.Lang.code||OpenLayers.Lang.setCode();return OpenLayers.Lang.code},setCode:function(a){var b;a||(a="msie"==OpenLayers.BROWSER_NAME?navigator.userLanguage:navigator.language);a=a.split("-");a[0]=a[0].toLowerCase();"object"==typeof OpenLayers.Lang[a[0]]&&(b=a[0]);if(a[1]){var c=a[0]+"-"+a[1].toUpperCase();"object"==typeof OpenLayers.Lang[c]&&(b=c)}b||(OpenLayers.Console.warn("Failed to find OpenLayers.Lang."+a.join("-")+" dictionary, falling back to default language"),
b=OpenLayers.Lang.defaultCode);OpenLayers.Lang.code=b},translate:function(a,b){var c=OpenLayers.Lang[OpenLayers.Lang.getCode()];(c=c&&c[a])||(c=a);b&&(c=OpenLayers.String.format(c,b));return c}};OpenLayers.i18n=OpenLayers.Lang.translate;OpenLayers.Util=OpenLayers.Util||{};OpenLayers.Util.getElement=function(){for(var a=[],b=0,c=arguments.length;b<c;b++){var d=arguments[b];"string"==typeof d&&(d=document.getElementById(d));if(1==arguments.length)return d;a.push(d)}return a};OpenLayers.Util.isElement=function(a){return!(!a||1!==a.nodeType)};OpenLayers.Util.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)};OpenLayers.Util.removeItem=function(a,b){for(var c=a.length-1;0<=c;c--)a[c]==b&&a.splice(c,1);return a};
OpenLayers.Util.indexOf=function(a,b){if("function"==typeof a.indexOf)return a.indexOf(b);for(var c=0,d=a.length;c<d;c++)if(a[c]==b)return c;return-1};OpenLayers.Util.dotless=/\./g;
OpenLayers.Util.modifyDOMElement=function(a,b,c,d,e,f,g,h){b&&(a.id=b.replace(OpenLayers.Util.dotless,"_"));c&&(a.style.left=c.x+"px",a.style.top=c.y+"px");d&&(a.style.width=d.w+"px",a.style.height=d.h+"px");e&&(a.style.position=e);f&&(a.style.border=f);g&&(a.style.overflow=g);0<=parseFloat(h)&&1>parseFloat(h)?(a.style.filter="alpha(opacity="+100*h+")",a.style.opacity=h):1==parseFloat(h)&&(a.style.filter="",a.style.opacity="")};
OpenLayers.Util.createDiv=function(a,b,c,d,e,f,g,h){var k=document.createElement("div");d&&(k.style.backgroundImage="url("+d+")");a||(a=OpenLayers.Util.createUniqueID("OpenLayersDiv"));e||(e="absolute");OpenLayers.Util.modifyDOMElement(k,a,b,c,e,f,g,h);return k};
OpenLayers.Util.createImage=function(a,b,c,d,e,f,g,h){var k=document.createElement("img");a||(a=OpenLayers.Util.createUniqueID("OpenLayersDiv"));e||(e="relative");OpenLayers.Util.modifyDOMElement(k,a,b,c,e,f,null,g);h&&(k.style.display="none",b=function(){k.style.display="";OpenLayers.Event.stopObservingElement(k)},OpenLayers.Event.observe(k,"load",b),OpenLayers.Event.observe(k,"error",b));k.style.alt=a;k.galleryImg="no";d&&(k.src=d);return k};OpenLayers.IMAGE_RELOAD_ATTEMPTS=0;
OpenLayers.Util.alphaHackNeeded=null;OpenLayers.Util.alphaHack=function(){if(null==OpenLayers.Util.alphaHackNeeded){var a=navigator.appVersion.split("MSIE"),a=parseFloat(a[1]),b=!1;try{b=!!document.body.filters}catch(c){}OpenLayers.Util.alphaHackNeeded=b&&5.5<=a&&7>a}return OpenLayers.Util.alphaHackNeeded};
OpenLayers.Util.modifyAlphaImageDiv=function(a,b,c,d,e,f,g,h,k){OpenLayers.Util.modifyDOMElement(a,b,c,d,f,null,null,k);b=a.childNodes[0];e&&(b.src=e);OpenLayers.Util.modifyDOMElement(b,a.id+"_innerImage",null,d,"relative",g);OpenLayers.Util.alphaHack()&&("none"!=a.style.display&&(a.style.display="inline-block"),null==h&&(h="scale"),a.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+b.src+"', sizingMethod='"+h+"')",0<=parseFloat(a.style.opacity)&&1>parseFloat(a.style.opacity)&&
(a.style.filter+=" alpha(opacity="+100*a.style.opacity+")"),b.style.filter="alpha(opacity=0)")};OpenLayers.Util.createAlphaImageDiv=function(a,b,c,d,e,f,g,h,k){var l=OpenLayers.Util.createDiv();k=OpenLayers.Util.createImage(null,null,null,null,null,null,null,k);k.className="olAlphaImg";l.appendChild(k);OpenLayers.Util.modifyAlphaImageDiv(l,a,b,c,d,e,f,g,h);return l};OpenLayers.Util.upperCaseObject=function(a){var b={},c;for(c in a)b[c.toUpperCase()]=a[c];return b};
OpenLayers.Util.applyDefaults=function(a,b){a=a||{};var c="function"==typeof window.Event&&b instanceof window.Event,d;for(d in b)if(void 0===a[d]||!c&&b.hasOwnProperty&&b.hasOwnProperty(d)&&!a.hasOwnProperty(d))a[d]=b[d];!c&&(b&&b.hasOwnProperty&&b.hasOwnProperty("toString")&&!a.hasOwnProperty("toString"))&&(a.toString=b.toString);return a};
OpenLayers.Util.getParameterString=function(a){var b=[],c;for(c in a){var d=a[c];if(null!=d&&"function"!=typeof d){if("object"==typeof d&&d.constructor==Array){for(var e=[],f,g=0,h=d.length;g<h;g++)f=d[g],e.push(encodeURIComponent(null===f||void 0===f?"":f));d=e.join(",")}else d=encodeURIComponent(d);b.push(encodeURIComponent(c)+"="+d)}}return b.join("&")};OpenLayers.Util.urlAppend=function(a,b){var c=a;if(b)var d=(a+" ").split(/[?&]/),c=c+(" "===d.pop()?b:d.length?"&"+b:"?"+b);return c};
OpenLayers.Util.getImagesLocation=function(){return OpenLayers.ImgPath||OpenLayers._getScriptLocation()+"img/"};OpenLayers.Util.getImageLocation=function(a){return OpenLayers.Util.getImagesLocation()+a};OpenLayers.Util.Try=function(){for(var a=null,b=0,c=arguments.length;b<c;b++){var d=arguments[b];try{a=d();break}catch(e){}}return a};
OpenLayers.Util.getXmlNodeValue=function(a){var b=null;OpenLayers.Util.Try(function(){b=a.text;b||(b=a.textContent);b||(b=a.firstChild.nodeValue)},function(){b=a.textContent});return b};OpenLayers.Util.mouseLeft=function(a,b){for(var c=a.relatedTarget?a.relatedTarget:a.toElement;c!=b&&null!=c;)c=c.parentNode;return c!=b};OpenLayers.Util.DEFAULT_PRECISION=14;OpenLayers.Util.toFloat=function(a,b){null==b&&(b=OpenLayers.Util.DEFAULT_PRECISION);"number"!==typeof a&&(a=parseFloat(a));return 0===b?a:parseFloat(a.toPrecision(b))};
OpenLayers.Util.rad=function(a){return a*Math.PI/180};OpenLayers.Util.deg=function(a){return 180*a/Math.PI};OpenLayers.Util.VincentyConstants={a:6378137,b:6356752.3142,f:1/298.257223563};
OpenLayers.Util.distVincenty=function(a,b){for(var c=OpenLayers.Util.VincentyConstants,d=c.a,e=c.b,c=c.f,f=OpenLayers.Util.rad(b.lon-a.lon),g=Math.atan((1-c)*Math.tan(OpenLayers.Util.rad(a.lat))),h=Math.atan((1-c)*Math.tan(OpenLayers.Util.rad(b.lat))),k=Math.sin(g),g=Math.cos(g),l=Math.sin(h),h=Math.cos(h),m=f,n=2*Math.PI,p=20;1E-12<Math.abs(m-n)&&0<--p;){var q=Math.sin(m),r=Math.cos(m),s=Math.sqrt(h*q*h*q+(g*l-k*h*r)*(g*l-k*h*r));if(0==s)return 0;var r=k*l+g*h*r,t=Math.atan2(s,r),u=Math.asin(g*h*
q/s),v=Math.cos(u)*Math.cos(u),q=r-2*k*l/v,w=c/16*v*(4+c*(4-3*v)),n=m,m=f+(1-w)*c*Math.sin(u)*(t+w*s*(q+w*r*(-1+2*q*q)))}if(0==p)return NaN;d=v*(d*d-e*e)/(e*e);c=d/1024*(256+d*(-128+d*(74-47*d)));return(e*(1+d/16384*(4096+d*(-768+d*(320-175*d))))*(t-c*s*(q+c/4*(r*(-1+2*q*q)-c/6*q*(-3+4*s*s)*(-3+4*q*q))))).toFixed(3)/1E3};
OpenLayers.Util.destinationVincenty=function(a,b,c){var d=OpenLayers.Util,e=d.VincentyConstants,f=e.a,g=e.b,h=e.f,e=a.lon;a=a.lat;var k=d.rad(b);b=Math.sin(k);k=Math.cos(k);a=(1-h)*Math.tan(d.rad(a));var l=1/Math.sqrt(1+a*a),m=a*l,n=Math.atan2(a,k);a=l*b;for(var p=1-a*a,f=p*(f*f-g*g)/(g*g),q=1+f/16384*(4096+f*(-768+f*(320-175*f))),r=f/1024*(256+f*(-128+f*(74-47*f))),f=c/(g*q),s=2*Math.PI;1E-12<Math.abs(f-s);)var t=Math.cos(2*n+f),u=Math.sin(f),v=Math.cos(f),w=r*u*(t+r/4*(v*(-1+2*t*t)-r/6*t*(-3+4*
u*u)*(-3+4*t*t))),s=f,f=c/(g*q)+w;c=m*u-l*v*k;g=Math.atan2(m*v+l*u*k,(1-h)*Math.sqrt(a*a+c*c));b=Math.atan2(u*b,l*v-m*u*k);k=h/16*p*(4+h*(4-3*p));t=b-(1-k)*h*a*(f+k*u*(t+k*v*(-1+2*t*t)));Math.atan2(a,-c);return new OpenLayers.LonLat(e+d.deg(t),d.deg(g))};
OpenLayers.Util.getParameters=function(a,b){b=b||{};a=null===a||void 0===a?window.location.href:a;var c="";if(OpenLayers.String.contains(a,"?"))var d=a.indexOf("?")+1,c=OpenLayers.String.contains(a,"#")?a.indexOf("#"):a.length,c=a.substring(d,c);for(var d={},c=c.split(/[&;]/),e=0,f=c.length;e<f;++e){var g=c[e].split("=");if(g[0]){var h=g[0];try{h=decodeURIComponent(h)}catch(k){h=unescape(h)}g=(g[1]||"").replace(/\+/g," ");try{g=decodeURIComponent(g)}catch(l){g=unescape(g)}!1!==b.splitArgs&&(g=g.split(","));
1==g.length&&(g=g[0]);d[h]=g}}return d};OpenLayers.Util.lastSeqID=0;OpenLayers.Util.createUniqueID=function(a){a=null==a?"id_":a.replace(OpenLayers.Util.dotless,"_");OpenLayers.Util.lastSeqID+=1;return a+OpenLayers.Util.lastSeqID};OpenLayers.INCHES_PER_UNIT={inches:1,ft:12,mi:63360,m:39.37,km:39370,dd:4374754,yd:36};OpenLayers.INCHES_PER_UNIT["in"]=OpenLayers.INCHES_PER_UNIT.inches;OpenLayers.INCHES_PER_UNIT.degrees=OpenLayers.INCHES_PER_UNIT.dd;OpenLayers.INCHES_PER_UNIT.nmi=1852*OpenLayers.INCHES_PER_UNIT.m;
OpenLayers.METERS_PER_INCH=0.0254000508001016;
OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT,{Inch:OpenLayers.INCHES_PER_UNIT.inches,Meter:1/OpenLayers.METERS_PER_INCH,Foot:0.3048006096012192/OpenLayers.METERS_PER_INCH,IFoot:0.3048/OpenLayers.METERS_PER_INCH,ClarkeFoot:0.3047972651151/OpenLayers.METERS_PER_INCH,SearsFoot:0.30479947153867626/OpenLayers.METERS_PER_INCH,GoldCoastFoot:0.3047997101815088/OpenLayers.METERS_PER_INCH,IInch:0.0254/OpenLayers.METERS_PER_INCH,MicroInch:2.54E-5/OpenLayers.METERS_PER_INCH,Mil:2.54E-8/OpenLayers.METERS_PER_INCH,
Centimeter:0.01/OpenLayers.METERS_PER_INCH,Kilometer:1E3/OpenLayers.METERS_PER_INCH,Yard:0.9144018288036576/OpenLayers.METERS_PER_INCH,SearsYard:0.914398414616029/OpenLayers.METERS_PER_INCH,IndianYard:0.9143985307444408/OpenLayers.METERS_PER_INCH,IndianYd37:0.91439523/OpenLayers.METERS_PER_INCH,IndianYd62:0.9143988/OpenLayers.METERS_PER_INCH,IndianYd75:0.9143985/OpenLayers.METERS_PER_INCH,IndianFoot:0.30479951/OpenLayers.METERS_PER_INCH,IndianFt37:0.30479841/OpenLayers.METERS_PER_INCH,IndianFt62:0.3047996/
OpenLayers.METERS_PER_INCH,IndianFt75:0.3047995/OpenLayers.METERS_PER_INCH,Mile:1609.3472186944373/OpenLayers.METERS_PER_INCH,IYard:0.9144/OpenLayers.METERS_PER_INCH,IMile:1609.344/OpenLayers.METERS_PER_INCH,NautM:1852/OpenLayers.METERS_PER_INCH,"Lat-66":110943.31648893273/OpenLayers.METERS_PER_INCH,"Lat-83":110946.25736872235/OpenLayers.METERS_PER_INCH,Decimeter:0.1/OpenLayers.METERS_PER_INCH,Millimeter:0.001/OpenLayers.METERS_PER_INCH,Dekameter:10/OpenLayers.METERS_PER_INCH,Decameter:10/OpenLayers.METERS_PER_INCH,
Hectometer:100/OpenLayers.METERS_PER_INCH,GermanMeter:1.0000135965/OpenLayers.METERS_PER_INCH,CaGrid:0.999738/OpenLayers.METERS_PER_INCH,ClarkeChain:20.1166194976/OpenLayers.METERS_PER_INCH,GunterChain:20.11684023368047/OpenLayers.METERS_PER_INCH,BenoitChain:20.116782494375872/OpenLayers.METERS_PER_INCH,SearsChain:20.11676512155/OpenLayers.METERS_PER_INCH,ClarkeLink:0.201166194976/OpenLayers.METERS_PER_INCH,GunterLink:0.2011684023368047/OpenLayers.METERS_PER_INCH,BenoitLink:0.20116782494375873/OpenLayers.METERS_PER_INCH,
SearsLink:0.2011676512155/OpenLayers.METERS_PER_INCH,Rod:5.02921005842012/OpenLayers.METERS_PER_INCH,IntnlChain:20.1168/OpenLayers.METERS_PER_INCH,IntnlLink:0.201168/OpenLayers.METERS_PER_INCH,Perch:5.02921005842012/OpenLayers.METERS_PER_INCH,Pole:5.02921005842012/OpenLayers.METERS_PER_INCH,Furlong:201.1684023368046/OpenLayers.METERS_PER_INCH,Rood:3.778266898/OpenLayers.METERS_PER_INCH,CapeFoot:0.3047972615/OpenLayers.METERS_PER_INCH,Brealey:375/OpenLayers.METERS_PER_INCH,ModAmFt:0.304812252984506/
OpenLayers.METERS_PER_INCH,Fathom:1.8288/OpenLayers.METERS_PER_INCH,"NautM-UK":1853.184/OpenLayers.METERS_PER_INCH,"50kilometers":5E4/OpenLayers.METERS_PER_INCH,"150kilometers":15E4/OpenLayers.METERS_PER_INCH});
OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT,{mm:OpenLayers.INCHES_PER_UNIT.Meter/1E3,cm:OpenLayers.INCHES_PER_UNIT.Meter/100,dm:100*OpenLayers.INCHES_PER_UNIT.Meter,km:1E3*OpenLayers.INCHES_PER_UNIT.Meter,kmi:OpenLayers.INCHES_PER_UNIT.nmi,fath:OpenLayers.INCHES_PER_UNIT.Fathom,ch:OpenLayers.INCHES_PER_UNIT.IntnlChain,link:OpenLayers.INCHES_PER_UNIT.IntnlLink,"us-in":OpenLayers.INCHES_PER_UNIT.inches,"us-ft":OpenLayers.INCHES_PER_UNIT.Foot,"us-yd":OpenLayers.INCHES_PER_UNIT.Yard,"us-ch":OpenLayers.INCHES_PER_UNIT.GunterChain,
"us-mi":OpenLayers.INCHES_PER_UNIT.Mile,"ind-yd":OpenLayers.INCHES_PER_UNIT.IndianYd37,"ind-ft":OpenLayers.INCHES_PER_UNIT.IndianFt37,"ind-ch":20.11669506/OpenLayers.METERS_PER_INCH});OpenLayers.DOTS_PER_INCH=72;OpenLayers.Util.normalizeScale=function(a){return 1<a?1/a:a};OpenLayers.Util.getResolutionFromScale=function(a,b){var c;a&&(null==b&&(b="degrees"),c=1/(OpenLayers.Util.normalizeScale(a)*OpenLayers.INCHES_PER_UNIT[b]*OpenLayers.DOTS_PER_INCH));return c};
OpenLayers.Util.getScaleFromResolution=function(a,b){null==b&&(b="degrees");return a*OpenLayers.INCHES_PER_UNIT[b]*OpenLayers.DOTS_PER_INCH};
OpenLayers.Util.pagePosition=function(a){var b=[0,0],c=OpenLayers.Util.getViewportElement();if(!a||a==window||a==c)return b;var d=OpenLayers.IS_GECKO&&document.getBoxObjectFor&&"absolute"==OpenLayers.Element.getStyle(a,"position")&&(""==a.style.top||""==a.style.left),e=null;if(a.getBoundingClientRect)a=a.getBoundingClientRect(),e=window.pageYOffset||c.scrollTop,b[0]=a.left+(window.pageXOffset||c.scrollLeft),b[1]=a.top+e;else if(document.getBoxObjectFor&&!d)a=document.getBoxObjectFor(a),c=document.getBoxObjectFor(c),
b[0]=a.screenX-c.screenX,b[1]=a.screenY-c.screenY;else{b[0]=a.offsetLeft;b[1]=a.offsetTop;e=a.offsetParent;if(e!=a)for(;e;)b[0]+=e.offsetLeft,b[1]+=e.offsetTop,e=e.offsetParent;c=OpenLayers.BROWSER_NAME;if("opera"==c||"safari"==c&&"absolute"==OpenLayers.Element.getStyle(a,"position"))b[1]-=document.body.offsetTop;for(e=a.offsetParent;e&&e!=document.body;){b[0]-=e.scrollLeft;if("opera"!=c||"TR"!=e.tagName)b[1]-=e.scrollTop;e=e.offsetParent}}return b};
OpenLayers.Util.getViewportElement=function(){var a=arguments.callee.viewportElement;void 0==a&&(a="msie"==OpenLayers.BROWSER_NAME&&"CSS1Compat"!=document.compatMode?document.body:document.documentElement,arguments.callee.viewportElement=a);return a};
OpenLayers.Util.isEquivalentUrl=function(a,b,c){c=c||{};OpenLayers.Util.applyDefaults(c,{ignoreCase:!0,ignorePort80:!0,ignoreHash:!0,splitArgs:!1});a=OpenLayers.Util.createUrlObject(a,c);b=OpenLayers.Util.createUrlObject(b,c);for(var d in a)if("args"!==d&&a[d]!=b[d])return!1;for(d in a.args){if(a.args[d]!=b.args[d])return!1;delete b.args[d]}for(d in b.args)return!1;return!0};
OpenLayers.Util.createUrlObject=function(a,b){b=b||{};if(!/^\w+:\/\//.test(a)){var c=window.location,d=c.port?":"+c.port:"",d=c.protocol+"//"+c.host.split(":").shift()+d;0===a.indexOf("/")?a=d+a:(c=c.pathname.split("/"),c.pop(),a=d+c.join("/")+"/"+a)}b.ignoreCase&&(a=a.toLowerCase());c=document.createElement("a");c.href=a;d={};d.host=c.host.split(":").shift();d.protocol=c.protocol;d.port=b.ignorePort80?"80"==c.port||"0"==c.port?"":c.port:""==c.port||"0"==c.port?"80":c.port;d.hash=b.ignoreHash||"#"===
c.hash?"":c.hash;var e=c.search;e||(e=a.indexOf("?"),e=-1!=e?a.substr(e):"");d.args=OpenLayers.Util.getParameters(e,{splitArgs:b.splitArgs});d.pathname="/"==c.pathname.charAt(0)?c.pathname:"/"+c.pathname;return d};OpenLayers.Util.removeTail=function(a){var b=null,b=a.indexOf("?"),c=a.indexOf("#");return b=-1==b?-1!=c?a.substr(0,c):a:-1!=c?a.substr(0,Math.min(b,c)):a.substr(0,b)};OpenLayers.IS_GECKO=function(){var a=navigator.userAgent.toLowerCase();return-1==a.indexOf("webkit")&&-1!=a.indexOf("gecko")}();
OpenLayers.CANVAS_SUPPORTED=function(){var a=document.createElement("canvas");return!(!a.getContext||!a.getContext("2d"))}();OpenLayers.BROWSER_NAME=function(){var a="",b=navigator.userAgent.toLowerCase();-1!=b.indexOf("opera")?a="opera":-1!=b.indexOf("msie")?a="msie":-1!=b.indexOf("safari")?a="safari":-1!=b.indexOf("mozilla")&&(a=-1!=b.indexOf("firefox")?"firefox":"mozilla");return a}();OpenLayers.Util.getBrowserName=function(){return OpenLayers.BROWSER_NAME};
OpenLayers.Util.getRenderedDimensions=function(a,b,c){var d,e,f=document.createElement("div");f.style.visibility="hidden";for(var g=c&&c.containerElement?c.containerElement:document.body,h=!1,k=null,l=g;l&&"body"!=l.tagName.toLowerCase();){var m=OpenLayers.Element.getStyle(l,"position");if("absolute"==m){h=!0;break}else if(m&&"static"!=m)break;l=l.parentNode}!h||0!==g.clientHeight&&0!==g.clientWidth||(k=document.createElement("div"),k.style.visibility="hidden",k.style.position="absolute",k.style.overflow=
"visible",k.style.width=document.body.clientWidth+"px",k.style.height=document.body.clientHeight+"px",k.appendChild(f));f.style.position="absolute";b&&(b.w?(d=b.w,f.style.width=d+"px"):b.h&&(e=b.h,f.style.height=e+"px"));c&&c.displayClass&&(f.className=c.displayClass);b=document.createElement("div");b.innerHTML=a;b.style.overflow="visible";if(b.childNodes)for(a=0,c=b.childNodes.length;a<c;a++)b.childNodes[a].style&&(b.childNodes[a].style.overflow="visible");f.appendChild(b);k?g.appendChild(k):g.appendChild(f);
d||(d=parseInt(b.scrollWidth),f.style.width=d+"px");e||(e=parseInt(b.scrollHeight));f.removeChild(b);k?(k.removeChild(f),g.removeChild(k)):g.removeChild(f);return new OpenLayers.Size(d,e)};
OpenLayers.Util.getScrollbarWidth=function(){var a=OpenLayers.Util._scrollbarWidth;if(null==a){var b=null,c=null,b=a=0,b=document.createElement("div");b.style.position="absolute";b.style.top="-1000px";b.style.left="-1000px";b.style.width="100px";b.style.height="50px";b.style.overflow="hidden";c=document.createElement("div");c.style.width="100%";c.style.height="200px";b.appendChild(c);document.body.appendChild(b);a=c.offsetWidth;b.style.overflow="scroll";b=c.offsetWidth;document.body.removeChild(document.body.lastChild);
OpenLayers.Util._scrollbarWidth=a-b;a=OpenLayers.Util._scrollbarWidth}return a};
OpenLayers.Util.getFormattedLonLat=function(a,b,c){c||(c="dms");a=(a+540)%360-180;var d=Math.abs(a),e=Math.floor(d),f=d=(d-e)/(1/60),d=Math.floor(d),f=Math.round(10*((f-d)/(1/60))),f=f/10;60<=f&&(f-=60,d+=1,60<=d&&(d-=60,e+=1));10>e&&(e="0"+e);e+="\u00b0";0<=c.indexOf("dm")&&(10>d&&(d="0"+d),e+=d+"'",0<=c.indexOf("dms")&&(10>f&&(f="0"+f),e+=f+'"'));return e="lon"==b?e+(0>a?OpenLayers.i18n("W"):OpenLayers.i18n("E")):e+(0>a?OpenLayers.i18n("S"):OpenLayers.i18n("N"))};OpenLayers.Format=OpenLayers.Class({options:null,externalProjection:null,internalProjection:null,data:null,keepData:!1,initialize:function(a){OpenLayers.Util.extend(this,a);this.options=a},destroy:function(){},read:function(a){throw Error("Read not implemented.");},write:function(a){throw Error("Write not implemented.");},CLASS_NAME:"OpenLayers.Format"});OpenLayers.Format.CSWGetRecords=function(a){a=OpenLayers.Util.applyDefaults(a,OpenLayers.Format.CSWGetRecords.DEFAULTS);var b=OpenLayers.Format.CSWGetRecords["v"+a.version.replace(/\./g,"_")];if(!b)throw"Unsupported CSWGetRecords version: "+a.version;return new b(a)};OpenLayers.Format.CSWGetRecords.DEFAULTS={version:"2.0.2"};OpenLayers.Control=OpenLayers.Class({id:null,map:null,div:null,type:null,allowSelection:!1,displayClass:"",title:"",autoActivate:!1,active:null,handlerOptions:null,handler:null,eventListeners:null,events:null,initialize:function(a){this.displayClass=this.CLASS_NAME.replace("OpenLayers.","ol").replace(/\./g,"");OpenLayers.Util.extend(this,a);this.events=new OpenLayers.Events(this);if(this.eventListeners instanceof Object)this.events.on(this.eventListeners);null==this.id&&(this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+
"_"))},destroy:function(){this.events&&(this.eventListeners&&this.events.un(this.eventListeners),this.events.destroy(),this.events=null);this.eventListeners=null;this.handler&&(this.handler.destroy(),this.handler=null);if(this.handlers){for(var a in this.handlers)this.handlers.hasOwnProperty(a)&&"function"==typeof this.handlers[a].destroy&&this.handlers[a].destroy();this.handlers=null}this.map&&(this.map.removeControl(this),this.map=null);this.div=null},setMap:function(a){this.map=a;this.handler&&
this.handler.setMap(a)},draw:function(a){null==this.div&&(this.div=OpenLayers.Util.createDiv(this.id),this.div.className=this.displayClass,this.allowSelection||(this.div.className+=" olControlNoSelect",this.div.setAttribute("unselectable","on",0),this.div.onselectstart=OpenLayers.Function.False),""!=this.title&&(this.div.title=this.title));null!=a&&(this.position=a.clone());this.moveTo(this.position);return this.div},moveTo:function(a){null!=a&&null!=this.div&&(this.div.style.left=a.x+"px",this.div.style.top=
a.y+"px")},activate:function(){if(this.active)return!1;this.handler&&this.handler.activate();this.active=!0;this.map&&OpenLayers.Element.addClass(this.map.viewPortDiv,this.displayClass.replace(/ /g,"")+"Active");this.events.triggerEvent("activate");return!0},deactivate:function(){return this.active?(this.handler&&this.handler.deactivate(),this.active=!1,this.map&&OpenLayers.Element.removeClass(this.map.viewPortDiv,this.displayClass.replace(/ /g,"")+"Active"),this.events.triggerEvent("deactivate"),
!0):!1},CLASS_NAME:"OpenLayers.Control"});OpenLayers.Control.TYPE_BUTTON=1;OpenLayers.Control.TYPE_TOGGLE=2;OpenLayers.Control.TYPE_TOOL=3;OpenLayers.Event={observers:!1,KEY_SPACE:32,KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(a){return a.target||a.srcElement},isSingleTouch:function(a){return a.touches&&1==a.touches.length},isMultiTouch:function(a){return a.touches&&1<a.touches.length},isLeftClick:function(a){return a.which&&1==a.which||a.button&&1==a.button},isRightClick:function(a){return a.which&&3==a.which||a.button&&2==a.button},stop:function(a,
b){b||OpenLayers.Event.preventDefault(a);a.stopPropagation?a.stopPropagation():a.cancelBubble=!0},preventDefault:function(a){a.preventDefault?a.preventDefault():a.returnValue=!1},findElement:function(a,b){for(var c=OpenLayers.Event.element(a);c.parentNode&&(!c.tagName||c.tagName.toUpperCase()!=b.toUpperCase());)c=c.parentNode;return c},observe:function(a,b,c,d){a=OpenLayers.Util.getElement(a);d=d||!1;"keypress"==b&&(navigator.appVersion.match(/Konqueror|Safari|KHTML/)||a.attachEvent)&&(b="keydown");
this.observers||(this.observers={});if(!a._eventCacheID){var e="eventCacheID_";a.id&&(e=a.id+"_"+e);a._eventCacheID=OpenLayers.Util.createUniqueID(e)}e=a._eventCacheID;this.observers[e]||(this.observers[e]=[]);this.observers[e].push({element:a,name:b,observer:c,useCapture:d});a.addEventListener?a.addEventListener(b,c,d):a.attachEvent&&a.attachEvent("on"+b,c)},stopObservingElement:function(a){a=OpenLayers.Util.getElement(a)._eventCacheID;this._removeElementObservers(OpenLayers.Event.observers[a])},
_removeElementObservers:function(a){if(a)for(var b=a.length-1;0<=b;b--){var c=a[b];OpenLayers.Event.stopObserving.apply(this,[c.element,c.name,c.observer,c.useCapture])}},stopObserving:function(a,b,c,d){d=d||!1;a=OpenLayers.Util.getElement(a);var e=a._eventCacheID;"keypress"==b&&(navigator.appVersion.match(/Konqueror|Safari|KHTML/)||a.detachEvent)&&(b="keydown");var f=!1,g=OpenLayers.Event.observers[e];if(g)for(var h=0;!f&&h<g.length;){var k=g[h];if(k.name==b&&k.observer==c&&k.useCapture==d){g.splice(h,
1);0==g.length&&delete OpenLayers.Event.observers[e];f=!0;break}h++}f&&(a.removeEventListener?a.removeEventListener(b,c,d):a&&a.detachEvent&&a.detachEvent("on"+b,c));return f},unloadCache:function(){if(OpenLayers.Event&&OpenLayers.Event.observers){for(var a in OpenLayers.Event.observers)OpenLayers.Event._removeElementObservers.apply(this,[OpenLayers.Event.observers[a]]);OpenLayers.Event.observers=!1}},CLASS_NAME:"OpenLayers.Event"};
OpenLayers.Event.observe(window,"unload",OpenLayers.Event.unloadCache,!1);
OpenLayers.Events=OpenLayers.Class({BROWSER_EVENTS:"mouseover mouseout mousedown mouseup mousemove click dblclick rightclick dblrightclick resize focus blur touchstart touchmove touchend keydown".split(" "),listeners:null,object:null,element:null,eventHandler:null,fallThrough:null,includeXY:!1,extensions:null,extensionCount:null,clearMouseListener:null,initialize:function(a,b,c,d,e){OpenLayers.Util.extend(this,e);this.object=a;this.fallThrough=d;this.listeners={};this.extensions={};this.extensionCount=
{};this._msTouches=[];null!=b&&this.attachToElement(b)},destroy:function(){for(var a in this.extensions)"boolean"!==typeof this.extensions[a]&&this.extensions[a].destroy();this.extensions=null;this.element&&(OpenLayers.Event.stopObservingElement(this.element),this.element.hasScrollEvent&&OpenLayers.Event.stopObserving(window,"scroll",this.clearMouseListener));this.eventHandler=this.fallThrough=this.object=this.listeners=this.element=null},addEventType:function(a){},attachToElement:function(a){this.element?
OpenLayers.Event.stopObservingElement(this.element):(this.eventHandler=OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent,this),this.clearMouseListener=OpenLayers.Function.bind(this.clearMouseCache,this));this.element=a;for(var b=!!window.navigator.msMaxTouchPoints,c,d=0,e=this.BROWSER_EVENTS.length;d<e;d++)c=this.BROWSER_EVENTS[d],OpenLayers.Event.observe(a,c,this.eventHandler),b&&0===c.indexOf("touch")&&this.addMsTouchListener(a,c,this.eventHandler);OpenLayers.Event.observe(a,"dragstart",
OpenLayers.Event.stop)},on:function(a){for(var b in a)"scope"!=b&&a.hasOwnProperty(b)&&this.register(b,a.scope,a[b])},register:function(a,b,c,d){a in OpenLayers.Events&&!this.extensions[a]&&(this.extensions[a]=new OpenLayers.Events[a](this));if(null!=c){null==b&&(b=this.object);var e=this.listeners[a];e||(e=[],this.listeners[a]=e,this.extensionCount[a]=0);b={obj:b,func:c};d?(e.splice(this.extensionCount[a],0,b),"object"===typeof d&&d.extension&&this.extensionCount[a]++):e.push(b)}},registerPriority:function(a,
b,c){this.register(a,b,c,!0)},un:function(a){for(var b in a)"scope"!=b&&a.hasOwnProperty(b)&&this.unregister(b,a.scope,a[b])},unregister:function(a,b,c){null==b&&(b=this.object);a=this.listeners[a];if(null!=a)for(var d=0,e=a.length;d<e;d++)if(a[d].obj==b&&a[d].func==c){a.splice(d,1);break}},remove:function(a){null!=this.listeners[a]&&(this.listeners[a]=[])},triggerEvent:function(a,b){var c=this.listeners[a];if(c&&0!=c.length){null==b&&(b={});b.object=this.object;b.element=this.element;b.type||(b.type=
a);for(var c=c.slice(),d,e=0,f=c.length;e<f&&(d=c[e],d=d.func.apply(d.obj,[b]),void 0==d||!1!=d);e++);this.fallThrough||OpenLayers.Event.stop(b,!0);return d}},handleBrowserEvent:function(a){var b=a.type,c=this.listeners[b];if(c&&0!=c.length){if((c=a.touches)&&c[0]){for(var d=0,e=0,f=c.length,g,h=0;h<f;++h)g=this.getTouchClientXY(c[h]),d+=g.clientX,e+=g.clientY;a.clientX=d/f;a.clientY=e/f}this.includeXY&&(a.xy=this.getMousePosition(a));this.triggerEvent(b,a)}},getTouchClientXY:function(a){var b=window.olMockWin||
window,c=b.pageXOffset,b=b.pageYOffset,d=a.clientX,e=a.clientY;if(0===a.pageY&&Math.floor(e)>Math.floor(a.pageY)||0===a.pageX&&Math.floor(d)>Math.floor(a.pageX))d-=c,e-=b;else if(e<a.pageY-b||d<a.pageX-c)d=a.pageX-c,e=a.pageY-b;a.olClientX=d;a.olClientY=e;return{clientX:d,clientY:e}},clearMouseCache:function(){this.element.scrolls=null;this.element.lefttop=null;this.element.offsets=null},getMousePosition:function(a){this.includeXY?this.element.hasScrollEvent||(OpenLayers.Event.observe(window,"scroll",
this.clearMouseListener),this.element.hasScrollEvent=!0):this.clearMouseCache();if(!this.element.scrolls){var b=OpenLayers.Util.getViewportElement();this.element.scrolls=[window.pageXOffset||b.scrollLeft,window.pageYOffset||b.scrollTop]}this.element.lefttop||(this.element.lefttop=[document.documentElement.clientLeft||0,document.documentElement.clientTop||0]);this.element.offsets||(this.element.offsets=OpenLayers.Util.pagePosition(this.element));return new OpenLayers.Pixel(a.clientX+this.element.scrolls[0]-
this.element.offsets[0]-this.element.lefttop[0],a.clientY+this.element.scrolls[1]-this.element.offsets[1]-this.element.lefttop[1])},addMsTouchListener:function(a,b,c){function d(a){c(OpenLayers.Util.applyDefaults({stopPropagation:function(){for(var a=e.length-1;0<=a;--a)e[a].stopPropagation()},preventDefault:function(){for(var a=e.length-1;0<=a;--a)e[a].preventDefault()},type:b},a))}var e=this._msTouches;switch(b){case "touchstart":return this.addMsTouchListenerStart(a,b,d);case "touchend":return this.addMsTouchListenerEnd(a,
b,d);case "touchmove":return this.addMsTouchListenerMove(a,b,d);default:throw"Unknown touch event type";}},addMsTouchListenerStart:function(a,b,c){var d=this._msTouches;OpenLayers.Event.observe(a,"MSPointerDown",function(a){for(var b=!1,g=0,h=d.length;g<h;++g)if(d[g].pointerId==a.pointerId){b=!0;break}b||d.push(a);a.touches=d.slice();c(a)});OpenLayers.Event.observe(a,"MSPointerUp",function(a){for(var b=0,c=d.length;b<c;++b)if(d[b].pointerId==a.pointerId){d.splice(b,1);break}})},addMsTouchListenerMove:function(a,
b,c){var d=this._msTouches;OpenLayers.Event.observe(a,"MSPointerMove",function(a){if(a.pointerType!=a.MSPOINTER_TYPE_MOUSE||0!=a.buttons)if(1!=d.length||d[0].pageX!=a.pageX||d[0].pageY!=a.pageY){for(var b=0,g=d.length;b<g;++b)if(d[b].pointerId==a.pointerId){d[b]=a;break}a.touches=d.slice();c(a)}})},addMsTouchListenerEnd:function(a,b,c){var d=this._msTouches;OpenLayers.Event.observe(a,"MSPointerUp",function(a){for(var b=0,g=d.length;b<g;++b)if(d[b].pointerId==a.pointerId){d.splice(b,1);break}a.touches=
d.slice();c(a)})},CLASS_NAME:"OpenLayers.Events"});OpenLayers.Events.buttonclick=OpenLayers.Class({target:null,events:"mousedown mouseup click dblclick touchstart touchmove touchend keydown".split(" "),startRegEx:/^mousedown|touchstart$/,cancelRegEx:/^touchmove$/,completeRegEx:/^mouseup|touchend$/,initialize:function(a){this.target=a;for(a=this.events.length-1;0<=a;--a)this.target.register(this.events[a],this,this.buttonClick,{extension:!0})},destroy:function(){for(var a=this.events.length-1;0<=a;--a)this.target.unregister(this.events[a],this,this.buttonClick);
delete this.target},getPressedButton:function(a){var b=3,c;do{if(OpenLayers.Element.hasClass(a,"olButton")){c=a;break}a=a.parentNode}while(0<--b&&a);return c},ignore:function(a){var b=3,c=!1;do{if("a"===a.nodeName.toLowerCase()){c=!0;break}a=a.parentNode}while(0<--b&&a);return c},buttonClick:function(a){var b=!0,c=OpenLayers.Event.element(a);if(c&&(OpenLayers.Event.isLeftClick(a)||!~a.type.indexOf("mouse")))if(c=this.getPressedButton(c)){if("keydown"===a.type)switch(a.keyCode){case OpenLayers.Event.KEY_RETURN:case OpenLayers.Event.KEY_SPACE:this.target.triggerEvent("buttonclick",
{buttonElement:c}),OpenLayers.Event.stop(a),b=!1}else if(this.startEvt){if(this.completeRegEx.test(a.type)){var b=OpenLayers.Util.pagePosition(c),d=OpenLayers.Util.getViewportElement(),e=window.pageYOffset||d.scrollTop;b[0]-=window.pageXOffset||d.scrollLeft;b[1]-=e;this.target.triggerEvent("buttonclick",{buttonElement:c,buttonXY:{x:this.startEvt.clientX-b[0],y:this.startEvt.clientY-b[1]}})}this.cancelRegEx.test(a.type)&&delete this.startEvt;OpenLayers.Event.stop(a);b=!1}this.startRegEx.test(a.type)&&
(this.startEvt=a,OpenLayers.Event.stop(a),b=!1)}else b=!this.ignore(OpenLayers.Event.element(a)),delete this.startEvt;return b}});OpenLayers.Util=OpenLayers.Util||{};
OpenLayers.Util.vendorPrefix=function(){function a(a){return a?a.replace(/([A-Z])/g,function(a){return"-"+a.toLowerCase()}).replace(/^ms-/,"-ms-"):null}function b(a,b){if(void 0===g[b]){var c,e=0,f=d.length,p="undefined"!==typeof a.cssText;for(g[b]=null;e<f;e++)if((c=d[e])?(p||(c=c.toLowerCase()),c=c+b.charAt(0).toUpperCase()+b.slice(1)):c=b,void 0!==a[c]){g[b]=c;break}}return g[b]}function c(a){return b(e,a)}var d=["","O","ms","Moz","Webkit"],e=document.createElement("div").style,f={},g={};return{css:function(b){if(void 0===
f[b]){var d=b.replace(/(-[\s\S])/g,function(a){return a.charAt(1).toUpperCase()}),d=c(d);f[b]=a(d)}return f[b]},js:b,style:c,cssCache:f,jsCache:g}}();OpenLayers.Animation=function(a){var b=OpenLayers.Util.vendorPrefix.js(a,"requestAnimationFrame"),c=!!b,d=function(){var c=a[b]||function(b,c){a.setTimeout(b,16)};return function(b,d){c.apply(a,[b,d])}}(),e=0,f={};return{isNative:c,requestFrame:d,start:function(a,b,c){b=0<b?b:Number.POSITIVE_INFINITY;var l=++e,m=+new Date;f[l]=function(){f[l]&&+new Date-m<=b?(a(),f[l]&&d(f[l],c)):delete f[l]};d(f[l],c);return l},stop:function(a){delete f[a]}}}(window);OpenLayers.Tween=OpenLayers.Class({easing:null,begin:null,finish:null,duration:null,callbacks:null,time:null,minFrameRate:null,startTime:null,animationId:null,playing:!1,initialize:function(a){this.easing=a?a:OpenLayers.Easing.Expo.easeOut},start:function(a,b,c,d){this.playing=!0;this.begin=a;this.finish=b;this.duration=c;this.callbacks=d.callbacks;this.minFrameRate=d.minFrameRate||30;this.time=0;this.startTime=(new Date).getTime();OpenLayers.Animation.stop(this.animationId);this.animationId=null;
this.callbacks&&this.callbacks.start&&this.callbacks.start.call(this,this.begin);this.animationId=OpenLayers.Animation.start(OpenLayers.Function.bind(this.play,this))},stop:function(){this.playing&&(this.callbacks&&this.callbacks.done&&this.callbacks.done.call(this,this.finish),OpenLayers.Animation.stop(this.animationId),this.animationId=null,this.playing=!1)},play:function(){var a={},b;for(b in this.begin){var c=this.begin[b],d=this.finish[b];if(null==c||null==d||isNaN(c)||isNaN(d))throw new TypeError("invalid value for Tween");
a[b]=this.easing.apply(this,[this.time,c,d-c,this.duration])}this.time++;this.callbacks&&this.callbacks.eachStep&&((new Date).getTime()-this.startTime)/this.time<=1E3/this.minFrameRate&&this.callbacks.eachStep.call(this,a);this.time>this.duration&&this.stop()},CLASS_NAME:"OpenLayers.Tween"});OpenLayers.Easing={CLASS_NAME:"OpenLayers.Easing"};OpenLayers.Easing.Linear={easeIn:function(a,b,c,d){return c*a/d+b},easeOut:function(a,b,c,d){return c*a/d+b},easeInOut:function(a,b,c,d){return c*a/d+b},CLASS_NAME:"OpenLayers.Easing.Linear"};
OpenLayers.Easing.Expo={easeIn:function(a,b,c,d){return 0==a?b:c*Math.pow(2,10*(a/d-1))+b},easeOut:function(a,b,c,d){return a==d?b+c:c*(-Math.pow(2,-10*a/d)+1)+b},easeInOut:function(a,b,c,d){return 0==a?b:a==d?b+c:1>(a/=d/2)?c/2*Math.pow(2,10*(a-1))+b:c/2*(-Math.pow(2,-10*--a)+2)+b},CLASS_NAME:"OpenLayers.Easing.Expo"};
OpenLayers.Easing.Quad={easeIn:function(a,b,c,d){return c*(a/=d)*a+b},easeOut:function(a,b,c,d){return-c*(a/=d)*(a-2)+b},easeInOut:function(a,b,c,d){return 1>(a/=d/2)?c/2*a*a+b:-c/2*(--a*(a-2)-1)+b},CLASS_NAME:"OpenLayers.Easing.Quad"};OpenLayers.Projection=OpenLayers.Class({proj:null,projCode:null,titleRegEx:/\+title=[^\+]*/,initialize:function(a,b){OpenLayers.Util.extend(this,b);this.projCode=a;"object"==typeof Proj4js&&(this.proj=new Proj4js.Proj(a))},getCode:function(){return this.proj?this.proj.srsCode:this.projCode},getUnits:function(){return this.proj?this.proj.units:null},toString:function(){return this.getCode()},equals:function(a){var b=!1;a&&(a instanceof OpenLayers.Projection||(a=new OpenLayers.Projection(a)),"object"==
typeof Proj4js&&this.proj.defData&&a.proj.defData?b=this.proj.defData.replace(this.titleRegEx,"")==a.proj.defData.replace(this.titleRegEx,""):a.getCode&&(b=this.getCode(),a=a.getCode(),b=b==a||!!OpenLayers.Projection.transforms[b]&&OpenLayers.Projection.transforms[b][a]===OpenLayers.Projection.nullTransform));return b},destroy:function(){delete this.proj;delete this.projCode},CLASS_NAME:"OpenLayers.Projection"});OpenLayers.Projection.transforms={};
OpenLayers.Projection.defaults={"EPSG:4326":{units:"degrees",maxExtent:[-180,-90,180,90],yx:!0},"CRS:84":{units:"degrees",maxExtent:[-180,-90,180,90]},"EPSG:900913":{units:"m",maxExtent:[-2.003750834E7,-2.003750834E7,2.003750834E7,2.003750834E7]}};
OpenLayers.Projection.addTransform=function(a,b,c){if(c===OpenLayers.Projection.nullTransform){var d=OpenLayers.Projection.defaults[a];d&&!OpenLayers.Projection.defaults[b]&&(OpenLayers.Projection.defaults[b]=d)}OpenLayers.Projection.transforms[a]||(OpenLayers.Projection.transforms[a]={});OpenLayers.Projection.transforms[a][b]=c};
OpenLayers.Projection.transform=function(a,b,c){if(b&&c)if(b instanceof OpenLayers.Projection||(b=new OpenLayers.Projection(b)),c instanceof OpenLayers.Projection||(c=new OpenLayers.Projection(c)),b.proj&&c.proj)a=Proj4js.transform(b.proj,c.proj,a);else{b=b.getCode();c=c.getCode();var d=OpenLayers.Projection.transforms;if(d[b]&&d[b][c])d[b][c](a)}return a};OpenLayers.Projection.nullTransform=function(a){return a};
(function(){function a(a){a.x=180*a.x/d;a.y=180/Math.PI*(2*Math.atan(Math.exp(a.y/d*Math.PI))-Math.PI/2);return a}function b(a){a.x=a.x*d/180;var b=Math.log(Math.tan((90+a.y)*Math.PI/360))/Math.PI*d;a.y=Math.max(-2.003750834E7,Math.min(b,2.003750834E7));return a}function c(c,d){var e=OpenLayers.Projection.addTransform,f=OpenLayers.Projection.nullTransform,g,p,q,r,s;g=0;for(p=d.length;g<p;++g)for(q=d[g],e(c,q,b),e(q,c,a),s=g+1;s<p;++s)r=d[s],e(q,r,f),e(r,q,f)}var d=2.003750834E7,e=["EPSG:900913","EPSG:3857",
"EPSG:102113","EPSG:102100"],f=["CRS:84","urn:ogc:def:crs:EPSG:6.6:4326","EPSG:4326"],g;for(g=e.length-1;0<=g;--g)c(e[g],f);for(g=f.length-1;0<=g;--g)c(f[g],e)})();OpenLayers.Map=OpenLayers.Class({Z_INDEX_BASE:{BaseLayer:100,Overlay:325,Feature:725,Popup:750,Control:1E3},id:null,fractionalZoom:!1,events:null,allOverlays:!1,div:null,dragging:!1,size:null,viewPortDiv:null,layerContainerOrigin:null,layerContainerDiv:null,layers:null,controls:null,popups:null,baseLayer:null,center:null,resolution:null,zoom:0,panRatio:1.5,options:null,tileSize:null,projection:"EPSG:4326",units:null,resolutions:null,maxResolution:null,minResolution:null,maxScale:null,minScale:null,
maxExtent:null,minExtent:null,restrictedExtent:null,numZoomLevels:16,theme:null,displayProjection:null,fallThrough:!1,autoUpdateSize:!0,eventListeners:null,panTween:null,panMethod:OpenLayers.Easing.Expo.easeOut,panDuration:50,zoomTween:null,zoomMethod:OpenLayers.Easing.Quad.easeOut,zoomDuration:20,paddingForPopups:null,layerContainerOriginPx:null,minPx:null,maxPx:null,initialize:function(a,b){1===arguments.length&&"object"===typeof a&&(a=(b=a)&&b.div);this.tileSize=new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
OpenLayers.Map.TILE_HEIGHT);this.paddingForPopups=new OpenLayers.Bounds(15,15,15,15);this.theme=OpenLayers._getScriptLocation()+"theme/default/style.css";this.options=OpenLayers.Util.extend({},b);OpenLayers.Util.extend(this,b);OpenLayers.Util.applyDefaults(this,OpenLayers.Projection.defaults[this.projection instanceof OpenLayers.Projection?this.projection.projCode:this.projection]);!this.maxExtent||this.maxExtent instanceof OpenLayers.Bounds||(this.maxExtent=new OpenLayers.Bounds(this.maxExtent));
!this.minExtent||this.minExtent instanceof OpenLayers.Bounds||(this.minExtent=new OpenLayers.Bounds(this.minExtent));!this.restrictedExtent||this.restrictedExtent instanceof OpenLayers.Bounds||(this.restrictedExtent=new OpenLayers.Bounds(this.restrictedExtent));!this.center||this.center instanceof OpenLayers.LonLat||(this.center=new OpenLayers.LonLat(this.center));this.layers=[];this.id=OpenLayers.Util.createUniqueID("OpenLayers.Map_");this.div=OpenLayers.Util.getElement(a);this.div||(this.div=document.createElement("div"),
this.div.style.height="1px",this.div.style.width="1px");OpenLayers.Element.addClass(this.div,"olMap");var c=this.id+"_OpenLayers_ViewPort";this.viewPortDiv=OpenLayers.Util.createDiv(c,null,null,null,"relative",null,"hidden");this.viewPortDiv.style.width="100%";this.viewPortDiv.style.height="100%";this.viewPortDiv.className="olMapViewport";this.div.appendChild(this.viewPortDiv);this.events=new OpenLayers.Events(this,this.viewPortDiv,null,this.fallThrough,{includeXY:!0});OpenLayers.TileManager&&null!==
this.tileManager&&(this.tileManager instanceof OpenLayers.TileManager||(this.tileManager=new OpenLayers.TileManager(this.tileManager)),this.tileManager.addMap(this));c=this.id+"_OpenLayers_Container";this.layerContainerDiv=OpenLayers.Util.createDiv(c);this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE.Popup-1;this.layerContainerOriginPx={x:0,y:0};this.applyTransform();this.viewPortDiv.appendChild(this.layerContainerDiv);this.updateSize();if(this.eventListeners instanceof Object)this.events.on(this.eventListeners);
!0===this.autoUpdateSize&&(this.updateSizeDestroy=OpenLayers.Function.bind(this.updateSize,this),OpenLayers.Event.observe(window,"resize",this.updateSizeDestroy));if(this.theme){for(var c=!0,d=document.getElementsByTagName("link"),e=0,f=d.length;e<f;++e)if(OpenLayers.Util.isEquivalentUrl(d.item(e).href,this.theme)){c=!1;break}c&&(c=document.createElement("link"),c.setAttribute("rel","stylesheet"),c.setAttribute("type","text/css"),c.setAttribute("href",this.theme),document.getElementsByTagName("head")[0].appendChild(c))}null==
this.controls&&(this.controls=[],null!=OpenLayers.Control&&(OpenLayers.Control.Navigation?this.controls.push(new OpenLayers.Control.Navigation):OpenLayers.Control.TouchNavigation&&this.controls.push(new OpenLayers.Control.TouchNavigation),OpenLayers.Control.Zoom?this.controls.push(new OpenLayers.Control.Zoom):OpenLayers.Control.PanZoom&&this.controls.push(new OpenLayers.Control.PanZoom),OpenLayers.Control.ArgParser&&this.controls.push(new OpenLayers.Control.ArgParser),OpenLayers.Control.Attribution&&
this.controls.push(new OpenLayers.Control.Attribution)));e=0;for(f=this.controls.length;e<f;e++)this.addControlToMap(this.controls[e]);this.popups=[];this.unloadDestroy=OpenLayers.Function.bind(this.destroy,this);OpenLayers.Event.observe(window,"unload",this.unloadDestroy);b&&b.layers&&(delete this.center,delete this.zoom,this.addLayers(b.layers),b.center&&!this.getCenter()&&this.setCenter(b.center,b.zoom));this.panMethod&&(this.panTween=new OpenLayers.Tween(this.panMethod));this.zoomMethod&&this.applyTransform.transform&&
(this.zoomTween=new OpenLayers.Tween(this.zoomMethod))},getViewport:function(){return this.viewPortDiv},render:function(a){this.div=OpenLayers.Util.getElement(a);OpenLayers.Element.addClass(this.div,"olMap");this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);this.div.appendChild(this.viewPortDiv);this.updateSize()},unloadDestroy:null,updateSizeDestroy:null,destroy:function(){if(!this.unloadDestroy)return!1;this.panTween&&(this.panTween.stop(),this.panTween=null);this.zoomTween&&(this.zoomTween.stop(),
this.zoomTween=null);OpenLayers.Event.stopObserving(window,"unload",this.unloadDestroy);this.unloadDestroy=null;this.updateSizeDestroy&&OpenLayers.Event.stopObserving(window,"resize",this.updateSizeDestroy);this.paddingForPopups=null;if(null!=this.controls){for(var a=this.controls.length-1;0<=a;--a)this.controls[a].destroy();this.controls=null}if(null!=this.layers){for(a=this.layers.length-1;0<=a;--a)this.layers[a].destroy(!1);this.layers=null}this.viewPortDiv&&this.viewPortDiv.parentNode&&this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
this.viewPortDiv=null;this.tileManager&&(this.tileManager.removeMap(this),this.tileManager=null);this.eventListeners&&(this.events.un(this.eventListeners),this.eventListeners=null);this.events.destroy();this.options=this.events=null},setOptions:function(a){var b=this.minPx&&a.restrictedExtent!=this.restrictedExtent;OpenLayers.Util.extend(this,a);b&&this.moveTo(this.getCachedCenter(),this.zoom,{forceZoomChange:!0})},getTileSize:function(){return this.tileSize},getBy:function(a,b,c){var d="function"==
typeof c.test;return OpenLayers.Array.filter(this[a],function(a){return a[b]==c||d&&c.test(a[b])})},getLayersBy:function(a,b){return this.getBy("layers",a,b)},getLayersByName:function(a){return this.getLayersBy("name",a)},getLayersByClass:function(a){return this.getLayersBy("CLASS_NAME",a)},getControlsBy:function(a,b){return this.getBy("controls",a,b)},getControlsByClass:function(a){return this.getControlsBy("CLASS_NAME",a)},getLayer:function(a){for(var b=null,c=0,d=this.layers.length;c<d;c++){var e=
this.layers[c];if(e.id==a){b=e;break}}return b},setLayerZIndex:function(a,b){a.setZIndex(this.Z_INDEX_BASE[a.isBaseLayer?"BaseLayer":"Overlay"]+5*b)},resetLayersZIndex:function(){for(var a=0,b=this.layers.length;a<b;a++)this.setLayerZIndex(this.layers[a],a)},addLayer:function(a){for(var b=0,c=this.layers.length;b<c;b++)if(this.layers[b]==a)return!1;if(!1===this.events.triggerEvent("preaddlayer",{layer:a}))return!1;this.allOverlays&&(a.isBaseLayer=!1);a.div.className="olLayerDiv";a.div.style.overflow=
"";this.setLayerZIndex(a,this.layers.length);a.isFixed?this.viewPortDiv.appendChild(a.div):this.layerContainerDiv.appendChild(a.div);this.layers.push(a);a.setMap(this);a.isBaseLayer||this.allOverlays&&!this.baseLayer?null==this.baseLayer?this.setBaseLayer(a):a.setVisibility(!1):a.redraw();this.events.triggerEvent("addlayer",{layer:a});a.events.triggerEvent("added",{map:this,layer:a});a.afterAdd();return!0},addLayers:function(a){for(var b=0,c=a.length;b<c;b++)this.addLayer(a[b])},removeLayer:function(a,
b){if(!1!==this.events.triggerEvent("preremovelayer",{layer:a})){null==b&&(b=!0);a.isFixed?this.viewPortDiv.removeChild(a.div):this.layerContainerDiv.removeChild(a.div);OpenLayers.Util.removeItem(this.layers,a);a.removeMap(this);a.map=null;if(this.baseLayer==a&&(this.baseLayer=null,b))for(var c=0,d=this.layers.length;c<d;c++){var e=this.layers[c];if(e.isBaseLayer||this.allOverlays){this.setBaseLayer(e);break}}this.resetLayersZIndex();this.events.triggerEvent("removelayer",{layer:a});a.events.triggerEvent("removed",
{map:this,layer:a})}},getNumLayers:function(){return this.layers.length},getLayerIndex:function(a){return OpenLayers.Util.indexOf(this.layers,a)},setLayerIndex:function(a,b){var c=this.getLayerIndex(a);0>b?b=0:b>this.layers.length&&(b=this.layers.length);if(c!=b){this.layers.splice(c,1);this.layers.splice(b,0,a);for(var c=0,d=this.layers.length;c<d;c++)this.setLayerZIndex(this.layers[c],c);this.events.triggerEvent("changelayer",{layer:a,property:"order"});this.allOverlays&&(0===b?this.setBaseLayer(a):
this.baseLayer!==this.layers[0]&&this.setBaseLayer(this.layers[0]))}},raiseLayer:function(a,b){var c=this.getLayerIndex(a)+b;this.setLayerIndex(a,c)},setBaseLayer:function(a){if(a!=this.baseLayer&&-1!=OpenLayers.Util.indexOf(this.layers,a)){var b=this.getCachedCenter(),c=OpenLayers.Util.getResolutionFromScale(this.getScale(),a.units);null==this.baseLayer||this.allOverlays||this.baseLayer.setVisibility(!1);this.baseLayer=a;if(!this.allOverlays||this.baseLayer.visibility)this.baseLayer.setVisibility(!0),
!1===this.baseLayer.inRange&&this.baseLayer.redraw();null!=b&&(a=this.getZoomForResolution(c||this.resolution,!0),this.setCenter(b,a,!1,!0));this.events.triggerEvent("changebaselayer",{layer:this.baseLayer})}},addControl:function(a,b){this.controls.push(a);this.addControlToMap(a,b)},addControls:function(a,b){for(var c=1===arguments.length?[]:b,d=0,e=a.length;d<e;d++)this.addControl(a[d],c[d]?c[d]:null)},addControlToMap:function(a,b){a.outsideViewport=null!=a.div;this.displayProjection&&!a.displayProjection&&
(a.displayProjection=this.displayProjection);a.setMap(this);var c=a.draw(b);c&&!a.outsideViewport&&(c.style.zIndex=this.Z_INDEX_BASE.Control+this.controls.length,this.viewPortDiv.appendChild(c));a.autoActivate&&a.activate()},getControl:function(a){for(var b=null,c=0,d=this.controls.length;c<d;c++){var e=this.controls[c];if(e.id==a){b=e;break}}return b},removeControl:function(a){a&&a==this.getControl(a.id)&&(a.div&&a.div.parentNode==this.viewPortDiv&&this.viewPortDiv.removeChild(a.div),OpenLayers.Util.removeItem(this.controls,
a))},addPopup:function(a,b){if(b)for(var c=this.popups.length-1;0<=c;--c)this.removePopup(this.popups[c]);a.map=this;this.popups.push(a);if(c=a.draw())c.style.zIndex=this.Z_INDEX_BASE.Popup+this.popups.length,this.layerContainerDiv.appendChild(c)},removePopup:function(a){OpenLayers.Util.removeItem(this.popups,a);if(a.div)try{this.layerContainerDiv.removeChild(a.div)}catch(b){}a.map=null},getSize:function(){var a=null;null!=this.size&&(a=this.size.clone());return a},updateSize:function(){var a=this.getCurrentSize();
if(a&&!isNaN(a.h)&&!isNaN(a.w)){this.events.clearMouseCache();var b=this.getSize();null==b&&(this.size=b=a);if(!a.equals(b)){this.size=a;a=0;for(b=this.layers.length;a<b;a++)this.layers[a].onMapResize();a=this.getCachedCenter();null!=this.baseLayer&&null!=a&&(b=this.getZoom(),this.zoom=null,this.setCenter(a,b))}}this.events.triggerEvent("updatesize")},getCurrentSize:function(){var a=new OpenLayers.Size(this.div.clientWidth,this.div.clientHeight);if(0==a.w&&0==a.h||isNaN(a.w)&&isNaN(a.h))a.w=this.div.offsetWidth,
a.h=this.div.offsetHeight;if(0==a.w&&0==a.h||isNaN(a.w)&&isNaN(a.h))a.w=parseInt(this.div.style.width),a.h=parseInt(this.div.style.height);return a},calculateBounds:function(a,b){var c=null;null==a&&(a=this.getCachedCenter());null==b&&(b=this.getResolution());if(null!=a&&null!=b)var c=this.size.w*b/2,d=this.size.h*b/2,c=new OpenLayers.Bounds(a.lon-c,a.lat-d,a.lon+c,a.lat+d);return c},getCenter:function(){var a=null,b=this.getCachedCenter();b&&(a=b.clone());return a},getCachedCenter:function(){!this.center&&
this.size&&(this.center=this.getLonLatFromViewPortPx({x:this.size.w/2,y:this.size.h/2}));return this.center},getZoom:function(){return this.zoom},pan:function(a,b,c){c=OpenLayers.Util.applyDefaults(c,{animate:!0,dragging:!1});if(c.dragging)0==a&&0==b||this.moveByPx(a,b);else{var d=this.getViewPortPxFromLonLat(this.getCachedCenter());a=d.add(a,b);if(this.dragging||!a.equals(d))d=this.getLonLatFromViewPortPx(a),c.animate?this.panTo(d):(this.moveTo(d),this.dragging&&(this.dragging=!1,this.events.triggerEvent("moveend")))}},
panTo:function(a){if(this.panTween&&this.getExtent().scale(this.panRatio).containsLonLat(a)){var b=this.getCachedCenter();if(!a.equals(b)){var b=this.getPixelFromLonLat(b),c=this.getPixelFromLonLat(a),d=0,e=0;this.panTween.start({x:0,y:0},{x:c.x-b.x,y:c.y-b.y},this.panDuration,{callbacks:{eachStep:OpenLayers.Function.bind(function(a){this.moveByPx(a.x-d,a.y-e);d=Math.round(a.x);e=Math.round(a.y)},this),done:OpenLayers.Function.bind(function(b){this.moveTo(a);this.dragging=!1;this.events.triggerEvent("moveend")},
this)}})}}else this.setCenter(a)},setCenter:function(a,b,c,d){this.panTween&&this.panTween.stop();this.zoomTween&&this.zoomTween.stop();this.moveTo(a,b,{dragging:c,forceZoomChange:d})},moveByPx:function(a,b){var c=this.size.w/2,d=this.size.h/2,e=c+a,f=d+b,g=this.baseLayer.wrapDateLine,h=0,k=0;this.restrictedExtent&&(h=c,k=d,g=!1);a=g||e<=this.maxPx.x-h&&e>=this.minPx.x+h?Math.round(a):0;b=f<=this.maxPx.y-k&&f>=this.minPx.y+k?Math.round(b):0;if(a||b){this.dragging||(this.dragging=!0,this.events.triggerEvent("movestart"));
this.center=null;a&&(this.layerContainerOriginPx.x-=a,this.minPx.x-=a,this.maxPx.x-=a);b&&(this.layerContainerOriginPx.y-=b,this.minPx.y-=b,this.maxPx.y-=b);this.applyTransform();d=0;for(e=this.layers.length;d<e;++d)c=this.layers[d],c.visibility&&(c===this.baseLayer||c.inRange)&&(c.moveByPx(a,b),c.events.triggerEvent("move"));this.events.triggerEvent("move")}},adjustZoom:function(a){if(this.baseLayer&&this.baseLayer.wrapDateLine){var b=this.baseLayer.resolutions,c=this.getMaxExtent().getWidth()/this.size.w;
if(this.getResolutionForZoom(a)>c)if(this.fractionalZoom)a=this.getZoomForResolution(c);else for(var d=a|0,e=b.length;d<e;++d)if(b[d]<=c){a=d;break}}return a},getMinZoom:function(){return this.adjustZoom(0)},moveTo:function(a,b,c){null==a||a instanceof OpenLayers.LonLat||(a=new OpenLayers.LonLat(a));c||(c={});null!=b&&(b=parseFloat(b),this.fractionalZoom||(b=Math.round(b)));var d=b;b=this.adjustZoom(b);b!==d&&(a=this.getCenter());var d=c.dragging||this.dragging,e=c.forceZoomChange;this.getCachedCenter()||
this.isValidLonLat(a)||(a=this.maxExtent.getCenterLonLat(),this.center=a.clone());if(null!=this.restrictedExtent){null==a&&(a=this.center);null==b&&(b=this.getZoom());var f=this.getResolutionForZoom(b),f=this.calculateBounds(a,f);if(!this.restrictedExtent.containsBounds(f)){var g=this.restrictedExtent.getCenterLonLat();f.getWidth()>this.restrictedExtent.getWidth()?a=new OpenLayers.LonLat(g.lon,a.lat):f.left<this.restrictedExtent.left?a=a.add(this.restrictedExtent.left-f.left,0):f.right>this.restrictedExtent.right&&
(a=a.add(this.restrictedExtent.right-f.right,0));f.getHeight()>this.restrictedExtent.getHeight()?a=new OpenLayers.LonLat(a.lon,g.lat):f.bottom<this.restrictedExtent.bottom?a=a.add(0,this.restrictedExtent.bottom-f.bottom):f.top>this.restrictedExtent.top&&(a=a.add(0,this.restrictedExtent.top-f.top))}}e=e||this.isValidZoomLevel(b)&&b!=this.getZoom();f=this.isValidLonLat(a)&&!a.equals(this.center);if(e||f||d){d||this.events.triggerEvent("movestart",{zoomChanged:e});f&&(!e&&this.center&&this.centerLayerContainer(a),
this.center=a.clone());a=e?this.getResolutionForZoom(b):this.getResolution();if(e||null==this.layerContainerOrigin){this.layerContainerOrigin=this.getCachedCenter();this.layerContainerOriginPx.x=0;this.layerContainerOriginPx.y=0;this.applyTransform();var f=this.getMaxExtent({restricted:!0}),h=f.getCenterLonLat(),g=this.center.lon-h.lon,h=h.lat-this.center.lat,k=Math.round(f.getWidth()/a),l=Math.round(f.getHeight()/a);this.minPx={x:(this.size.w-k)/2-g/a,y:(this.size.h-l)/2-h/a};this.maxPx={x:this.minPx.x+
Math.round(f.getWidth()/a),y:this.minPx.y+Math.round(f.getHeight()/a)}}e&&(this.zoom=b,this.resolution=a);a=this.getExtent();this.baseLayer.visibility&&(this.baseLayer.moveTo(a,e,c.dragging),c.dragging||this.baseLayer.events.triggerEvent("moveend",{zoomChanged:e}));a=this.baseLayer.getExtent();for(b=this.layers.length-1;0<=b;--b)f=this.layers[b],f===this.baseLayer||f.isBaseLayer||(g=f.calculateInRange(),f.inRange!=g&&((f.inRange=g)||f.display(!1),this.events.triggerEvent("changelayer",{layer:f,property:"visibility"})),
g&&f.visibility&&(f.moveTo(a,e,c.dragging),c.dragging||f.events.triggerEvent("moveend",{zoomChanged:e})));this.events.triggerEvent("move");d||this.events.triggerEvent("moveend");if(e){b=0;for(c=this.popups.length;b<c;b++)this.popups[b].updatePosition();this.events.triggerEvent("zoomend")}}},centerLayerContainer:function(a){var b=this.getViewPortPxFromLonLat(this.layerContainerOrigin),c=this.getViewPortPxFromLonLat(a);if(null!=b&&null!=c){var d=this.layerContainerOriginPx.x;a=this.layerContainerOriginPx.y;
var e=Math.round(b.x-c.x),b=Math.round(b.y-c.y);this.applyTransform(this.layerContainerOriginPx.x=e,this.layerContainerOriginPx.y=b);d-=e;a-=b;this.minPx.x-=d;this.maxPx.x-=d;this.minPx.y-=a;this.maxPx.y-=a}},isValidZoomLevel:function(a){return null!=a&&0<=a&&a<this.getNumZoomLevels()},isValidLonLat:function(a){var b=!1;null!=a&&(b=this.getMaxExtent(),b=b.containsLonLat(a,{worldBounds:this.baseLayer.wrapDateLine&&b}));return b},getProjection:function(){var a=this.getProjectionObject();return a?a.getCode():
null},getProjectionObject:function(){var a=null;null!=this.baseLayer&&(a=this.baseLayer.projection);return a},getMaxResolution:function(){var a=null;null!=this.baseLayer&&(a=this.baseLayer.maxResolution);return a},getMaxExtent:function(a){var b=null;a&&a.restricted&&this.restrictedExtent?b=this.restrictedExtent:null!=this.baseLayer&&(b=this.baseLayer.maxExtent);return b},getNumZoomLevels:function(){var a=null;null!=this.baseLayer&&(a=this.baseLayer.numZoomLevels);return a},getExtent:function(){var a=
null;null!=this.baseLayer&&(a=this.baseLayer.getExtent());return a},getResolution:function(){var a=null;null!=this.baseLayer?a=this.baseLayer.getResolution():!0===this.allOverlays&&0<this.layers.length&&(a=this.layers[0].getResolution());return a},getUnits:function(){var a=null;null!=this.baseLayer&&(a=this.baseLayer.units);return a},getScale:function(){var a=null;null!=this.baseLayer&&(a=this.getResolution(),a=OpenLayers.Util.getScaleFromResolution(a,this.baseLayer.units));return a},getZoomForExtent:function(a,
b){var c=null;null!=this.baseLayer&&(c=this.baseLayer.getZoomForExtent(a,b));return c},getResolutionForZoom:function(a){var b=null;this.baseLayer&&(b=this.baseLayer.getResolutionForZoom(a));return b},getZoomForResolution:function(a,b){var c=null;null!=this.baseLayer&&(c=this.baseLayer.getZoomForResolution(a,b));return c},zoomTo:function(a,b){var c=this;if(c.isValidZoomLevel(a))if(c.baseLayer.wrapDateLine&&(a=c.adjustZoom(a)),c.zoomTween){var d=c.getResolution(),e=c.getResolutionForZoom(a),f={scale:1},
d={scale:d/e};c.zoomTween.playing&&c.zoomTween.duration<3*c.zoomDuration?c.zoomTween.finish={scale:c.zoomTween.finish.scale*d.scale}:(b||(e=c.getSize(),b={x:e.w/2,y:e.h/2}),c.zoomTween.start(f,d,c.zoomDuration,{minFrameRate:50,callbacks:{eachStep:function(a){var d=c.layerContainerOriginPx;a=a.scale;c.applyTransform(d.x+((a-1)*(d.x-b.x)|0),d.y+((a-1)*(d.y-b.y)|0),a)},done:function(a){c.applyTransform();a=c.getResolution()/a.scale;var d=c.getZoomForResolution(a,!0);c.moveTo(c.getZoomTargetCenter(b,
a),d,!0)}}}))}else f=b?c.getZoomTargetCenter(b,c.getResolutionForZoom(a)):null,c.setCenter(f,a)},zoomIn:function(){this.zoomTo(this.getZoom()+1)},zoomOut:function(){this.zoomTo(this.getZoom()-1)},zoomToExtent:function(a,b){a instanceof OpenLayers.Bounds||(a=new OpenLayers.Bounds(a));var c=a.getCenterLonLat();if(this.baseLayer.wrapDateLine){c=this.getMaxExtent();for(a=a.clone();a.right<a.left;)a.right+=c.getWidth();c=a.getCenterLonLat().wrapDateLine(c)}this.setCenter(c,this.getZoomForExtent(a,b))},
zoomToMaxExtent:function(a){a=this.getMaxExtent({restricted:a?a.restricted:!0});this.zoomToExtent(a)},zoomToScale:function(a,b){var c=OpenLayers.Util.getResolutionFromScale(a,this.baseLayer.units),d=this.size.w*c/2,c=this.size.h*c/2,e=this.getCachedCenter(),d=new OpenLayers.Bounds(e.lon-d,e.lat-c,e.lon+d,e.lat+c);this.zoomToExtent(d,b)},getLonLatFromViewPortPx:function(a){var b=null;null!=this.baseLayer&&(b=this.baseLayer.getLonLatFromViewPortPx(a));return b},getViewPortPxFromLonLat:function(a){var b=
null;null!=this.baseLayer&&(b=this.baseLayer.getViewPortPxFromLonLat(a));return b},getZoomTargetCenter:function(a,b){var c=null,d=this.getSize(),e=d.w/2-a.x,d=a.y-d.h/2,f=this.getLonLatFromPixel(a);f&&(c=new OpenLayers.LonLat(f.lon+e*b,f.lat+d*b));return c},getLonLatFromPixel:function(a){return this.getLonLatFromViewPortPx(a)},getPixelFromLonLat:function(a){a=this.getViewPortPxFromLonLat(a);a.x=Math.round(a.x);a.y=Math.round(a.y);return a},getGeodesicPixelSize:function(a){var b=a?this.getLonLatFromPixel(a):
this.getCachedCenter()||new OpenLayers.LonLat(0,0),c=this.getResolution();a=b.add(-c/2,0);var d=b.add(c/2,0),e=b.add(0,-c/2),b=b.add(0,c/2),c=new OpenLayers.Projection("EPSG:4326"),f=this.getProjectionObject()||c;f.equals(c)||(a.transform(f,c),d.transform(f,c),e.transform(f,c),b.transform(f,c));return new OpenLayers.Size(OpenLayers.Util.distVincenty(a,d),OpenLayers.Util.distVincenty(e,b))},getViewPortPxFromLayerPx:function(a){var b=null;null!=a&&(b=a.add(this.layerContainerOriginPx.x,this.layerContainerOriginPx.y));
return b},getLayerPxFromViewPortPx:function(a){var b=null;null!=a&&(b=a.add(-this.layerContainerOriginPx.x,-this.layerContainerOriginPx.y),isNaN(b.x)||isNaN(b.y))&&(b=null);return b},getLonLatFromLayerPx:function(a){a=this.getViewPortPxFromLayerPx(a);return this.getLonLatFromViewPortPx(a)},getLayerPxFromLonLat:function(a){a=this.getPixelFromLonLat(a);return this.getLayerPxFromViewPortPx(a)},applyTransform:function(a,b,c){c=c||1;var d=this.layerContainerOriginPx,e=1!==c;a=a||d.x;b=b||d.y;var f=this.layerContainerDiv.style,
g=this.applyTransform.transform,h=this.applyTransform.template;if(void 0===g&&(g=OpenLayers.Util.vendorPrefix.style("transform"),this.applyTransform.transform=g)){var k=OpenLayers.Element.getStyle(this.viewPortDiv,OpenLayers.Util.vendorPrefix.css("transform"));k&&"none"===k||(h=["translate3d(",",0) ","scale3d(",",1)"],f[g]=[h[0],"0,0",h[1]].join(""));h&&~f[g].indexOf(h[0])||(h=["translate(",") ","scale(",")"]);this.applyTransform.template=h}null===g||"translate3d("!==h[0]&&!0!==e?(f.left=a+"px",f.top=
b+"px",null!==g&&(f[g]="")):(!0===e&&"translate("===h[0]&&(a-=d.x,b-=d.y,f.left=d.x+"px",f.top=d.y+"px"),f[g]=[h[0],a,"px,",b,"px",h[1],h[2],c,",",c,h[3]].join(""))},CLASS_NAME:"OpenLayers.Map"});OpenLayers.Map.TILE_WIDTH=256;OpenLayers.Map.TILE_HEIGHT=256;OpenLayers.Handler=OpenLayers.Class({id:null,control:null,map:null,keyMask:null,active:!1,evt:null,touch:!1,initialize:function(a,b,c){OpenLayers.Util.extend(this,c);this.control=a;this.callbacks=b;(a=this.map||a.map)&&this.setMap(a);this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},setMap:function(a){this.map=a},checkModifiers:function(a){return null==this.keyMask?!0:((a.shiftKey?OpenLayers.Handler.MOD_SHIFT:0)|(a.ctrlKey?OpenLayers.Handler.MOD_CTRL:0)|(a.altKey?OpenLayers.Handler.MOD_ALT:
0)|(a.metaKey?OpenLayers.Handler.MOD_META:0))==this.keyMask},activate:function(){if(this.active)return!1;for(var a=OpenLayers.Events.prototype.BROWSER_EVENTS,b=0,c=a.length;b<c;b++)this[a[b]]&&this.register(a[b],this[a[b]]);return this.active=!0},deactivate:function(){if(!this.active)return!1;for(var a=OpenLayers.Events.prototype.BROWSER_EVENTS,b=0,c=a.length;b<c;b++)this[a[b]]&&this.unregister(a[b],this[a[b]]);this.active=this.touch=!1;return!0},startTouch:function(){if(!this.touch){this.touch=!0;
for(var a="mousedown mouseup mousemove click dblclick mouseout".split(" "),b=0,c=a.length;b<c;b++)this[a[b]]&&this.unregister(a[b],this[a[b]])}},callback:function(a,b){a&&this.callbacks[a]&&this.callbacks[a].apply(this.control,b)},register:function(a,b){this.map.events.registerPriority(a,this,b);this.map.events.registerPriority(a,this,this.setEvent)},unregister:function(a,b){this.map.events.unregister(a,this,b);this.map.events.unregister(a,this,this.setEvent)},setEvent:function(a){this.evt=a;return!0},
destroy:function(){this.deactivate();this.control=this.map=null},CLASS_NAME:"OpenLayers.Handler"});OpenLayers.Handler.MOD_NONE=0;OpenLayers.Handler.MOD_SHIFT=1;OpenLayers.Handler.MOD_CTRL=2;OpenLayers.Handler.MOD_ALT=4;OpenLayers.Handler.MOD_META=8;OpenLayers.Handler.Click=OpenLayers.Class(OpenLayers.Handler,{delay:300,single:!0,"double":!1,pixelTolerance:0,dblclickTolerance:13,stopSingle:!1,stopDouble:!1,timerId:null,down:null,last:null,first:null,rightclickTimerId:null,touchstart:function(a){this.startTouch();this.down=this.getEventInfo(a);this.last=this.getEventInfo(a);return!0},touchmove:function(a){this.last=this.getEventInfo(a);return!0},touchend:function(a){this.down&&(a.xy=this.last.xy,a.lastTouches=this.last.touches,this.handleSingle(a),
this.down=null);return!0},mousedown:function(a){this.down=this.getEventInfo(a);this.last=this.getEventInfo(a);return!0},mouseup:function(a){var b=!0;this.checkModifiers(a)&&(this.control.handleRightClicks&&OpenLayers.Event.isRightClick(a))&&(b=this.rightclick(a));return b},rightclick:function(a){if(this.passesTolerance(a)){if(null!=this.rightclickTimerId)return this.clearTimer(),this.callback("dblrightclick",[a]),!this.stopDouble;a=this["double"]?OpenLayers.Util.extend({},a):this.callback("rightclick",
[a]);a=OpenLayers.Function.bind(this.delayedRightCall,this,a);this.rightclickTimerId=window.setTimeout(a,this.delay)}return!this.stopSingle},delayedRightCall:function(a){this.rightclickTimerId=null;a&&this.callback("rightclick",[a])},click:function(a){this.last||(this.last=this.getEventInfo(a));this.handleSingle(a);return!this.stopSingle},dblclick:function(a){this.handleDouble(a);return!this.stopDouble},handleDouble:function(a){this.passesDblclickTolerance(a)&&(this["double"]&&this.callback("dblclick",
[a]),this.clearTimer())},handleSingle:function(a){this.passesTolerance(a)&&(null!=this.timerId?(this.last.touches&&1===this.last.touches.length&&(this["double"]&&OpenLayers.Event.preventDefault(a),this.handleDouble(a)),this.last.touches&&2===this.last.touches.length||this.clearTimer()):(this.first=this.getEventInfo(a),a=this.single?OpenLayers.Util.extend({},a):null,this.queuePotentialClick(a)))},queuePotentialClick:function(a){this.timerId=window.setTimeout(OpenLayers.Function.bind(this.delayedCall,
this,a),this.delay)},passesTolerance:function(a){var b=!0;if(null!=this.pixelTolerance&&this.down&&this.down.xy&&(b=this.pixelTolerance>=this.down.xy.distanceTo(a.xy))&&this.touch&&this.down.touches.length===this.last.touches.length){a=0;for(var c=this.down.touches.length;a<c;++a)if(this.getTouchDistance(this.down.touches[a],this.last.touches[a])>this.pixelTolerance){b=!1;break}}return b},getTouchDistance:function(a,b){return Math.sqrt(Math.pow(a.clientX-b.clientX,2)+Math.pow(a.clientY-b.clientY,
2))},passesDblclickTolerance:function(a){a=!0;this.down&&this.first&&(a=this.down.xy.distanceTo(this.first.xy)<=this.dblclickTolerance);return a},clearTimer:function(){null!=this.timerId&&(window.clearTimeout(this.timerId),this.timerId=null);null!=this.rightclickTimerId&&(window.clearTimeout(this.rightclickTimerId),this.rightclickTimerId=null)},delayedCall:function(a){this.timerId=null;a&&this.callback("click",[a])},getEventInfo:function(a){var b;if(a.touches){var c=a.touches.length;b=Array(c);for(var d,
e=0;e<c;e++)d=a.touches[e],b[e]={clientX:d.olClientX,clientY:d.olClientY}}return{xy:a.xy,touches:b}},deactivate:function(){var a=!1;OpenLayers.Handler.prototype.deactivate.apply(this,arguments)&&(this.clearTimer(),this.last=this.first=this.down=null,a=!0);return a},CLASS_NAME:"OpenLayers.Handler.Click"});OpenLayers.Handler.Drag=OpenLayers.Class(OpenLayers.Handler,{started:!1,stopDown:!0,dragging:!1,last:null,start:null,lastMoveEvt:null,oldOnselectstart:null,interval:0,timeoutId:null,documentDrag:!1,documentEvents:null,initialize:function(a,b,c){OpenLayers.Handler.prototype.initialize.apply(this,arguments);if(!0===this.documentDrag){var d=this;this._docMove=function(a){d.mousemove({xy:{x:a.clientX,y:a.clientY},element:document})};this._docUp=function(a){d.mouseup({xy:{x:a.clientX,y:a.clientY}})}}},
dragstart:function(a){var b=!0;this.dragging=!1;this.checkModifiers(a)&&(OpenLayers.Event.isLeftClick(a)||OpenLayers.Event.isSingleTouch(a))?(this.started=!0,this.last=this.start=a.xy,OpenLayers.Element.addClass(this.map.viewPortDiv,"olDragDown"),this.down(a),this.callback("down",[a.xy]),OpenLayers.Event.preventDefault(a),this.oldOnselectstart||(this.oldOnselectstart=document.onselectstart?document.onselectstart:OpenLayers.Function.True),document.onselectstart=OpenLayers.Function.False,b=!this.stopDown):
(this.started=!1,this.last=this.start=null);return b},dragmove:function(a){this.lastMoveEvt=a;!this.started||(this.timeoutId||a.xy.x==this.last.x&&a.xy.y==this.last.y)||(!0===this.documentDrag&&this.documentEvents&&(a.element===document?(this.adjustXY(a),this.setEvent(a)):this.removeDocumentEvents()),0<this.interval&&(this.timeoutId=setTimeout(OpenLayers.Function.bind(this.removeTimeout,this),this.interval)),this.dragging=!0,this.move(a),this.callback("move",[a.xy]),this.oldOnselectstart||(this.oldOnselectstart=
document.onselectstart,document.onselectstart=OpenLayers.Function.False),this.last=a.xy);return!0},dragend:function(a){if(this.started){!0===this.documentDrag&&this.documentEvents&&(this.adjustXY(a),this.removeDocumentEvents());var b=this.start!=this.last;this.dragging=this.started=!1;OpenLayers.Element.removeClass(this.map.viewPortDiv,"olDragDown");this.up(a);this.callback("up",[a.xy]);b&&this.callback("done",[a.xy]);document.onselectstart=this.oldOnselectstart}return!0},down:function(a){},move:function(a){},
up:function(a){},out:function(a){},mousedown:function(a){return this.dragstart(a)},touchstart:function(a){this.startTouch();return this.dragstart(a)},mousemove:function(a){return this.dragmove(a)},touchmove:function(a){return this.dragmove(a)},removeTimeout:function(){this.timeoutId=null;this.dragging&&this.mousemove(this.lastMoveEvt)},mouseup:function(a){return this.dragend(a)},touchend:function(a){a.xy=this.last;return this.dragend(a)},mouseout:function(a){if(this.started&&OpenLayers.Util.mouseLeft(a,
this.map.viewPortDiv))if(!0===this.documentDrag)this.addDocumentEvents();else{var b=this.start!=this.last;this.dragging=this.started=!1;OpenLayers.Element.removeClass(this.map.viewPortDiv,"olDragDown");this.out(a);this.callback("out",[]);b&&this.callback("done",[a.xy]);document.onselectstart&&(document.onselectstart=this.oldOnselectstart)}return!0},click:function(a){return this.start==this.last},activate:function(){var a=!1;OpenLayers.Handler.prototype.activate.apply(this,arguments)&&(this.dragging=
!1,a=!0);return a},deactivate:function(){var a=!1;OpenLayers.Handler.prototype.deactivate.apply(this,arguments)&&(this.dragging=this.started=!1,this.last=this.start=null,a=!0,OpenLayers.Element.removeClass(this.map.viewPortDiv,"olDragDown"));return a},adjustXY:function(a){var b=OpenLayers.Util.pagePosition(this.map.viewPortDiv);a.xy.x-=b[0];a.xy.y-=b[1]},addDocumentEvents:function(){OpenLayers.Element.addClass(document.body,"olDragDown");this.documentEvents=!0;OpenLayers.Event.observe(document,"mousemove",
this._docMove);OpenLayers.Event.observe(document,"mouseup",this._docUp)},removeDocumentEvents:function(){OpenLayers.Element.removeClass(document.body,"olDragDown");this.documentEvents=!1;OpenLayers.Event.stopObserving(document,"mousemove",this._docMove);OpenLayers.Event.stopObserving(document,"mouseup",this._docUp)},CLASS_NAME:"OpenLayers.Handler.Drag"});OpenLayers.Control.OverviewMap=OpenLayers.Class(OpenLayers.Control,{element:null,ovmap:null,size:{w:180,h:90},layers:null,minRectSize:15,minRectDisplayClass:"RectReplacement",minRatio:8,maxRatio:32,mapOptions:null,autoPan:!1,handlers:null,resolutionFactor:1,maximized:!1,maximizeTitle:"",minimizeTitle:"",initialize:function(a){this.layers=[];this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,[a])},destroy:function(){this.mapDiv&&(this.handlers.click&&this.handlers.click.destroy(),
this.handlers.drag&&this.handlers.drag.destroy(),this.ovmap&&this.ovmap.viewPortDiv.removeChild(this.extentRectangle),this.extentRectangle=null,this.rectEvents&&(this.rectEvents.destroy(),this.rectEvents=null),this.ovmap&&(this.ovmap.destroy(),this.ovmap=null),this.element.removeChild(this.mapDiv),this.mapDiv=null,this.div.removeChild(this.element),this.element=null,this.maximizeDiv&&(this.div.removeChild(this.maximizeDiv),this.maximizeDiv=null),this.minimizeDiv&&(this.div.removeChild(this.minimizeDiv),
this.minimizeDiv=null),this.map.events.un({buttonclick:this.onButtonClick,moveend:this.update,changebaselayer:this.baseLayerDraw,scope:this}),OpenLayers.Control.prototype.destroy.apply(this,arguments))},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(0===this.layers.length)if(this.map.baseLayer)this.layers=[this.map.baseLayer.clone()];else return this.map.events.register("changebaselayer",this,this.baseLayerDraw),this.div;this.element=document.createElement("div");this.element.className=
this.displayClass+"Element";this.element.style.display="none";this.mapDiv=document.createElement("div");this.mapDiv.style.width=this.size.w+"px";this.mapDiv.style.height=this.size.h+"px";this.mapDiv.style.position="relative";this.mapDiv.style.overflow="hidden";this.mapDiv.id=OpenLayers.Util.createUniqueID("overviewMap");this.extentRectangle=document.createElement("div");this.extentRectangle.style.position="absolute";this.extentRectangle.style.zIndex=1E3;this.extentRectangle.className=this.displayClass+
"ExtentRectangle";this.element.appendChild(this.mapDiv);this.div.appendChild(this.element);if(this.outsideViewport)this.element.style.display="";else{this.div.className+=" "+this.displayClass+"Container";var a=OpenLayers.Util.getImageLocation("layer-switcher-maximize.png");this.maximizeDiv=OpenLayers.Util.createAlphaImageDiv(this.displayClass+"MaximizeButton",null,null,a,"absolute");this.maximizeDiv.style.display="none";this.maximizeDiv.className=this.displayClass+"MaximizeButton olButton";this.maximizeTitle&&
(this.maximizeDiv.title=this.maximizeTitle);this.div.appendChild(this.maximizeDiv);a=OpenLayers.Util.getImageLocation("layer-switcher-minimize.png");this.minimizeDiv=OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_minimizeDiv",null,null,a,"absolute");this.minimizeDiv.style.display="none";this.minimizeDiv.className=this.displayClass+"MinimizeButton olButton";this.minimizeTitle&&(this.minimizeDiv.title=this.minimizeTitle);this.div.appendChild(this.minimizeDiv);this.minimizeControl()}this.map.getExtent()&&
this.update();this.map.events.on({buttonclick:this.onButtonClick,moveend:this.update,scope:this});this.maximized&&this.maximizeControl();return this.div},baseLayerDraw:function(){this.draw();this.map.events.unregister("changebaselayer",this,this.baseLayerDraw)},rectDrag:function(a){var b=this.handlers.drag.last.x-a.x,c=this.handlers.drag.last.y-a.y;if(0!=b||0!=c){var d=this.rectPxBounds.top,e=this.rectPxBounds.left;a=Math.abs(this.rectPxBounds.getHeight());var f=this.rectPxBounds.getWidth(),c=Math.max(0,
d-c),c=Math.min(c,this.ovmap.size.h-this.hComp-a),b=Math.max(0,e-b),b=Math.min(b,this.ovmap.size.w-this.wComp-f);this.setRectPxBounds(new OpenLayers.Bounds(b,c+a,b+f,c))}},mapDivClick:function(a){var b=this.rectPxBounds.getCenterPixel(),c=a.xy.x-b.x,d=a.xy.y-b.y,e=this.rectPxBounds.top,f=this.rectPxBounds.left;a=Math.abs(this.rectPxBounds.getHeight());b=this.rectPxBounds.getWidth();d=Math.max(0,e+d);d=Math.min(d,this.ovmap.size.h-a);c=Math.max(0,f+c);c=Math.min(c,this.ovmap.size.w-b);this.setRectPxBounds(new OpenLayers.Bounds(c,
d+a,c+b,d));this.updateMapToRect()},onButtonClick:function(a){a.buttonElement===this.minimizeDiv?this.minimizeControl():a.buttonElement===this.maximizeDiv&&this.maximizeControl()},maximizeControl:function(a){this.element.style.display="";this.showToggle(!1);null!=a&&OpenLayers.Event.stop(a)},minimizeControl:function(a){this.element.style.display="none";this.showToggle(!0);null!=a&&OpenLayers.Event.stop(a)},showToggle:function(a){this.maximizeDiv&&(this.maximizeDiv.style.display=a?"":"none");this.minimizeDiv&&
(this.minimizeDiv.style.display=a?"none":"")},update:function(){null==this.ovmap&&this.createMap();!this.autoPan&&this.isSuitableOverview()||this.updateOverview();this.updateRectToMap()},isSuitableOverview:function(){var a=this.map.getExtent(),b=this.map.getMaxExtent(),a=new OpenLayers.Bounds(Math.max(a.left,b.left),Math.max(a.bottom,b.bottom),Math.min(a.right,b.right),Math.min(a.top,b.top));this.ovmap.getProjection()!=this.map.getProjection()&&(a=a.transform(this.map.getProjectionObject(),this.ovmap.getProjectionObject()));
b=this.ovmap.getResolution()/this.map.getResolution();return b>this.minRatio&&b<=this.maxRatio&&this.ovmap.getExtent().containsBounds(a)},updateOverview:function(){var a=this.map.getResolution(),b=this.ovmap.getResolution(),c=b/a;c>this.maxRatio?b=this.minRatio*a:c<=this.minRatio&&(b=this.maxRatio*a);this.ovmap.getProjection()!=this.map.getProjection()?(a=this.map.center.clone(),a.transform(this.map.getProjectionObject(),this.ovmap.getProjectionObject())):a=this.map.center;this.ovmap.setCenter(a,
this.ovmap.getZoomForResolution(b*this.resolutionFactor));this.updateRectToMap()},createMap:function(){var a=OpenLayers.Util.extend({controls:[],maxResolution:"auto",fallThrough:!1},this.mapOptions);this.ovmap=new OpenLayers.Map(this.mapDiv,a);this.ovmap.viewPortDiv.appendChild(this.extentRectangle);OpenLayers.Event.stopObserving(window,"unload",this.ovmap.unloadDestroy);this.ovmap.addLayers(this.layers);this.ovmap.zoomToMaxExtent();this.wComp=(this.wComp=parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
"border-left-width"))+parseInt(OpenLayers.Element.getStyle(this.extentRectangle,"border-right-width")))?this.wComp:2;this.hComp=(this.hComp=parseInt(OpenLayers.Element.getStyle(this.extentRectangle,"border-top-width"))+parseInt(OpenLayers.Element.getStyle(this.extentRectangle,"border-bottom-width")))?this.hComp:2;this.handlers.drag=new OpenLayers.Handler.Drag(this,{move:this.rectDrag,done:this.updateMapToRect},{map:this.ovmap});this.handlers.click=new OpenLayers.Handler.Click(this,{click:this.mapDivClick},
{single:!0,"double":!1,stopSingle:!0,stopDouble:!0,pixelTolerance:1,map:this.ovmap});this.handlers.click.activate();this.rectEvents=new OpenLayers.Events(this,this.extentRectangle,null,!0);this.rectEvents.register("mouseover",this,function(a){this.handlers.drag.active||this.map.dragging||this.handlers.drag.activate()});this.rectEvents.register("mouseout",this,function(a){this.handlers.drag.dragging||this.handlers.drag.deactivate()});if(this.ovmap.getProjection()!=this.map.getProjection()){var a=this.map.getProjectionObject().getUnits()||
this.map.units||this.map.baseLayer.units,b=this.ovmap.getProjectionObject().getUnits()||this.ovmap.units||this.ovmap.baseLayer.units;this.resolutionFactor=a&&b?OpenLayers.INCHES_PER_UNIT[a]/OpenLayers.INCHES_PER_UNIT[b]:1}},updateRectToMap:function(){var a;a=this.ovmap.getProjection()!=this.map.getProjection()?this.map.getExtent().transform(this.map.getProjectionObject(),this.ovmap.getProjectionObject()):this.map.getExtent();(a=this.getRectBoundsFromMapBounds(a))&&this.setRectPxBounds(a)},updateMapToRect:function(){var a=
this.getMapBoundsFromRectBounds(this.rectPxBounds);this.ovmap.getProjection()!=this.map.getProjection()&&(a=a.transform(this.ovmap.getProjectionObject(),this.map.getProjectionObject()));this.map.panTo(a.getCenterLonLat())},setRectPxBounds:function(a){var b=Math.max(a.top,0),c=Math.max(a.left,0),d=Math.min(a.top+Math.abs(a.getHeight()),this.ovmap.size.h-this.hComp);a=Math.min(a.left+a.getWidth(),this.ovmap.size.w-this.wComp);var e=Math.max(a-c,0),f=Math.max(d-b,0);e<this.minRectSize||f<this.minRectSize?
(this.extentRectangle.className=this.displayClass+this.minRectDisplayClass,e=c+e/2-this.minRectSize/2,this.extentRectangle.style.top=Math.round(b+f/2-this.minRectSize/2)+"px",this.extentRectangle.style.left=Math.round(e)+"px",this.extentRectangle.style.height=this.minRectSize+"px",this.extentRectangle.style.width=this.minRectSize+"px"):(this.extentRectangle.className=this.displayClass+"ExtentRectangle",this.extentRectangle.style.top=Math.round(b)+"px",this.extentRectangle.style.left=Math.round(c)+
"px",this.extentRectangle.style.height=Math.round(f)+"px",this.extentRectangle.style.width=Math.round(e)+"px");this.rectPxBounds=new OpenLayers.Bounds(Math.round(c),Math.round(d),Math.round(a),Math.round(b))},getRectBoundsFromMapBounds:function(a){var b=this.getOverviewPxFromLonLat({lon:a.left,lat:a.bottom});a=this.getOverviewPxFromLonLat({lon:a.right,lat:a.top});var c=null;b&&a&&(c=new OpenLayers.Bounds(b.x,b.y,a.x,a.y));return c},getMapBoundsFromRectBounds:function(a){var b=this.getLonLatFromOverviewPx({x:a.left,
y:a.bottom});a=this.getLonLatFromOverviewPx({x:a.right,y:a.top});return new OpenLayers.Bounds(b.lon,b.lat,a.lon,a.lat)},getLonLatFromOverviewPx:function(a){var b=this.ovmap.size,c=this.ovmap.getResolution(),d=this.ovmap.getExtent().getCenterLonLat();return{lon:d.lon+(a.x-b.w/2)*c,lat:d.lat-(a.y-b.h/2)*c}},getOverviewPxFromLonLat:function(a){var b=this.ovmap.getResolution(),c=this.ovmap.getExtent();if(c)return{x:Math.round(1/b*(a.lon-c.left)),y:Math.round(1/b*(c.top-a.lat))}},CLASS_NAME:"OpenLayers.Control.OverviewMap"});OpenLayers.Layer=OpenLayers.Class({id:null,name:null,div:null,opacity:1,alwaysInRange:null,RESOLUTION_PROPERTIES:"scales resolutions maxScale minScale maxResolution minResolution numZoomLevels maxZoomLevel".split(" "),events:null,map:null,isBaseLayer:!1,alpha:!1,displayInLayerSwitcher:!0,visibility:!0,attribution:null,inRange:!1,imageSize:null,options:null,eventListeners:null,gutter:0,projection:null,units:null,scales:null,resolutions:null,maxExtent:null,minExtent:null,maxResolution:null,minResolution:null,
numZoomLevels:null,minScale:null,maxScale:null,displayOutsideMaxExtent:!1,wrapDateLine:!1,metadata:null,initialize:function(a,b){this.metadata={};b=OpenLayers.Util.extend({},b);null!=this.alwaysInRange&&(b.alwaysInRange=this.alwaysInRange);this.addOptions(b);this.name=a;if(null==this.id&&(this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_"),this.div=OpenLayers.Util.createDiv(this.id),this.div.style.width="100%",this.div.style.height="100%",this.div.dir="ltr",this.events=new OpenLayers.Events(this,
this.div),this.eventListeners instanceof Object))this.events.on(this.eventListeners)},destroy:function(a){null==a&&(a=!0);null!=this.map&&this.map.removeLayer(this,a);this.options=this.div=this.name=this.map=this.projection=null;this.events&&(this.eventListeners&&this.events.un(this.eventListeners),this.events.destroy());this.events=this.eventListeners=null},clone:function(a){null==a&&(a=new OpenLayers.Layer(this.name,this.getOptions()));OpenLayers.Util.applyDefaults(a,this);a.map=null;return a},
getOptions:function(){var a={},b;for(b in this.options)a[b]=this[b];return a},setName:function(a){a!=this.name&&(this.name=a,null!=this.map&&this.map.events.triggerEvent("changelayer",{layer:this,property:"name"}))},addOptions:function(a,b){null==this.options&&(this.options={});a&&("string"==typeof a.projection&&(a.projection=new OpenLayers.Projection(a.projection)),a.projection&&OpenLayers.Util.applyDefaults(a,OpenLayers.Projection.defaults[a.projection.getCode()]),!a.maxExtent||a.maxExtent instanceof
OpenLayers.Bounds||(a.maxExtent=new OpenLayers.Bounds(a.maxExtent)),!a.minExtent||a.minExtent instanceof OpenLayers.Bounds||(a.minExtent=new OpenLayers.Bounds(a.minExtent)));OpenLayers.Util.extend(this.options,a);OpenLayers.Util.extend(this,a);this.projection&&this.projection.getUnits()&&(this.units=this.projection.getUnits());if(this.map){var c=this.map.getResolution(),d=this.RESOLUTION_PROPERTIES.concat(["projection","units","minExtent","maxExtent"]),e;for(e in a)if(a.hasOwnProperty(e)&&0<=OpenLayers.Util.indexOf(d,
e)){this.initResolutions();b&&this.map.baseLayer===this&&(this.map.setCenter(this.map.getCenter(),this.map.getZoomForResolution(c),!1,!0),this.map.events.triggerEvent("changebaselayer",{layer:this}));break}}},onMapResize:function(){},redraw:function(){var a=!1;if(this.map){this.inRange=this.calculateInRange();var b=this.getExtent();b&&(this.inRange&&this.visibility)&&(this.moveTo(b,!0,!1),this.events.triggerEvent("moveend",{zoomChanged:!0}),a=!0)}return a},moveTo:function(a,b,c){a=this.visibility;
this.isBaseLayer||(a=a&&this.inRange);this.display(a)},moveByPx:function(a,b){},setMap:function(a){null==this.map&&(this.map=a,this.maxExtent=this.maxExtent||this.map.maxExtent,this.minExtent=this.minExtent||this.map.minExtent,this.projection=this.projection||this.map.projection,"string"==typeof this.projection&&(this.projection=new OpenLayers.Projection(this.projection)),this.units=this.projection.getUnits()||this.units||this.map.units,this.initResolutions(),this.isBaseLayer||(this.inRange=this.calculateInRange(),
this.div.style.display=this.visibility&&this.inRange?"":"none"),this.setTileSize())},afterAdd:function(){},removeMap:function(a){},getImageSize:function(a){return this.imageSize||this.tileSize},setTileSize:function(a){this.tileSize=a=a?a:this.tileSize?this.tileSize:this.map.getTileSize();this.gutter&&(this.imageSize=new OpenLayers.Size(a.w+2*this.gutter,a.h+2*this.gutter))},getVisibility:function(){return this.visibility},setVisibility:function(a){a!=this.visibility&&(this.visibility=a,this.display(a),
this.redraw(),null!=this.map&&this.map.events.triggerEvent("changelayer",{layer:this,property:"visibility"}),this.events.triggerEvent("visibilitychanged"))},display:function(a){a!=("none"!=this.div.style.display)&&(this.div.style.display=a&&this.calculateInRange()?"block":"none")},calculateInRange:function(){var a=!1;this.alwaysInRange?a=!0:this.map&&(a=this.map.getResolution(),a=a>=this.minResolution&&a<=this.maxResolution);return a},setIsBaseLayer:function(a){a!=this.isBaseLayer&&(this.isBaseLayer=
a,null!=this.map&&this.map.events.triggerEvent("changebaselayer",{layer:this}))},initResolutions:function(){var a,b,c,d={},e=!0;a=0;for(b=this.RESOLUTION_PROPERTIES.length;a<b;a++)c=this.RESOLUTION_PROPERTIES[a],d[c]=this.options[c],e&&this.options[c]&&(e=!1);null==this.options.alwaysInRange&&(this.alwaysInRange=e);null==d.resolutions&&(d.resolutions=this.resolutionsFromScales(d.scales));null==d.resolutions&&(d.resolutions=this.calculateResolutions(d));if(null==d.resolutions){a=0;for(b=this.RESOLUTION_PROPERTIES.length;a<
b;a++)c=this.RESOLUTION_PROPERTIES[a],d[c]=null!=this.options[c]?this.options[c]:this.map[c];null==d.resolutions&&(d.resolutions=this.resolutionsFromScales(d.scales));null==d.resolutions&&(d.resolutions=this.calculateResolutions(d))}var f;this.options.maxResolution&&"auto"!==this.options.maxResolution&&(f=this.options.maxResolution);this.options.minScale&&(f=OpenLayers.Util.getResolutionFromScale(this.options.minScale,this.units));var g;this.options.minResolution&&"auto"!==this.options.minResolution&&
(g=this.options.minResolution);this.options.maxScale&&(g=OpenLayers.Util.getResolutionFromScale(this.options.maxScale,this.units));d.resolutions&&(d.resolutions.sort(function(a,b){return b-a}),f||(f=d.resolutions[0]),g||(g=d.resolutions[d.resolutions.length-1]));if(this.resolutions=d.resolutions){b=this.resolutions.length;this.scales=Array(b);for(a=0;a<b;a++)this.scales[a]=OpenLayers.Util.getScaleFromResolution(this.resolutions[a],this.units);this.numZoomLevels=b}if(this.minResolution=g)this.maxScale=
OpenLayers.Util.getScaleFromResolution(g,this.units);if(this.maxResolution=f)this.minScale=OpenLayers.Util.getScaleFromResolution(f,this.units)},resolutionsFromScales:function(a){if(null!=a){var b,c,d;d=a.length;b=Array(d);for(c=0;c<d;c++)b[c]=OpenLayers.Util.getResolutionFromScale(a[c],this.units);return b}},calculateResolutions:function(a){var b,c,d=a.maxResolution;null!=a.minScale?d=OpenLayers.Util.getResolutionFromScale(a.minScale,this.units):"auto"==d&&null!=this.maxExtent&&(b=this.map.getSize(),
c=this.maxExtent.getWidth()/b.w,b=this.maxExtent.getHeight()/b.h,d=Math.max(c,b));c=a.minResolution;null!=a.maxScale?c=OpenLayers.Util.getResolutionFromScale(a.maxScale,this.units):"auto"==a.minResolution&&null!=this.minExtent&&(b=this.map.getSize(),c=this.minExtent.getWidth()/b.w,b=this.minExtent.getHeight()/b.h,c=Math.max(c,b));"number"!==typeof d&&("number"!==typeof c&&null!=this.maxExtent)&&(d=this.map.getTileSize(),d=Math.max(this.maxExtent.getWidth()/d.w,this.maxExtent.getHeight()/d.h));b=a.maxZoomLevel;
a=a.numZoomLevels;"number"===typeof c&&"number"===typeof d&&void 0===a?a=Math.floor(Math.log(d/c)/Math.log(2))+1:void 0===a&&null!=b&&(a=b+1);if(!("number"!==typeof a||0>=a||"number"!==typeof d&&"number"!==typeof c)){b=Array(a);var e=2;"number"==typeof c&&"number"==typeof d&&(e=Math.pow(d/c,1/(a-1)));var f;if("number"===typeof d)for(f=0;f<a;f++)b[f]=d/Math.pow(e,f);else for(f=0;f<a;f++)b[a-1-f]=c*Math.pow(e,f);return b}},getResolution:function(){var a=this.map.getZoom();return this.getResolutionForZoom(a)},
getExtent:function(){return this.map.calculateBounds()},getZoomForExtent:function(a,b){var c=this.map.getSize(),c=Math.max(a.getWidth()/c.w,a.getHeight()/c.h);return this.getZoomForResolution(c,b)},getDataExtent:function(){},getResolutionForZoom:function(a){a=Math.max(0,Math.min(a,this.resolutions.length-1));if(this.map.fractionalZoom){var b=Math.floor(a),c=Math.ceil(a);a=this.resolutions[b]-(a-b)*(this.resolutions[b]-this.resolutions[c])}else a=this.resolutions[Math.round(a)];return a},getZoomForResolution:function(a,
b){var c,d;if(this.map.fractionalZoom){var e=0,f=this.resolutions[e],g=this.resolutions[this.resolutions.length-1],h;c=0;for(d=this.resolutions.length;c<d;++c)if(h=this.resolutions[c],h>=a&&(f=h,e=c),h<=a){g=h;break}c=f-g;c=0<c?e+(f-a)/c:e}else{f=Number.POSITIVE_INFINITY;c=0;for(d=this.resolutions.length;c<d;c++)if(b){e=Math.abs(this.resolutions[c]-a);if(e>f)break;f=e}else if(this.resolutions[c]<a)break;c=Math.max(0,c-1)}return c},getLonLatFromViewPortPx:function(a){var b=null,c=this.map;if(null!=
a&&c.minPx){var b=c.getResolution(),d=c.getMaxExtent({restricted:!0}),b=new OpenLayers.LonLat((a.x-c.minPx.x)*b+d.left,(c.minPx.y-a.y)*b+d.top);this.wrapDateLine&&(b=b.wrapDateLine(this.maxExtent))}return b},getViewPortPxFromLonLat:function(a,b){var c=null;null!=a&&(b=b||this.map.getResolution(),c=this.map.calculateBounds(null,b),c=new OpenLayers.Pixel(1/b*(a.lon-c.left),1/b*(c.top-a.lat)));return c},setOpacity:function(a){if(a!=this.opacity){this.opacity=a;for(var b=this.div.childNodes,c=0,d=b.length;c<
d;++c){var e=b[c].firstChild||b[c],f=b[c].lastChild;f&&"iframe"===f.nodeName.toLowerCase()&&(e=f.parentNode);OpenLayers.Util.modifyDOMElement(e,null,null,null,null,null,null,a)}null!=this.map&&this.map.events.triggerEvent("changelayer",{layer:this,property:"opacity"})}},getZIndex:function(){return this.div.style.zIndex},setZIndex:function(a){this.div.style.zIndex=a},adjustBounds:function(a){if(this.gutter){var b=this.gutter*this.map.getResolution();a=new OpenLayers.Bounds(a.left-b,a.bottom-b,a.right+
b,a.top+b)}this.wrapDateLine&&(b={rightTolerance:this.getResolution(),leftTolerance:this.getResolution()},a=a.wrapDateLine(this.maxExtent,b));return a},CLASS_NAME:"OpenLayers.Layer"});OpenLayers.Layer.SphericalMercator={getExtent:function(){var a=null;return a=this.sphericalMercator?this.map.calculateBounds():OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this)},getLonLatFromViewPortPx:function(a){return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this,arguments)},getViewPortPxFromLonLat:function(a){return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this,arguments)},initMercatorParameters:function(){this.RESOLUTIONS=[];for(var a=0;a<=this.MAX_ZOOM_LEVEL;++a)this.RESOLUTIONS[a]=
156543.03390625/Math.pow(2,a);this.units="m";this.projection=this.projection||"EPSG:900913"},forwardMercator:function(){var a=new OpenLayers.Projection("EPSG:4326"),b=new OpenLayers.Projection("EPSG:900913");return function(c,d){var e=OpenLayers.Projection.transform({x:c,y:d},a,b);return new OpenLayers.LonLat(e.x,e.y)}}(),inverseMercator:function(){var a=new OpenLayers.Projection("EPSG:4326"),b=new OpenLayers.Projection("EPSG:900913");return function(c,d){var e=OpenLayers.Projection.transform({x:c,
y:d},b,a);return new OpenLayers.LonLat(e.x,e.y)}}()};OpenLayers.Layer.EventPane=OpenLayers.Class(OpenLayers.Layer,{smoothDragPan:!0,isBaseLayer:!0,isFixed:!0,pane:null,mapObject:null,initialize:function(a,b){OpenLayers.Layer.prototype.initialize.apply(this,arguments);null==this.pane&&(this.pane=OpenLayers.Util.createDiv(this.div.id+"_EventPane"))},destroy:function(){this.pane=this.mapObject=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments)},setMap:function(a){OpenLayers.Layer.prototype.setMap.apply(this,arguments);this.pane.style.zIndex=
parseInt(this.div.style.zIndex)+1;this.pane.style.display=this.div.style.display;this.pane.style.width="100%";this.pane.style.height="100%";"msie"==OpenLayers.BROWSER_NAME&&(this.pane.style.background="url("+OpenLayers.Util.getImageLocation("blank.gif")+")");this.isFixed?this.map.viewPortDiv.appendChild(this.pane):this.map.layerContainerDiv.appendChild(this.pane);this.loadMapObject();null==this.mapObject&&this.loadWarningMessage()},removeMap:function(a){this.pane&&this.pane.parentNode&&this.pane.parentNode.removeChild(this.pane);
OpenLayers.Layer.prototype.removeMap.apply(this,arguments)},loadWarningMessage:function(){this.div.style.backgroundColor="darkblue";var a=this.map.getSize(),b=Math.min(a.w,300),c=Math.min(a.h,200),b=new OpenLayers.Size(b,c),a=(new OpenLayers.Pixel(a.w/2,a.h/2)).add(-b.w/2,-b.h/2),a=OpenLayers.Util.createDiv(this.name+"_warning",a,b,null,null,null,"auto");a.style.padding="7px";a.style.backgroundColor="yellow";a.innerHTML=this.getWarningHTML();this.div.appendChild(a)},getWarningHTML:function(){return""},
display:function(a){OpenLayers.Layer.prototype.display.apply(this,arguments);this.pane.style.display=this.div.style.display},setZIndex:function(a){OpenLayers.Layer.prototype.setZIndex.apply(this,arguments);this.pane.style.zIndex=parseInt(this.div.style.zIndex)+1},moveByPx:function(a,b){OpenLayers.Layer.prototype.moveByPx.apply(this,arguments);this.dragPanMapObject?this.dragPanMapObject(a,-b):this.moveTo(this.map.getCachedCenter())},moveTo:function(a,b,c){OpenLayers.Layer.prototype.moveTo.apply(this,
arguments);if(null!=this.mapObject){var d=this.map.getCenter(),e=this.map.getZoom();if(null!=d){var f=this.getMapObjectCenter(),f=this.getOLLonLatFromMapObjectLonLat(f),g=this.getMapObjectZoom(),g=this.getOLZoomFromMapObjectZoom(g);d.equals(f)&&e==g||(!b&&f&&this.dragPanMapObject&&this.smoothDragPan?(e=this.map.getViewPortPxFromLonLat(f),d=this.map.getViewPortPxFromLonLat(d),this.dragPanMapObject(d.x-e.x,e.y-d.y)):(d=this.getMapObjectLonLatFromOLLonLat(d),e=this.getMapObjectZoomFromOLZoom(e),this.setMapObjectCenter(d,
e,c)))}}},getLonLatFromViewPortPx:function(a){var b=null;null!=this.mapObject&&null!=this.getMapObjectCenter()&&(a=this.getMapObjectPixelFromOLPixel(a),a=this.getMapObjectLonLatFromMapObjectPixel(a),b=this.getOLLonLatFromMapObjectLonLat(a));return b},getViewPortPxFromLonLat:function(a){var b=null;null!=this.mapObject&&null!=this.getMapObjectCenter()&&(a=this.getMapObjectLonLatFromOLLonLat(a),a=this.getMapObjectPixelFromMapObjectLonLat(a),b=this.getOLPixelFromMapObjectPixel(a));return b},getOLLonLatFromMapObjectLonLat:function(a){var b=
null;null!=a&&(b=this.getLongitudeFromMapObjectLonLat(a),a=this.getLatitudeFromMapObjectLonLat(a),b=new OpenLayers.LonLat(b,a));return b},getMapObjectLonLatFromOLLonLat:function(a){var b=null;null!=a&&(b=this.getMapObjectLonLatFromLonLat(a.lon,a.lat));return b},getOLPixelFromMapObjectPixel:function(a){var b=null;null!=a&&(b=this.getXFromMapObjectPixel(a),a=this.getYFromMapObjectPixel(a),b=new OpenLayers.Pixel(b,a));return b},getMapObjectPixelFromOLPixel:function(a){var b=null;null!=a&&(b=this.getMapObjectPixelFromXY(a.x,
a.y));return b},CLASS_NAME:"OpenLayers.Layer.EventPane"});OpenLayers.Layer.FixedZoomLevels=OpenLayers.Class({initialize:function(){},initResolutions:function(){for(var a=["minZoomLevel","maxZoomLevel","numZoomLevels"],b=0,c=a.length;b<c;b++){var d=a[b];this[d]=null!=this.options[d]?this.options[d]:this.map[d]}if(null==this.minZoomLevel||this.minZoomLevel<this.MIN_ZOOM_LEVEL)this.minZoomLevel=this.MIN_ZOOM_LEVEL;a=this.MAX_ZOOM_LEVEL-this.minZoomLevel+1;b=null==this.options.numZoomLevels&&null!=this.options.maxZoomLevel||null==this.numZoomLevels&&null!=this.maxZoomLevel?
this.maxZoomLevel-this.minZoomLevel+1:this.numZoomLevels;this.numZoomLevels=null!=b?Math.min(b,a):a;this.maxZoomLevel=this.minZoomLevel+this.numZoomLevels-1;if(null!=this.RESOLUTIONS){a=0;this.resolutions=[];for(b=this.minZoomLevel;b<=this.maxZoomLevel;b++)this.resolutions[a++]=this.RESOLUTIONS[b];this.maxResolution=this.resolutions[0];this.minResolution=this.resolutions[this.resolutions.length-1]}},getResolution:function(){if(null!=this.resolutions)return OpenLayers.Layer.prototype.getResolution.apply(this,
arguments);var a=null,b=this.map.getSize(),c=this.getExtent();null!=b&&null!=c&&(a=Math.max(c.getWidth()/b.w,c.getHeight()/b.h));return a},getExtent:function(){var a=this.map.getSize(),b=this.getLonLatFromViewPortPx({x:0,y:0}),a=this.getLonLatFromViewPortPx({x:a.w,y:a.h});return null!=b&&null!=a?new OpenLayers.Bounds(b.lon,a.lat,a.lon,b.lat):null},getZoomForResolution:function(a){if(null!=this.resolutions)return OpenLayers.Layer.prototype.getZoomForResolution.apply(this,arguments);var b=OpenLayers.Layer.prototype.getExtent.apply(this,
[]);return this.getZoomForExtent(b)},getOLZoomFromMapObjectZoom:function(a){var b=null;null!=a&&(b=a-this.minZoomLevel,this.map.baseLayer!==this&&(b=this.map.baseLayer.getZoomForResolution(this.getResolutionForZoom(b))));return b},getMapObjectZoomFromOLZoom:function(a){var b=null;null!=a&&(b=a+this.minZoomLevel,this.map.baseLayer!==this&&(b=this.getZoomForResolution(this.map.baseLayer.getResolutionForZoom(b))));return b},CLASS_NAME:"OpenLayers.Layer.FixedZoomLevels"});OpenLayers.Layer.Google=OpenLayers.Class(OpenLayers.Layer.EventPane,OpenLayers.Layer.FixedZoomLevels,{MIN_ZOOM_LEVEL:0,MAX_ZOOM_LEVEL:21,RESOLUTIONS:[1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,6.866455078125E-4,3.4332275390625E-4,1.71661376953125E-4,8.58306884765625E-5,4.291534423828125E-5,2.145767211914062E-5,1.072883605957031E-5,5.36441802978515E-6,2.68220901489257E-6,1.341104507446289E-6,6.705522537231445E-7],
type:null,wrapDateLine:!0,sphericalMercator:!1,version:null,initialize:function(a,b){b=b||{};b.version||(b.version="function"===typeof GMap2?"2":"3");var c=OpenLayers.Layer.Google["v"+b.version.replace(/\./g,"_")];if(c)OpenLayers.Util.applyDefaults(b,c);else throw"Unsupported Google Maps API version: "+b.version;OpenLayers.Util.applyDefaults(b,c.DEFAULTS);b.maxExtent&&(b.maxExtent=b.maxExtent.clone());OpenLayers.Layer.EventPane.prototype.initialize.apply(this,[a,b]);OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
[a,b]);this.sphericalMercator&&(OpenLayers.Util.extend(this,OpenLayers.Layer.SphericalMercator),this.initMercatorParameters())},clone:function(){return new OpenLayers.Layer.Google(this.name,this.getOptions())},setVisibility:function(a){var b=null==this.opacity?1:this.opacity;OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this,arguments);this.setOpacity(b)},display:function(a){this._dragging||this.setGMapVisibility(a);OpenLayers.Layer.EventPane.prototype.display.apply(this,arguments)},moveTo:function(a,
b,c){this._dragging=c;OpenLayers.Layer.EventPane.prototype.moveTo.apply(this,arguments);delete this._dragging},setOpacity:function(a){a!==this.opacity&&(null!=this.map&&this.map.events.triggerEvent("changelayer",{layer:this,property:"opacity"}),this.opacity=a);if(this.getVisibility()){var b=this.getMapContainer();OpenLayers.Util.modifyDOMElement(b,null,null,null,null,null,null,a)}},destroy:function(){if(this.map){this.setGMapVisibility(!1);var a=OpenLayers.Layer.Google.cache[this.map.id];a&&1>=a.count&&
this.removeGMapElements()}OpenLayers.Layer.EventPane.prototype.destroy.apply(this,arguments)},removeGMapElements:function(){var a=OpenLayers.Layer.Google.cache[this.map.id];if(a){var b=this.mapObject&&this.getMapContainer();b&&b.parentNode&&b.parentNode.removeChild(b);(b=a.termsOfUse)&&b.parentNode&&b.parentNode.removeChild(b);(a=a.poweredBy)&&a.parentNode&&a.parentNode.removeChild(a);this.mapObject&&(window.google&&google.maps&&google.maps.event&&google.maps.event.clearListeners)&&google.maps.event.clearListeners(this.mapObject,
"tilesloaded")}},removeMap:function(a){this.visibility&&this.mapObject&&this.setGMapVisibility(!1);var b=OpenLayers.Layer.Google.cache[a.id];b&&(1>=b.count?(this.removeGMapElements(),delete OpenLayers.Layer.Google.cache[a.id]):--b.count);delete this.termsOfUse;delete this.poweredBy;delete this.mapObject;delete this.dragObject;OpenLayers.Layer.EventPane.prototype.removeMap.apply(this,arguments)},getOLBoundsFromMapObjectBounds:function(a){var b=null;null!=a&&(b=a.getSouthWest(),a=a.getNorthEast(),this.sphericalMercator?
(b=this.forwardMercator(b.lng(),b.lat()),a=this.forwardMercator(a.lng(),a.lat())):(b=new OpenLayers.LonLat(b.lng(),b.lat()),a=new OpenLayers.LonLat(a.lng(),a.lat())),b=new OpenLayers.Bounds(b.lon,b.lat,a.lon,a.lat));return b},getWarningHTML:function(){return OpenLayers.i18n("googleWarning")},getMapObjectCenter:function(){return this.mapObject.getCenter()},getMapObjectZoom:function(){return this.mapObject.getZoom()},getLongitudeFromMapObjectLonLat:function(a){return this.sphericalMercator?this.forwardMercator(a.lng(),
a.lat()).lon:a.lng()},getLatitudeFromMapObjectLonLat:function(a){return this.sphericalMercator?this.forwardMercator(a.lng(),a.lat()).lat:a.lat()},getXFromMapObjectPixel:function(a){return a.x},getYFromMapObjectPixel:function(a){return a.y},CLASS_NAME:"OpenLayers.Layer.Google"});OpenLayers.Layer.Google.cache={};
OpenLayers.Layer.Google.v2={termsOfUse:null,poweredBy:null,dragObject:null,loadMapObject:function(){this.type||(this.type=G_NORMAL_MAP);var a,b,c,d=OpenLayers.Layer.Google.cache[this.map.id];if(d)a=d.mapObject,b=d.termsOfUse,c=d.poweredBy,++d.count;else{var d=this.map.viewPortDiv,e=document.createElement("div");e.id=this.map.id+"_GMap2Container";e.style.position="absolute";e.style.width="100%";e.style.height="100%";d.appendChild(e);try{a=new GMap2(e),b=e.lastChild,d.appendChild(b),b.style.zIndex=
"1100",b.style.right="",b.style.bottom="",b.className="olLayerGoogleCopyright",c=e.lastChild,d.appendChild(c),c.style.zIndex="1100",c.style.right="",c.style.bottom="",c.className="olLayerGooglePoweredBy gmnoprint"}catch(f){throw f;}OpenLayers.Layer.Google.cache[this.map.id]={mapObject:a,termsOfUse:b,poweredBy:c,count:1}}this.mapObject=a;this.termsOfUse=b;this.poweredBy=c;-1===OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),this.type)&&this.mapObject.addMapType(this.type);"function"==typeof a.getDragObject?
this.dragObject=a.getDragObject():this.dragPanMapObject=null;!1===this.isBaseLayer&&this.setGMapVisibility("none"!==this.div.style.display)},onMapResize:function(){if(this.visibility&&this.mapObject.isLoaded())this.mapObject.checkResize();else{if(!this._resized)var a=this,b=GEvent.addListener(this.mapObject,"load",function(){GEvent.removeListener(b);delete a._resized;a.mapObject.checkResize();a.moveTo(a.map.getCenter(),a.map.getZoom())});this._resized=!0}},setGMapVisibility:function(a){var b=OpenLayers.Layer.Google.cache[this.map.id];
if(b){var c=this.mapObject.getContainer();!0===a?(this.mapObject.setMapType(this.type),c.style.display="",this.termsOfUse.style.left="",this.termsOfUse.style.display="",this.poweredBy.style.display="",b.displayed=this.id):(b.displayed===this.id&&delete b.displayed,b.displayed||(c.style.display="none",this.termsOfUse.style.display="none",this.termsOfUse.style.left="-9999px",this.poweredBy.style.display="none"))}},getMapContainer:function(){return this.mapObject.getContainer()},getMapObjectBoundsFromOLBounds:function(a){var b=
null;null!=a&&(b=this.sphericalMercator?this.inverseMercator(a.bottom,a.left):new OpenLayers.LonLat(a.bottom,a.left),a=this.sphericalMercator?this.inverseMercator(a.top,a.right):new OpenLayers.LonLat(a.top,a.right),b=new GLatLngBounds(new GLatLng(b.lat,b.lon),new GLatLng(a.lat,a.lon)));return b},setMapObjectCenter:function(a,b){this.mapObject.setCenter(a,b)},dragPanMapObject:function(a,b){this.dragObject.moveBy(new GSize(-a,b))},getMapObjectLonLatFromMapObjectPixel:function(a){return this.mapObject.fromContainerPixelToLatLng(a)},
getMapObjectPixelFromMapObjectLonLat:function(a){return this.mapObject.fromLatLngToContainerPixel(a)},getMapObjectZoomFromMapObjectBounds:function(a){return this.mapObject.getBoundsZoomLevel(a)},getMapObjectLonLatFromLonLat:function(a,b){var c;this.sphericalMercator?(c=this.inverseMercator(a,b),c=new GLatLng(c.lat,c.lon)):c=new GLatLng(b,a);return c},getMapObjectPixelFromXY:function(a,b){return new GPoint(a,b)}};OpenLayers.Format.XML=OpenLayers.Class(OpenLayers.Format,{namespaces:null,namespaceAlias:null,defaultPrefix:null,readers:{},writers:{},xmldom:null,initialize:function(a){window.ActiveXObject&&(this.xmldom=new ActiveXObject("Microsoft.XMLDOM"));OpenLayers.Format.prototype.initialize.apply(this,[a]);this.namespaces=OpenLayers.Util.extend({},this.namespaces);this.namespaceAlias={};for(var b in this.namespaces)this.namespaceAlias[this.namespaces[b]]=b},destroy:function(){this.xmldom=null;OpenLayers.Format.prototype.destroy.apply(this,
arguments)},setNamespace:function(a,b){this.namespaces[a]=b;this.namespaceAlias[b]=a},read:function(a){var b=a.indexOf("<");0<b&&(a=a.substring(b));b=OpenLayers.Util.Try(OpenLayers.Function.bind(function(){var b;b=window.ActiveXObject&&!this.xmldom?new ActiveXObject("Microsoft.XMLDOM"):this.xmldom;b.loadXML(a);return b},this),function(){return(new DOMParser).parseFromString(a,"text/xml")},function(){var b=new XMLHttpRequest;b.open("GET","data:text/xml;charset=utf-8,"+encodeURIComponent(a),!1);b.overrideMimeType&&
b.overrideMimeType("text/xml");b.send(null);return b.responseXML});this.keepData&&(this.data=b);return b},write:function(a){if(this.xmldom)a=a.xml;else{var b=new XMLSerializer;if(1==a.nodeType){var c=document.implementation.createDocument("","",null);c.importNode&&(a=c.importNode(a,!0));c.appendChild(a);a=b.serializeToString(c)}else a=b.serializeToString(a)}return a},createElementNS:function(a,b){return this.xmldom?"string"==typeof a?this.xmldom.createNode(1,b,a):this.xmldom.createNode(1,b,""):document.createElementNS(a,
b)},createDocumentFragment:function(){return this.xmldom?this.xmldom.createDocumentFragment():document.createDocumentFragment()},createTextNode:function(a){"string"!==typeof a&&(a=String(a));return this.xmldom?this.xmldom.createTextNode(a):document.createTextNode(a)},getElementsByTagNameNS:function(a,b,c){var d=[];if(a.getElementsByTagNameNS)d=a.getElementsByTagNameNS(b,c);else{a=a.getElementsByTagName("*");for(var e,f,g=0,h=a.length;g<h;++g)if(e=a[g],f=e.prefix?e.prefix+":"+c:c,"*"==c||f==e.nodeName)"*"!=
b&&b!=e.namespaceURI||d.push(e)}return d},getAttributeNodeNS:function(a,b,c){var d=null;if(a.getAttributeNodeNS)d=a.getAttributeNodeNS(b,c);else{a=a.attributes;for(var e,f,g=0,h=a.length;g<h;++g)if(e=a[g],e.namespaceURI==b&&(f=e.prefix?e.prefix+":"+c:c,f==e.nodeName)){d=e;break}}return d},getAttributeNS:function(a,b,c){var d="";if(a.getAttributeNS)d=a.getAttributeNS(b,c)||"";else if(a=this.getAttributeNodeNS(a,b,c))d=a.nodeValue;return d},getChildValue:function(a,b){var c=b||"";if(a)for(var d=a.firstChild;d;d=
d.nextSibling)switch(d.nodeType){case 3:case 4:c+=d.nodeValue}return c},isSimpleContent:function(a){var b=!0;for(a=a.firstChild;a;a=a.nextSibling)if(1===a.nodeType){b=!1;break}return b},contentType:function(a){var b=!1,c=!1,d=OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;for(a=a.firstChild;a;a=a.nextSibling){switch(a.nodeType){case 1:c=!0;break;case 8:break;default:b=!0}if(c&&b)break}if(c&&b)d=OpenLayers.Format.XML.CONTENT_TYPE.MIXED;else{if(c)return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;if(b)return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE}return d},
hasAttributeNS:function(a,b,c){var d=!1;return d=a.hasAttributeNS?a.hasAttributeNS(b,c):!!this.getAttributeNodeNS(a,b,c)},setAttributeNS:function(a,b,c,d){if(a.setAttributeNS)a.setAttributeNS(b,c,d);else if(this.xmldom)b?(b=a.ownerDocument.createNode(2,c,b),b.nodeValue=d,a.setAttributeNode(b)):a.setAttribute(c,d);else throw"setAttributeNS not implemented";},createElementNSPlus:function(a,b){b=b||{};var c=b.uri||this.namespaces[b.prefix];c||(c=a.indexOf(":"),c=this.namespaces[a.substring(0,c)]);c||
(c=this.namespaces[this.defaultPrefix]);c=this.createElementNS(c,a);b.attributes&&this.setAttributes(c,b.attributes);var d=b.value;null!=d&&c.appendChild(this.createTextNode(d));return c},setAttributes:function(a,b){var c,d,e;for(e in b)null!=b[e]&&b[e].toString&&(c=b[e].toString(),d=this.namespaces[e.substring(0,e.indexOf(":"))]||null,this.setAttributeNS(a,d,e,c))},readNode:function(a,b){b||(b={});var c=this.readers[a.namespaceURI?this.namespaceAlias[a.namespaceURI]:this.defaultPrefix];if(c){var d=
a.localName||a.nodeName.split(":").pop();(c=c[d]||c["*"])&&c.apply(this,[a,b])}return b},readChildNodes:function(a,b){b||(b={});for(var c=a.childNodes,d,e=0,f=c.length;e<f;++e)d=c[e],1==d.nodeType&&this.readNode(d,b);return b},writeNode:function(a,b,c){var d,e=a.indexOf(":");0<e?(d=a.substring(0,e),a=a.substring(e+1)):d=c?this.namespaceAlias[c.namespaceURI]:this.defaultPrefix;b=this.writers[d][a].apply(this,[b]);c&&c.appendChild(b);return b},getChildEl:function(a,b,c){return a&&this.getThisOrNextEl(a.firstChild,
b,c)},getNextEl:function(a,b,c){return a&&this.getThisOrNextEl(a.nextSibling,b,c)},getThisOrNextEl:function(a,b,c){a:for(;a;a=a.nextSibling)switch(a.nodeType){case 1:if(!(b&&b!==(a.localName||a.nodeName.split(":").pop())||c&&c!==a.namespaceURI))break a;a=null;break a;case 3:if(/^\s*$/.test(a.nodeValue))break;case 4:case 6:case 12:case 10:case 11:a=null;break a}return a||null},lookupNamespaceURI:function(a,b){var c=null;if(a)if(a.lookupNamespaceURI)c=a.lookupNamespaceURI(b);else a:switch(a.nodeType){case 1:if(null!==
a.namespaceURI&&a.prefix===b){c=a.namespaceURI;break a}if(c=a.attributes.length)for(var d,e=0;e<c;++e)if(d=a.attributes[e],"xmlns"===d.prefix&&d.name==="xmlns:"+b){c=d.value||null;break a}else if("xmlns"===d.name&&null===b){c=d.value||null;break a}c=this.lookupNamespaceURI(a.parentNode,b);break a;case 2:c=this.lookupNamespaceURI(a.ownerElement,b);break a;case 9:c=this.lookupNamespaceURI(a.documentElement,b);break a;case 6:case 12:case 10:case 11:break a;default:c=this.lookupNamespaceURI(a.parentNode,
b)}return c},getXMLDoc:function(){OpenLayers.Format.XML.document||this.xmldom||(document.implementation&&document.implementation.createDocument?OpenLayers.Format.XML.document=document.implementation.createDocument("","",null):!this.xmldom&&window.ActiveXObject&&(this.xmldom=new ActiveXObject("Microsoft.XMLDOM")));return OpenLayers.Format.XML.document||this.xmldom},CLASS_NAME:"OpenLayers.Format.XML"});OpenLayers.Format.XML.CONTENT_TYPE={EMPTY:0,SIMPLE:1,COMPLEX:2,MIXED:3};
OpenLayers.Format.XML.lookupNamespaceURI=OpenLayers.Function.bind(OpenLayers.Format.XML.prototype.lookupNamespaceURI,OpenLayers.Format.XML.prototype);OpenLayers.Format.XML.document=null;OpenLayers.Format.WFST=function(a){a=OpenLayers.Util.applyDefaults(a,OpenLayers.Format.WFST.DEFAULTS);var b=OpenLayers.Format.WFST["v"+a.version.replace(/\./g,"_")];if(!b)throw"Unsupported WFST version: "+a.version;return new b(a)};OpenLayers.Format.WFST.DEFAULTS={version:"1.0.0"};OpenLayers.Feature=OpenLayers.Class({layer:null,id:null,lonlat:null,data:null,marker:null,popupClass:null,popup:null,initialize:function(a,b,c){this.layer=a;this.lonlat=b;this.data=null!=c?c:{};this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},destroy:function(){null!=this.layer&&null!=this.layer.map&&null!=this.popup&&this.layer.map.removePopup(this.popup);null!=this.layer&&null!=this.marker&&this.layer.removeMarker(this.marker);this.data=this.lonlat=this.id=this.layer=null;null!=this.marker&&
(this.destroyMarker(this.marker),this.marker=null);null!=this.popup&&(this.destroyPopup(this.popup),this.popup=null)},onScreen:function(){var a=!1;null!=this.layer&&null!=this.layer.map&&(a=this.layer.map.getExtent().containsLonLat(this.lonlat));return a},createMarker:function(){null!=this.lonlat&&(this.marker=new OpenLayers.Marker(this.lonlat,this.data.icon));return this.marker},destroyMarker:function(){this.marker.destroy()},createPopup:function(a){null!=this.lonlat&&(this.popup||(this.popup=new (this.popupClass?
this.popupClass:OpenLayers.Popup.Anchored)(this.id+"_popup",this.lonlat,this.data.popupSize,this.data.popupContentHTML,this.marker?this.marker.icon:null,a)),null!=this.data.overflow&&(this.popup.contentDiv.style.overflow=this.data.overflow),this.popup.feature=this);return this.popup},destroyPopup:function(){this.popup&&(this.popup.feature=null,this.popup.destroy(),this.popup=null)},CLASS_NAME:"OpenLayers.Feature"});OpenLayers.State={UNKNOWN:"Unknown",INSERT:"Insert",UPDATE:"Update",DELETE:"Delete"};
OpenLayers.Feature.Vector=OpenLayers.Class(OpenLayers.Feature,{fid:null,geometry:null,attributes:null,bounds:null,state:null,style:null,url:null,renderIntent:"default",modified:null,initialize:function(a,b,c){OpenLayers.Feature.prototype.initialize.apply(this,[null,null,b]);this.lonlat=null;this.geometry=a?a:null;this.state=null;this.attributes={};b&&(this.attributes=OpenLayers.Util.extend(this.attributes,b));this.style=c?c:null},destroy:function(){this.layer&&(this.layer.removeFeatures(this),this.layer=
null);this.modified=this.geometry=null;OpenLayers.Feature.prototype.destroy.apply(this,arguments)},clone:function(){return new OpenLayers.Feature.Vector(this.geometry?this.geometry.clone():null,this.attributes,this.style)},onScreen:function(a){var b=!1;this.layer&&this.layer.map&&(b=this.layer.map.getExtent(),a?(a=this.geometry.getBounds(),b=b.intersectsBounds(a)):b=b.toGeometry().intersects(this.geometry));return b},getVisibility:function(){return!(this.style&&"none"==this.style.display||!this.layer||
this.layer&&this.layer.styleMap&&"none"==this.layer.styleMap.createSymbolizer(this,this.renderIntent).display||this.layer&&!this.layer.getVisibility())},createMarker:function(){return null},destroyMarker:function(){},createPopup:function(){return null},atPoint:function(a,b,c){var d=!1;this.geometry&&(d=this.geometry.atPoint(a,b,c));return d},destroyPopup:function(){},move:function(a){if(this.layer&&this.geometry.move){a="OpenLayers.LonLat"==a.CLASS_NAME?this.layer.getViewPortPxFromLonLat(a):a;var b=
this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat()),c=this.layer.map.getResolution();this.geometry.move(c*(a.x-b.x),c*(b.y-a.y));this.layer.drawFeature(this);return b}},toState:function(a){if(a==OpenLayers.State.UPDATE)switch(this.state){case OpenLayers.State.UNKNOWN:case OpenLayers.State.DELETE:this.state=a}else if(a==OpenLayers.State.INSERT)switch(this.state){case OpenLayers.State.UNKNOWN:break;default:this.state=a}else if(a==OpenLayers.State.DELETE)switch(this.state){case OpenLayers.State.UNKNOWN:case OpenLayers.State.UPDATE:this.state=
a}else a==OpenLayers.State.UNKNOWN&&(this.state=a)},CLASS_NAME:"OpenLayers.Feature.Vector"});
OpenLayers.Feature.Vector.style={"default":{fillColor:"#ee9900",fillOpacity:0.4,hoverFillColor:"white",hoverFillOpacity:0.8,strokeColor:"#ee9900",strokeOpacity:1,strokeWidth:1,strokeLinecap:"round",strokeDashstyle:"solid",hoverStrokeColor:"red",hoverStrokeOpacity:1,hoverStrokeWidth:0.2,pointRadius:6,hoverPointRadius:1,hoverPointUnit:"%",pointerEvents:"visiblePainted",cursor:"inherit",fontColor:"#000000",labelAlign:"cm",labelOutlineColor:"white",labelOutlineWidth:3},select:{fillColor:"blue",fillOpacity:0.4,
hoverFillColor:"white",hoverFillOpacity:0.8,strokeColor:"blue",strokeOpacity:1,strokeWidth:2,strokeLinecap:"round",strokeDashstyle:"solid",hoverStrokeColor:"red",hoverStrokeOpacity:1,hoverStrokeWidth:0.2,pointRadius:6,hoverPointRadius:1,hoverPointUnit:"%",pointerEvents:"visiblePainted",cursor:"pointer",fontColor:"#000000",labelAlign:"cm",labelOutlineColor:"white",labelOutlineWidth:3},temporary:{fillColor:"#66cccc",fillOpacity:0.2,hoverFillColor:"white",hoverFillOpacity:0.8,strokeColor:"#66cccc",strokeOpacity:1,
strokeLinecap:"round",strokeWidth:2,strokeDashstyle:"solid",hoverStrokeColor:"red",hoverStrokeOpacity:1,hoverStrokeWidth:0.2,pointRadius:6,hoverPointRadius:1,hoverPointUnit:"%",pointerEvents:"visiblePainted",cursor:"inherit",fontColor:"#000000",labelAlign:"cm",labelOutlineColor:"white",labelOutlineWidth:3},"delete":{display:"none"}};OpenLayers.Style=OpenLayers.Class({id:null,name:null,title:null,description:null,layerName:null,isDefault:!1,rules:null,context:null,defaultStyle:null,defaultsPerSymbolizer:!1,propertyStyles:null,initialize:function(a,b){OpenLayers.Util.extend(this,b);this.rules=[];b&&b.rules&&this.addRules(b.rules);this.setDefaultStyle(a||OpenLayers.Feature.Vector.style["default"]);this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},destroy:function(){for(var a=0,b=this.rules.length;a<b;a++)this.rules[a].destroy(),
this.rules[a]=null;this.defaultStyle=this.rules=null},createSymbolizer:function(a){for(var b=this.defaultsPerSymbolizer?{}:this.createLiterals(OpenLayers.Util.extend({},this.defaultStyle),a),c=this.rules,d,e=[],f=!1,g=0,h=c.length;g<h;g++)d=c[g],d.evaluate(a)&&(d instanceof OpenLayers.Rule&&d.elseFilter?e.push(d):(f=!0,this.applySymbolizer(d,b,a)));if(!1==f&&0<e.length)for(f=!0,g=0,h=e.length;g<h;g++)this.applySymbolizer(e[g],b,a);0<c.length&&!1==f&&(b.display="none");null!=b.label&&"string"!==typeof b.label&&
(b.label=String(b.label));return b},applySymbolizer:function(a,b,c){var d=c.geometry?this.getSymbolizerPrefix(c.geometry):OpenLayers.Style.SYMBOLIZER_PREFIXES[0];a=a.symbolizer[d]||a.symbolizer;!0===this.defaultsPerSymbolizer&&(d=this.defaultStyle,OpenLayers.Util.applyDefaults(a,{pointRadius:d.pointRadius}),!0!==a.stroke&&!0!==a.graphic||OpenLayers.Util.applyDefaults(a,{strokeWidth:d.strokeWidth,strokeColor:d.strokeColor,strokeOpacity:d.strokeOpacity,strokeDashstyle:d.strokeDashstyle,strokeLinecap:d.strokeLinecap}),
!0!==a.fill&&!0!==a.graphic||OpenLayers.Util.applyDefaults(a,{fillColor:d.fillColor,fillOpacity:d.fillOpacity}),!0===a.graphic&&OpenLayers.Util.applyDefaults(a,{pointRadius:this.defaultStyle.pointRadius,externalGraphic:this.defaultStyle.externalGraphic,graphicName:this.defaultStyle.graphicName,graphicOpacity:this.defaultStyle.graphicOpacity,graphicWidth:this.defaultStyle.graphicWidth,graphicHeight:this.defaultStyle.graphicHeight,graphicXOffset:this.defaultStyle.graphicXOffset,graphicYOffset:this.defaultStyle.graphicYOffset}));
return this.createLiterals(OpenLayers.Util.extend(b,a),c)},createLiterals:function(a,b){var c=OpenLayers.Util.extend({},b.attributes||b.data);OpenLayers.Util.extend(c,this.context);for(var d in this.propertyStyles)a[d]=OpenLayers.Style.createLiteral(a[d],c,b,d);return a},findPropertyStyles:function(){var a={};this.addPropertyStyles(a,this.defaultStyle);for(var b=this.rules,c,d,e=0,f=b.length;e<f;e++){c=b[e].symbolizer;for(var g in c)if(d=c[g],"object"==typeof d)this.addPropertyStyles(a,d);else{this.addPropertyStyles(a,
c);break}}return a},addPropertyStyles:function(a,b){var c,d;for(d in b)c=b[d],"string"==typeof c&&c.match(/\$\{\w+\}/)&&(a[d]=!0);return a},addRules:function(a){Array.prototype.push.apply(this.rules,a);this.propertyStyles=this.findPropertyStyles()},setDefaultStyle:function(a){this.defaultStyle=a;this.propertyStyles=this.findPropertyStyles()},getSymbolizerPrefix:function(a){for(var b=OpenLayers.Style.SYMBOLIZER_PREFIXES,c=0,d=b.length;c<d;c++)if(-1!=a.CLASS_NAME.indexOf(b[c]))return b[c]},clone:function(){var a=
OpenLayers.Util.extend({},this);if(this.rules){a.rules=[];for(var b=0,c=this.rules.length;b<c;++b)a.rules.push(this.rules[b].clone())}a.context=this.context&&OpenLayers.Util.extend({},this.context);b=OpenLayers.Util.extend({},this.defaultStyle);return new OpenLayers.Style(b,a)},CLASS_NAME:"OpenLayers.Style"});OpenLayers.Style.createLiteral=function(a,b,c,d){"string"==typeof a&&-1!=a.indexOf("${")&&(a=OpenLayers.String.format(a,b,[c,d]),a=isNaN(a)||!a?a:parseFloat(a));return a};
OpenLayers.Style.SYMBOLIZER_PREFIXES=["Point","Line","Polygon","Text","Raster"];OpenLayers.Filter=OpenLayers.Class({initialize:function(a){OpenLayers.Util.extend(this,a)},destroy:function(){},evaluate:function(a){return!0},clone:function(){return null},toString:function(){return OpenLayers.Format&&OpenLayers.Format.CQL?OpenLayers.Format.CQL.prototype.write(this):Object.prototype.toString.call(this)},CLASS_NAME:"OpenLayers.Filter"});OpenLayers.Filter.Spatial=OpenLayers.Class(OpenLayers.Filter,{type:null,property:null,value:null,distance:null,distanceUnits:null,evaluate:function(a){var b=!1;switch(this.type){case OpenLayers.Filter.Spatial.BBOX:case OpenLayers.Filter.Spatial.INTERSECTS:if(a.geometry){var c=this.value;"OpenLayers.Bounds"==this.value.CLASS_NAME&&(c=this.value.toGeometry());a.geometry.intersects(c)&&(b=!0)}break;default:throw Error("evaluate is not implemented for this filter type.");}return b},clone:function(){var a=
OpenLayers.Util.applyDefaults({value:this.value&&this.value.clone&&this.value.clone()},this);return new OpenLayers.Filter.Spatial(a)},CLASS_NAME:"OpenLayers.Filter.Spatial"});OpenLayers.Filter.Spatial.BBOX="BBOX";OpenLayers.Filter.Spatial.INTERSECTS="INTERSECTS";OpenLayers.Filter.Spatial.DWITHIN="DWITHIN";OpenLayers.Filter.Spatial.WITHIN="WITHIN";OpenLayers.Filter.Spatial.CONTAINS="CONTAINS";OpenLayers.Filter.FeatureId=OpenLayers.Class(OpenLayers.Filter,{fids:null,type:"FID",initialize:function(a){this.fids=[];OpenLayers.Filter.prototype.initialize.apply(this,[a])},evaluate:function(a){for(var b=0,c=this.fids.length;b<c;b++)if((a.fid||a.id)==this.fids[b])return!0;return!1},clone:function(){var a=new OpenLayers.Filter.FeatureId;OpenLayers.Util.extend(a,this);a.fids=this.fids.slice();return a},CLASS_NAME:"OpenLayers.Filter.FeatureId"});OpenLayers.Format.WFST.v1=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance",wfs:"http://www.opengis.net/wfs",gml:"http://www.opengis.net/gml",ogc:"http://www.opengis.net/ogc",ows:"http://www.opengis.net/ows"},defaultPrefix:"wfs",version:null,schemaLocations:null,srsName:null,extractAttributes:!0,xy:!0,stateName:null,initialize:function(a){this.stateName={};this.stateName[OpenLayers.State.INSERT]="wfs:Insert";this.stateName[OpenLayers.State.UPDATE]=
"wfs:Update";this.stateName[OpenLayers.State.DELETE]="wfs:Delete";OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},getSrsName:function(a,b){var c=b&&b.srsName;c||(c=a&&a.layer?a.layer.projection.getCode():this.srsName);return c},read:function(a,b){b=b||{};OpenLayers.Util.applyDefaults(b,{output:"features"});"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var c={};a&&this.readNode(a,c,!0);c.features&&"features"===b.output&&
(c=c.features);return c},readers:{wfs:{FeatureCollection:function(a,b){b.features=[];this.readChildNodes(a,b)}}},write:function(a,b){var c=this.writeNode("wfs:Transaction",{features:a,options:b}),d=this.schemaLocationAttr();d&&this.setAttributeNS(c,this.namespaces.xsi,"xsi:schemaLocation",d);return OpenLayers.Format.XML.prototype.write.apply(this,[c])},writers:{wfs:{GetFeature:function(a){var b=this.createElementNSPlus("wfs:GetFeature",{attributes:{service:"WFS",version:this.version,handle:a&&a.handle,
outputFormat:a&&a.outputFormat,maxFeatures:a&&a.maxFeatures,"xsi:schemaLocation":this.schemaLocationAttr(a)}});if("string"==typeof this.featureType)this.writeNode("Query",a,b);else for(var c=0,d=this.featureType.length;c<d;c++)a.featureType=this.featureType[c],this.writeNode("Query",a,b);return b},Transaction:function(a){a=a||{};var b=a.options||{},c=this.createElementNSPlus("wfs:Transaction",{attributes:{service:"WFS",version:this.version,handle:b.handle}}),d,e=a.features;if(e){!0===b.multi&&OpenLayers.Util.extend(this.geometryTypes,
{"OpenLayers.Geometry.Point":"MultiPoint","OpenLayers.Geometry.LineString":!0===this.multiCurve?"MultiCurve":"MultiLineString","OpenLayers.Geometry.Polygon":!0===this.multiSurface?"MultiSurface":"MultiPolygon"});var f,g;a=0;for(d=e.length;a<d;++a)g=e[a],(f=this.stateName[g.state])&&this.writeNode(f,{feature:g,options:b},c);!0===b.multi&&this.setGeometryTypes()}if(b.nativeElements)for(a=0,d=b.nativeElements.length;a<d;++a)this.writeNode("wfs:Native",b.nativeElements[a],c);return c},Native:function(a){return this.createElementNSPlus("wfs:Native",
{attributes:{vendorId:a.vendorId,safeToIgnore:a.safeToIgnore},value:a.value})},Insert:function(a){var b=a.feature;a=a.options;a=this.createElementNSPlus("wfs:Insert",{attributes:{handle:a&&a.handle}});this.srsName=this.getSrsName(b);this.writeNode("feature:_typeName",b,a);return a},Update:function(a){var b=a.feature;a=a.options;a=this.createElementNSPlus("wfs:Update",{attributes:{handle:a&&a.handle,typeName:(this.featureNS?this.featurePrefix+":":"")+this.featureType}});this.featureNS&&a.setAttribute("xmlns:"+
this.featurePrefix,this.featureNS);var c=b.modified;null===this.geometryName||c&&void 0===c.geometry||(this.srsName=this.getSrsName(b),this.writeNode("Property",{name:this.geometryName,value:b.geometry},a));for(var d in b.attributes)void 0===b.attributes[d]||c&&c.attributes&&(!c.attributes||void 0===c.attributes[d])||this.writeNode("Property",{name:d,value:b.attributes[d]},a);this.writeNode("ogc:Filter",new OpenLayers.Filter.FeatureId({fids:[b.fid]}),a);return a},Property:function(a){var b=this.createElementNSPlus("wfs:Property");
this.writeNode("Name",a.name,b);null!==a.value&&this.writeNode("Value",a.value,b);return b},Name:function(a){return this.createElementNSPlus("wfs:Name",{value:a})},Value:function(a){var b;a instanceof OpenLayers.Geometry?(b=this.createElementNSPlus("wfs:Value"),a=this.writeNode("feature:_geometry",a).firstChild,b.appendChild(a)):b=this.createElementNSPlus("wfs:Value",{value:a});return b},Delete:function(a){var b=a.feature;a=a.options;a=this.createElementNSPlus("wfs:Delete",{attributes:{handle:a&&
a.handle,typeName:(this.featureNS?this.featurePrefix+":":"")+this.featureType}});this.featureNS&&a.setAttribute("xmlns:"+this.featurePrefix,this.featureNS);this.writeNode("ogc:Filter",new OpenLayers.Filter.FeatureId({fids:[b.fid]}),a);return a}}},schemaLocationAttr:function(a){a=OpenLayers.Util.extend({featurePrefix:this.featurePrefix,schema:this.schema},a);var b=OpenLayers.Util.extend({},this.schemaLocations);a.schema&&(b[a.featurePrefix]=a.schema);a=[];var c,d;for(d in b)(c=this.namespaces[d])&&
a.push(c+" "+b[d]);return a.join(" ")||void 0},setFilterProperty:function(a){if(a.filters)for(var b=0,c=a.filters.length;b<c;++b)OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this,a.filters[b]);else a instanceof OpenLayers.Filter.Spatial&&!a.property&&(a.property=this.geometryName)},CLASS_NAME:"OpenLayers.Format.WFST.v1"});OpenLayers.Format.OGCExceptionReport=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{ogc:"http://www.opengis.net/ogc"},regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},defaultPrefix:"ogc",read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));var b={exceptionReport:null};a.documentElement&&(this.readChildNodes(a,b),null===b.exceptionReport&&(b=(new OpenLayers.Format.OWSCommon).read(a)));return b},readers:{ogc:{ServiceExceptionReport:function(a,
b){b.exceptionReport={exceptions:[]};this.readChildNodes(a,b.exceptionReport)},ServiceException:function(a,b){var c={code:a.getAttribute("code"),locator:a.getAttribute("locator"),text:this.getChildValue(a)};b.exceptions.push(c)}}},CLASS_NAME:"OpenLayers.Format.OGCExceptionReport"});OpenLayers.Format.XML.VersionedOGC=OpenLayers.Class(OpenLayers.Format.XML,{defaultVersion:null,version:null,profile:null,allowFallback:!1,name:null,stringifyOutput:!1,parser:null,initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a]);a=this.CLASS_NAME;this.name=a.substring(a.lastIndexOf(".")+1)},getVersion:function(a,b){var c;a?(c=this.version,c||(c=a.getAttribute("version"),c||(c=this.defaultVersion))):c=b&&b.version||this.version||this.defaultVersion;return c},getParser:function(a){a=
a||this.defaultVersion;var b=this.profile?"_"+this.profile:"";if(!this.parser||this.parser.VERSION!=a){var c=OpenLayers.Format[this.name]["v"+a.replace(/\./g,"_")+b];if(!c&&(""!==b&&this.allowFallback&&(b="",c=OpenLayers.Format[this.name]["v"+a.replace(/\./g,"_")]),!c))throw"Can't find a "+this.name+" parser for version "+a+b;this.parser=new c(this.options)}return this.parser},write:function(a,b){var c=this.getVersion(null,b);this.parser=this.getParser(c);c=this.parser.write(a,b);return!1===this.stringifyOutput?
c:OpenLayers.Format.XML.prototype.write.apply(this,[c])},read:function(a,b){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));var c=this.getVersion(a.documentElement);this.parser=this.getParser(c);var d=this.parser.read(a,b),e=this.parser.errorProperty||null;null!==e&&void 0===d[e]&&(e=new OpenLayers.Format.OGCExceptionReport,d.error=e.read(a));d.version=c;return d},CLASS_NAME:"OpenLayers.Format.XML.VersionedOGC"});OpenLayers.Filter.Logical=OpenLayers.Class(OpenLayers.Filter,{filters:null,type:null,initialize:function(a){this.filters=[];OpenLayers.Filter.prototype.initialize.apply(this,[a])},destroy:function(){this.filters=null;OpenLayers.Filter.prototype.destroy.apply(this)},evaluate:function(a){var b,c;switch(this.type){case OpenLayers.Filter.Logical.AND:b=0;for(c=this.filters.length;b<c;b++)if(!1==this.filters[b].evaluate(a))return!1;return!0;case OpenLayers.Filter.Logical.OR:b=0;for(c=this.filters.length;b<
c;b++)if(!0==this.filters[b].evaluate(a))return!0;return!1;case OpenLayers.Filter.Logical.NOT:return!this.filters[0].evaluate(a)}},clone:function(){for(var a=[],b=0,c=this.filters.length;b<c;++b)a.push(this.filters[b].clone());return new OpenLayers.Filter.Logical({type:this.type,filters:a})},CLASS_NAME:"OpenLayers.Filter.Logical"});OpenLayers.Filter.Logical.AND="&&";OpenLayers.Filter.Logical.OR="||";OpenLayers.Filter.Logical.NOT="!";OpenLayers.Filter.Comparison=OpenLayers.Class(OpenLayers.Filter,{type:null,property:null,value:null,matchCase:!0,lowerBoundary:null,upperBoundary:null,initialize:function(a){OpenLayers.Filter.prototype.initialize.apply(this,[a]);this.type===OpenLayers.Filter.Comparison.LIKE&&void 0===a.matchCase&&(this.matchCase=null)},evaluate:function(a){a instanceof OpenLayers.Feature.Vector&&(a=a.attributes);var b=!1;a=a[this.property];switch(this.type){case OpenLayers.Filter.Comparison.EQUAL_TO:b=this.value;
b=this.matchCase||"string"!=typeof a||"string"!=typeof b?a==b:a.toUpperCase()==b.toUpperCase();break;case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:b=this.value;b=this.matchCase||"string"!=typeof a||"string"!=typeof b?a!=b:a.toUpperCase()!=b.toUpperCase();break;case OpenLayers.Filter.Comparison.LESS_THAN:b=a<this.value;break;case OpenLayers.Filter.Comparison.GREATER_THAN:b=a>this.value;break;case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:b=a<=this.value;break;case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:b=
a>=this.value;break;case OpenLayers.Filter.Comparison.BETWEEN:b=a>=this.lowerBoundary&&a<=this.upperBoundary;break;case OpenLayers.Filter.Comparison.LIKE:b=RegExp(this.value,"gi").test(a);break;case OpenLayers.Filter.Comparison.IS_NULL:b=null===a}return b},value2regex:function(a,b,c){if("."==a)throw Error("'.' is an unsupported wildCard character for OpenLayers.Filter.Comparison");a=a?a:"*";b=b?b:".";this.value=this.value.replace(RegExp("\\"+(c?c:"!")+"(.|$)","g"),"\\$1");this.value=this.value.replace(RegExp("\\"+
b,"g"),".");this.value=this.value.replace(RegExp("\\"+a,"g"),".*");this.value=this.value.replace(RegExp("\\\\.\\*","g"),"\\"+a);return this.value=this.value.replace(RegExp("\\\\\\.","g"),"\\"+b)},regex2value:function(){var a=this.value,a=a.replace(/!/g,"!!"),a=a.replace(/(\\)?\\\./g,function(a,c){return c?a:"!."}),a=a.replace(/(\\)?\\\*/g,function(a,c){return c?a:"!*"}),a=a.replace(/\\\\/g,"\\");return a=a.replace(/\.\*/g,"*")},clone:function(){return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison,
this)},CLASS_NAME:"OpenLayers.Filter.Comparison"});OpenLayers.Filter.Comparison.EQUAL_TO="==";OpenLayers.Filter.Comparison.NOT_EQUAL_TO="!=";OpenLayers.Filter.Comparison.LESS_THAN="<";OpenLayers.Filter.Comparison.GREATER_THAN=">";OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO="<=";OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO=">=";OpenLayers.Filter.Comparison.BETWEEN="..";OpenLayers.Filter.Comparison.LIKE="~";OpenLayers.Filter.Comparison.IS_NULL="NULL";OpenLayers.Format.Filter=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.0.0",CLASS_NAME:"OpenLayers.Format.Filter"});OpenLayers.Filter.Function=OpenLayers.Class(OpenLayers.Filter,{name:null,params:null,CLASS_NAME:"OpenLayers.Filter.Function"});OpenLayers.Date={dateRegEx:/^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/,toISOString:function(){return"toISOString"in Date.prototype?function(a){return a.toISOString()}:function(a){return isNaN(a.getTime())?"Invalid Date":a.getUTCFullYear()+"-"+OpenLayers.Number.zeroPad(a.getUTCMonth()+1,2)+"-"+OpenLayers.Number.zeroPad(a.getUTCDate(),2)+"T"+OpenLayers.Number.zeroPad(a.getUTCHours(),2)+":"+OpenLayers.Number.zeroPad(a.getUTCMinutes(),
2)+":"+OpenLayers.Number.zeroPad(a.getUTCSeconds(),2)+"."+OpenLayers.Number.zeroPad(a.getUTCMilliseconds(),3)+"Z"}}(),parse:function(a){var b;if((a=a.match(this.dateRegEx))&&(a[1]||a[7])){b=parseInt(a[1],10)||0;var c=parseInt(a[2],10)-1||0,d=parseInt(a[3],10)||1;b=new Date(Date.UTC(b,c,d));if(c=a[7]){var d=parseInt(a[4],10),e=parseInt(a[5],10),f=parseFloat(a[6]),g=f|0,f=Math.round(1E3*(f-g));b.setUTCHours(d,e,g,f);"Z"!==c&&(c=parseInt(c,10),a=parseInt(a[8],10)||0,a=-1E3*(60*60*c+60*a),b=new Date(b.getTime()+
a))}}else b=new Date("invalid");return b}};OpenLayers.Format.Filter.v1=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{ogc:"http://www.opengis.net/ogc",gml:"http://www.opengis.net/gml",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},defaultPrefix:"ogc",schemaLocation:null,initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},read:function(a){var b={};this.readers.ogc.Filter.apply(this,[a,b]);return b.filter},readers:{ogc:{_expression:function(a){for(var b="",c=a.firstChild;c;c=
c.nextSibling)switch(c.nodeType){case 1:a=this.readNode(c);a.property?b+="${"+a.property+"}":void 0!==a.value&&(b+=a.value);break;case 3:case 4:b+=c.nodeValue}return b},Filter:function(a,b){var c={fids:[],filters:[]};this.readChildNodes(a,c);0<c.fids.length?b.filter=new OpenLayers.Filter.FeatureId({fids:c.fids}):0<c.filters.length&&(b.filter=c.filters[0])},FeatureId:function(a,b){var c=a.getAttribute("fid");c&&b.fids.push(c)},And:function(a,b){var c=new OpenLayers.Filter.Logical({type:OpenLayers.Filter.Logical.AND});
this.readChildNodes(a,c);b.filters.push(c)},Or:function(a,b){var c=new OpenLayers.Filter.Logical({type:OpenLayers.Filter.Logical.OR});this.readChildNodes(a,c);b.filters.push(c)},Not:function(a,b){var c=new OpenLayers.Filter.Logical({type:OpenLayers.Filter.Logical.NOT});this.readChildNodes(a,c);b.filters.push(c)},PropertyIsLessThan:function(a,b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.LESS_THAN});this.readChildNodes(a,c);b.filters.push(c)},PropertyIsGreaterThan:function(a,
b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.GREATER_THAN});this.readChildNodes(a,c);b.filters.push(c)},PropertyIsLessThanOrEqualTo:function(a,b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO});this.readChildNodes(a,c);b.filters.push(c)},PropertyIsGreaterThanOrEqualTo:function(a,b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO});this.readChildNodes(a,c);b.filters.push(c)},
PropertyIsBetween:function(a,b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.BETWEEN});this.readChildNodes(a,c);b.filters.push(c)},Literal:function(a,b){b.value=OpenLayers.String.numericIf(this.getChildValue(a),!0)},PropertyName:function(a,b){b.property=this.getChildValue(a)},LowerBoundary:function(a,b){b.lowerBoundary=OpenLayers.String.numericIf(this.readers.ogc._expression.call(this,a),!0)},UpperBoundary:function(a,b){b.upperBoundary=OpenLayers.String.numericIf(this.readers.ogc._expression.call(this,
a),!0)},Intersects:function(a,b){this.readSpatial(a,b,OpenLayers.Filter.Spatial.INTERSECTS)},Within:function(a,b){this.readSpatial(a,b,OpenLayers.Filter.Spatial.WITHIN)},Contains:function(a,b){this.readSpatial(a,b,OpenLayers.Filter.Spatial.CONTAINS)},DWithin:function(a,b){this.readSpatial(a,b,OpenLayers.Filter.Spatial.DWITHIN)},Distance:function(a,b){b.distance=parseInt(this.getChildValue(a));b.distanceUnits=a.getAttribute("units")},Function:function(a,b){},PropertyIsNull:function(a,b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.IS_NULL});
this.readChildNodes(a,c);b.filters.push(c)}}},readSpatial:function(a,b,c){c=new OpenLayers.Filter.Spatial({type:c});this.readChildNodes(a,c);c.value=c.components[0];delete c.components;b.filters.push(c)},encodeLiteral:function(a){a instanceof Date&&(a=OpenLayers.Date.toISOString(a));return a},writeOgcExpression:function(a,b){a instanceof OpenLayers.Filter.Function?this.writeNode("Function",a,b):this.writeNode("Literal",a,b);return b},write:function(a){return this.writers.ogc.Filter.apply(this,[a])},
writers:{ogc:{Filter:function(a){var b=this.createElementNSPlus("ogc:Filter");this.writeNode(this.getFilterType(a),a,b);return b},_featureIds:function(a){for(var b=this.createDocumentFragment(),c=0,d=a.fids.length;c<d;++c)this.writeNode("ogc:FeatureId",a.fids[c],b);return b},FeatureId:function(a){return this.createElementNSPlus("ogc:FeatureId",{attributes:{fid:a}})},And:function(a){for(var b=this.createElementNSPlus("ogc:And"),c,d=0,e=a.filters.length;d<e;++d)c=a.filters[d],this.writeNode(this.getFilterType(c),
c,b);return b},Or:function(a){for(var b=this.createElementNSPlus("ogc:Or"),c,d=0,e=a.filters.length;d<e;++d)c=a.filters[d],this.writeNode(this.getFilterType(c),c,b);return b},Not:function(a){var b=this.createElementNSPlus("ogc:Not");a=a.filters[0];this.writeNode(this.getFilterType(a),a,b);return b},PropertyIsLessThan:function(a){var b=this.createElementNSPlus("ogc:PropertyIsLessThan");this.writeNode("PropertyName",a,b);this.writeOgcExpression(a.value,b);return b},PropertyIsGreaterThan:function(a){var b=
this.createElementNSPlus("ogc:PropertyIsGreaterThan");this.writeNode("PropertyName",a,b);this.writeOgcExpression(a.value,b);return b},PropertyIsLessThanOrEqualTo:function(a){var b=this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");this.writeNode("PropertyName",a,b);this.writeOgcExpression(a.value,b);return b},PropertyIsGreaterThanOrEqualTo:function(a){var b=this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");this.writeNode("PropertyName",a,b);this.writeOgcExpression(a.value,b);
return b},PropertyIsBetween:function(a){var b=this.createElementNSPlus("ogc:PropertyIsBetween");this.writeNode("PropertyName",a,b);this.writeNode("LowerBoundary",a,b);this.writeNode("UpperBoundary",a,b);return b},PropertyName:function(a){return this.createElementNSPlus("ogc:PropertyName",{value:a.property})},Literal:function(a){return this.createElementNSPlus("ogc:Literal",{value:(this.encodeLiteral||OpenLayers.Format.Filter.v1.prototype.encodeLiteral)(a)})},LowerBoundary:function(a){var b=this.createElementNSPlus("ogc:LowerBoundary");
this.writeOgcExpression(a.lowerBoundary,b);return b},UpperBoundary:function(a){var b=this.createElementNSPlus("ogc:UpperBoundary");this.writeNode("Literal",a.upperBoundary,b);return b},INTERSECTS:function(a){return this.writeSpatial(a,"Intersects")},WITHIN:function(a){return this.writeSpatial(a,"Within")},CONTAINS:function(a){return this.writeSpatial(a,"Contains")},DWITHIN:function(a){var b=this.writeSpatial(a,"DWithin");this.writeNode("Distance",a,b);return b},Distance:function(a){return this.createElementNSPlus("ogc:Distance",
{attributes:{units:a.distanceUnits},value:a.distance})},Function:function(a){var b=this.createElementNSPlus("ogc:Function",{attributes:{name:a.name}});a=a.params;for(var c=0,d=a.length;c<d;c++)this.writeOgcExpression(a[c],b);return b},PropertyIsNull:function(a){var b=this.createElementNSPlus("ogc:PropertyIsNull");this.writeNode("PropertyName",a,b);return b}}},getFilterType:function(a){var b=this.filterMap[a.type];if(!b)throw"Filter writing not supported for rule type: "+a.type;return b},filterMap:{"&&":"And",
"||":"Or","!":"Not","==":"PropertyIsEqualTo","!=":"PropertyIsNotEqualTo","<":"PropertyIsLessThan",">":"PropertyIsGreaterThan","<=":"PropertyIsLessThanOrEqualTo",">=":"PropertyIsGreaterThanOrEqualTo","..":"PropertyIsBetween","~":"PropertyIsLike",NULL:"PropertyIsNull",BBOX:"BBOX",DWITHIN:"DWITHIN",WITHIN:"WITHIN",CONTAINS:"CONTAINS",INTERSECTS:"INTERSECTS",FID:"_featureIds"},CLASS_NAME:"OpenLayers.Format.Filter.v1"});OpenLayers.Geometry=OpenLayers.Class({id:null,parent:null,bounds:null,initialize:function(){this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},destroy:function(){this.bounds=this.id=null},clone:function(){return new OpenLayers.Geometry},setBounds:function(a){a&&(this.bounds=a.clone())},clearBounds:function(){this.bounds=null;this.parent&&this.parent.clearBounds()},extendBounds:function(a){this.getBounds()?this.bounds.extend(a):this.setBounds(a)},getBounds:function(){null==this.bounds&&this.calculateBounds();
return this.bounds},calculateBounds:function(){},distanceTo:function(a,b){},getVertices:function(a){},atPoint:function(a,b,c){var d=!1;null!=this.getBounds()&&null!=a&&(b=null!=b?b:0,c=null!=c?c:0,d=(new OpenLayers.Bounds(this.bounds.left-b,this.bounds.bottom-c,this.bounds.right+b,this.bounds.top+c)).containsLonLat(a));return d},getLength:function(){return 0},getArea:function(){return 0},getCentroid:function(){return null},toString:function(){return OpenLayers.Format&&OpenLayers.Format.WKT?OpenLayers.Format.WKT.prototype.write(new OpenLayers.Feature.Vector(this)):
Object.prototype.toString.call(this)},CLASS_NAME:"OpenLayers.Geometry"});OpenLayers.Geometry.fromWKT=function(a){var b;if(OpenLayers.Format&&OpenLayers.Format.WKT){var c=OpenLayers.Geometry.fromWKT.format;c||(c=new OpenLayers.Format.WKT,OpenLayers.Geometry.fromWKT.format=c);a=c.read(a);if(a instanceof OpenLayers.Feature.Vector)b=a.geometry;else if(OpenLayers.Util.isArray(a)){b=a.length;for(var c=Array(b),d=0;d<b;++d)c[d]=a[d].geometry;b=new OpenLayers.Geometry.Collection(c)}}return b};
OpenLayers.Geometry.segmentsIntersect=function(a,b,c){var d=c&&c.point;c=c&&c.tolerance;var e=!1,f=a.x1-b.x1,g=a.y1-b.y1,h=a.x2-a.x1,k=a.y2-a.y1,l=b.y2-b.y1,m=b.x2-b.x1,n=l*h-m*k,l=m*g-l*f,g=h*g-k*f;0==n?0==l&&0==g&&(e=!0):(f=l/n,n=g/n,0<=f&&(1>=f&&0<=n&&1>=n)&&(d?(h=a.x1+f*h,n=a.y1+f*k,e=new OpenLayers.Geometry.Point(h,n)):e=!0));if(c)if(e){if(d)a:for(a=[a,b],b=0;2>b;++b)for(f=a[b],k=1;3>k;++k)if(h=f["x"+k],n=f["y"+k],d=Math.sqrt(Math.pow(h-e.x,2)+Math.pow(n-e.y,2)),d<c){e.x=h;e.y=n;break a}}else a:for(a=
[a,b],b=0;2>b;++b)for(h=a[b],n=a[(b+1)%2],k=1;3>k;++k)if(f={x:h["x"+k],y:h["y"+k]},g=OpenLayers.Geometry.distanceToSegment(f,n),g.distance<c){e=d?new OpenLayers.Geometry.Point(f.x,f.y):!0;break a}return e};OpenLayers.Geometry.distanceToSegment=function(a,b){var c=OpenLayers.Geometry.distanceSquaredToSegment(a,b);c.distance=Math.sqrt(c.distance);return c};
OpenLayers.Geometry.distanceSquaredToSegment=function(a,b){var c=a.x,d=a.y,e=b.x1,f=b.y1,g=b.x2,h=b.y2,k=g-e,l=h-f,m=(k*(c-e)+l*(d-f))/(Math.pow(k,2)+Math.pow(l,2));0>=m||(1<=m?(e=g,f=h):(e+=m*k,f+=m*l));return{distance:Math.pow(e-c,2)+Math.pow(f-d,2),x:e,y:f,along:m}};OpenLayers.Geometry.Point=OpenLayers.Class(OpenLayers.Geometry,{x:null,y:null,initialize:function(a,b){OpenLayers.Geometry.prototype.initialize.apply(this,arguments);this.x=parseFloat(a);this.y=parseFloat(b)},clone:function(a){null==a&&(a=new OpenLayers.Geometry.Point(this.x,this.y));OpenLayers.Util.applyDefaults(a,this);return a},calculateBounds:function(){this.bounds=new OpenLayers.Bounds(this.x,this.y,this.x,this.y)},distanceTo:function(a,b){var c=!(b&&!1===b.edge)&&b&&b.details,d,e,f,g,h;a instanceof
OpenLayers.Geometry.Point?(e=this.x,f=this.y,g=a.x,h=a.y,d=Math.sqrt(Math.pow(e-g,2)+Math.pow(f-h,2)),d=c?{x0:e,y0:f,x1:g,y1:h,distance:d}:d):(d=a.distanceTo(this,b),c&&(d={x0:d.x1,y0:d.y1,x1:d.x0,y1:d.y0,distance:d.distance}));return d},equals:function(a){var b=!1;null!=a&&(b=this.x==a.x&&this.y==a.y||isNaN(this.x)&&isNaN(this.y)&&isNaN(a.x)&&isNaN(a.y));return b},toShortString:function(){return this.x+", "+this.y},move:function(a,b){this.x+=a;this.y+=b;this.clearBounds()},rotate:function(a,b){a*=
Math.PI/180;var c=this.distanceTo(b),d=a+Math.atan2(this.y-b.y,this.x-b.x);this.x=b.x+c*Math.cos(d);this.y=b.y+c*Math.sin(d);this.clearBounds()},getCentroid:function(){return new OpenLayers.Geometry.Point(this.x,this.y)},resize:function(a,b,c){this.x=b.x+a*(void 0==c?1:c)*(this.x-b.x);this.y=b.y+a*(this.y-b.y);this.clearBounds();return this},intersects:function(a){var b=!1;return b="OpenLayers.Geometry.Point"==a.CLASS_NAME?this.equals(a):a.intersects(this)},transform:function(a,b){a&&b&&(OpenLayers.Projection.transform(this,
a,b),this.bounds=null);return this},getVertices:function(a){return[this]},CLASS_NAME:"OpenLayers.Geometry.Point"});OpenLayers.Geometry.Collection=OpenLayers.Class(OpenLayers.Geometry,{components:null,componentTypes:null,initialize:function(a){OpenLayers.Geometry.prototype.initialize.apply(this,arguments);this.components=[];null!=a&&this.addComponents(a)},destroy:function(){this.components.length=0;this.components=null;OpenLayers.Geometry.prototype.destroy.apply(this,arguments)},clone:function(){for(var a=eval("new "+this.CLASS_NAME+"()"),b=0,c=this.components.length;b<c;b++)a.addComponent(this.components[b].clone());
OpenLayers.Util.applyDefaults(a,this);return a},getComponentsString:function(){for(var a=[],b=0,c=this.components.length;b<c;b++)a.push(this.components[b].toShortString());return a.join(",")},calculateBounds:function(){this.bounds=null;var a=new OpenLayers.Bounds,b=this.components;if(b)for(var c=0,d=b.length;c<d;c++)a.extend(b[c].getBounds());null!=a.left&&(null!=a.bottom&&null!=a.right&&null!=a.top)&&this.setBounds(a)},addComponents:function(a){OpenLayers.Util.isArray(a)||(a=[a]);for(var b=0,c=a.length;b<
c;b++)this.addComponent(a[b])},addComponent:function(a,b){var c=!1;if(a&&(null==this.componentTypes||-1<OpenLayers.Util.indexOf(this.componentTypes,a.CLASS_NAME))){if(null!=b&&b<this.components.length){var c=this.components.slice(0,b),d=this.components.slice(b,this.components.length);c.push(a);this.components=c.concat(d)}else this.components.push(a);a.parent=this;this.clearBounds();c=!0}return c},removeComponents:function(a){var b=!1;OpenLayers.Util.isArray(a)||(a=[a]);for(var c=a.length-1;0<=c;--c)b=
this.removeComponent(a[c])||b;return b},removeComponent:function(a){OpenLayers.Util.removeItem(this.components,a);this.clearBounds();return!0},getLength:function(){for(var a=0,b=0,c=this.components.length;b<c;b++)a+=this.components[b].getLength();return a},getArea:function(){for(var a=0,b=0,c=this.components.length;b<c;b++)a+=this.components[b].getArea();return a},getGeodesicArea:function(a){for(var b=0,c=0,d=this.components.length;c<d;c++)b+=this.components[c].getGeodesicArea(a);return b},getCentroid:function(a){if(!a)return this.components.length&&
this.components[0].getCentroid();a=this.components.length;if(!a)return!1;for(var b=[],c=[],d=0,e=Number.MAX_VALUE,f,g=0;g<a;++g){f=this.components[g];var h=f.getArea();f=f.getCentroid(!0);isNaN(h)||(isNaN(f.x)||isNaN(f.y))||(b.push(h),d+=h,e=h<e&&0<h?h:e,c.push(f))}a=b.length;if(0===d){for(g=0;g<a;++g)b[g]=1;d=b.length}else{for(g=0;g<a;++g)b[g]/=e;d/=e}for(var k=e=0,g=0;g<a;++g)f=c[g],h=b[g],e+=f.x*h,k+=f.y*h;return new OpenLayers.Geometry.Point(e/d,k/d)},getGeodesicLength:function(a){for(var b=0,
c=0,d=this.components.length;c<d;c++)b+=this.components[c].getGeodesicLength(a);return b},move:function(a,b){for(var c=0,d=this.components.length;c<d;c++)this.components[c].move(a,b)},rotate:function(a,b){for(var c=0,d=this.components.length;c<d;++c)this.components[c].rotate(a,b)},resize:function(a,b,c){for(var d=0;d<this.components.length;++d)this.components[d].resize(a,b,c);return this},distanceTo:function(a,b){for(var c=!(b&&!1===b.edge)&&b&&b.details,d,e,f,g=Number.POSITIVE_INFINITY,h=0,k=this.components.length;h<
k&&!(d=this.components[h].distanceTo(a,b),f=c?d.distance:d,f<g&&(g=f,e=d,0==g));++h);return e},equals:function(a){var b=!0;if(a&&a.CLASS_NAME&&this.CLASS_NAME==a.CLASS_NAME)if(OpenLayers.Util.isArray(a.components)&&a.components.length==this.components.length)for(var c=0,d=this.components.length;c<d;++c){if(!this.components[c].equals(a.components[c])){b=!1;break}}else b=!1;else b=!1;return b},transform:function(a,b){if(a&&b){for(var c=0,d=this.components.length;c<d;c++)this.components[c].transform(a,
b);this.bounds=null}return this},intersects:function(a){for(var b=!1,c=0,d=this.components.length;c<d&&!(b=a.intersects(this.components[c]));++c);return b},getVertices:function(a){for(var b=[],c=0,d=this.components.length;c<d;++c)Array.prototype.push.apply(b,this.components[c].getVertices(a));return b},CLASS_NAME:"OpenLayers.Geometry.Collection"});OpenLayers.Geometry.MultiPoint=OpenLayers.Class(OpenLayers.Geometry.Collection,{componentTypes:["OpenLayers.Geometry.Point"],addPoint:function(a,b){this.addComponent(a,b)},removePoint:function(a){this.removeComponent(a)},CLASS_NAME:"OpenLayers.Geometry.MultiPoint"});OpenLayers.Geometry.Curve=OpenLayers.Class(OpenLayers.Geometry.MultiPoint,{componentTypes:["OpenLayers.Geometry.Point"],getLength:function(){var a=0;if(this.components&&1<this.components.length)for(var b=1,c=this.components.length;b<c;b++)a+=this.components[b-1].distanceTo(this.components[b]);return a},getGeodesicLength:function(a){var b=this;if(a){var c=new OpenLayers.Projection("EPSG:4326");c.equals(a)||(b=this.clone().transform(a,c))}a=0;if(b.components&&1<b.components.length)for(var d,e=1,f=b.components.length;e<
f;e++)c=b.components[e-1],d=b.components[e],a+=OpenLayers.Util.distVincenty({lon:c.x,lat:c.y},{lon:d.x,lat:d.y});return 1E3*a},CLASS_NAME:"OpenLayers.Geometry.Curve"});OpenLayers.Geometry.LineString=OpenLayers.Class(OpenLayers.Geometry.Curve,{removeComponent:function(a){var b=this.components&&2<this.components.length;b&&OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,arguments);return b},intersects:function(a){var b=!1,c=a.CLASS_NAME;if("OpenLayers.Geometry.LineString"==c||"OpenLayers.Geometry.LinearRing"==c||"OpenLayers.Geometry.Point"==c){var d=this.getSortedSegments();a="OpenLayers.Geometry.Point"==c?[{x1:a.x,y1:a.y,x2:a.x,y2:a.y}]:a.getSortedSegments();
var e,f,g,h,k,l,m,n=0,p=d.length;a:for(;n<p;++n){c=d[n];e=c.x1;f=c.x2;g=c.y1;h=c.y2;var q=0,r=a.length;for(;q<r;++q){k=a[q];if(k.x1>f)break;if(!(k.x2<e||(l=k.y1,m=k.y2,Math.min(l,m)>Math.max(g,h)||Math.max(l,m)<Math.min(g,h)||!OpenLayers.Geometry.segmentsIntersect(c,k)))){b=!0;break a}}}}else b=a.intersects(this);return b},getSortedSegments:function(){for(var a=this.components.length-1,b=Array(a),c,d,e=0;e<a;++e)c=this.components[e],d=this.components[e+1],b[e]=c.x<d.x?{x1:c.x,y1:c.y,x2:d.x,y2:d.y}:
{x1:d.x,y1:d.y,x2:c.x,y2:c.y};return b.sort(function(a,b){return a.x1-b.x1})},splitWithSegment:function(a,b){for(var c=!(b&&!1===b.edge),d=b&&b.tolerance,e=[],f=this.getVertices(),g=[],h=[],k=!1,l,m,n,p={point:!0,tolerance:d},q=null,r=0,s=f.length-2;r<=s;++r)if(d=f[r],g.push(d.clone()),l=f[r+1],m={x1:d.x,y1:d.y,x2:l.x,y2:l.y},m=OpenLayers.Geometry.segmentsIntersect(a,m,p),m instanceof OpenLayers.Geometry.Point&&((n=m.x===a.x1&&m.y===a.y1||m.x===a.x2&&m.y===a.y2||m.equals(d)||m.equals(l)?!0:!1)||c))m.equals(h[h.length-
1])||h.push(m.clone()),0===r&&m.equals(d)||m.equals(l)||(k=!0,m.equals(d)||g.push(m),e.push(new OpenLayers.Geometry.LineString(g)),g=[m.clone()]);k&&(g.push(l.clone()),e.push(new OpenLayers.Geometry.LineString(g)));if(0<h.length)var t=a.x1<a.x2?1:-1,u=a.y1<a.y2?1:-1,q={lines:e,points:h.sort(function(a,b){return t*a.x-t*b.x||u*a.y-u*b.y})};return q},split:function(a,b){var c=null,d=b&&b.mutual,e,f,g,h;if(a instanceof OpenLayers.Geometry.LineString){var k=this.getVertices(),l,m,n,p,q,r=[];g=[];for(var s=
0,t=k.length-2;s<=t;++s){l=k[s];m=k[s+1];n={x1:l.x,y1:l.y,x2:m.x,y2:m.y};h=h||[a];d&&r.push(l.clone());for(var u=0;u<h.length;++u)if(p=h[u].splitWithSegment(n,b))if(q=p.lines,0<q.length&&(q.unshift(u,1),Array.prototype.splice.apply(h,q),u+=q.length-2),d)for(var v=0,w=p.points.length;v<w;++v)q=p.points[v],q.equals(l)||(r.push(q),g.push(new OpenLayers.Geometry.LineString(r)),r=q.equals(m)?[]:[q.clone()])}d&&(0<g.length&&0<r.length)&&(r.push(m.clone()),g.push(new OpenLayers.Geometry.LineString(r)))}else c=
a.splitWith(this,b);h&&1<h.length?f=!0:h=[];g&&1<g.length?e=!0:g=[];if(f||e)c=d?[g,h]:h;return c},splitWith:function(a,b){return a.split(this,b)},getVertices:function(a){return!0===a?[this.components[0],this.components[this.components.length-1]]:!1===a?this.components.slice(1,this.components.length-1):this.components.slice()},distanceTo:function(a,b){var c=!(b&&!1===b.edge)&&b&&b.details,d,e={},f=Number.POSITIVE_INFINITY;if(a instanceof OpenLayers.Geometry.Point){for(var g=this.getSortedSegments(),
h=a.x,k=a.y,l,m=0,n=g.length;m<n;++m)if(l=g[m],d=OpenLayers.Geometry.distanceToSegment(a,l),d.distance<f){if(f=d.distance,e=d,0===f)break}else if(l.x2>h&&(k>l.y1&&k<l.y2||k<l.y1&&k>l.y2))break;e=c?{distance:e.distance,x0:e.x,y0:e.y,x1:h,y1:k}:e.distance}else if(a instanceof OpenLayers.Geometry.LineString){var g=this.getSortedSegments(),h=a.getSortedSegments(),p,q,r=h.length,s={point:!0},m=0,n=g.length;a:for(;m<n;++m){k=g[m];l=k.x1;q=k.y1;for(var t=0;t<r;++t)if(d=h[t],p=OpenLayers.Geometry.segmentsIntersect(k,
d,s)){f=0;e={distance:0,x0:p.x,y0:p.y,x1:p.x,y1:p.y};break a}else d=OpenLayers.Geometry.distanceToSegment({x:l,y:q},d),d.distance<f&&(f=d.distance,e={distance:f,x0:l,y0:q,x1:d.x,y1:d.y})}c||(e=e.distance);0!==f&&k&&(d=a.distanceTo(new OpenLayers.Geometry.Point(k.x2,k.y2),b),m=c?d.distance:d,m<f&&(e=c?{distance:f,x0:d.x1,y0:d.y1,x1:d.x0,y1:d.y0}:m))}else e=a.distanceTo(this,b),c&&(e={distance:e.distance,x0:e.x1,y0:e.y1,x1:e.x0,y1:e.y0});return e},simplify:function(a){if(this&&null!==this){var b=this.getVertices();
if(3>b.length)return this;var c=function(a,b,d,k){for(var l=0,m=0,n=b,p;n<d;n++){p=a[b];var q=a[d],r=a[n],r=Math.abs(0.5*(p.x*q.y+q.x*r.y+r.x*p.y-q.x*p.y-r.x*q.y-p.x*r.y));p=Math.sqrt(Math.pow(p.x-q.x,2)+Math.pow(p.y-q.y,2));p=2*(r/p);p>l&&(l=p,m=n)}l>k&&m!=b&&(e.push(m),c(a,b,m,k),c(a,m,d,k))},d=b.length-1,e=[];e.push(0);for(e.push(d);b[0].equals(b[d]);)d--,e.push(d);c(b,0,d,a);a=[];e.sort(function(a,b){return a-b});for(d=0;d<e.length;d++)a.push(b[e[d]]);return new OpenLayers.Geometry.LineString(a)}return this},
CLASS_NAME:"OpenLayers.Geometry.LineString"});OpenLayers.Geometry.MultiLineString=OpenLayers.Class(OpenLayers.Geometry.Collection,{componentTypes:["OpenLayers.Geometry.LineString"],split:function(a,b){for(var c=null,d=b&&b.mutual,e,f,g,h,k=[],l=[a],m=0,n=this.components.length;m<n;++m){f=this.components[m];g=!1;for(var p=0;p<l.length;++p)if(e=f.split(l[p],b)){if(d){g=e[0];for(var q=0,r=g.length;q<r;++q)0===q&&k.length?k[k.length-1].addComponent(g[q]):k.push(new OpenLayers.Geometry.MultiLineString([g[q]]));g=!0;e=e[1]}if(e.length){e.unshift(p,
1);Array.prototype.splice.apply(l,e);break}}g||(k.length?k[k.length-1].addComponent(f.clone()):k=[new OpenLayers.Geometry.MultiLineString(f.clone())])}k&&1<k.length?g=!0:k=[];l&&1<l.length?h=!0:l=[];if(g||h)c=d?[k,l]:l;return c},splitWith:function(a,b){var c=null,d=b&&b.mutual,e,f,g,h,k,l;if(a instanceof OpenLayers.Geometry.LineString){l=[];k=[a];for(var m=0,n=this.components.length;m<n;++m){g=!1;f=this.components[m];for(var p=0;p<k.length;++p)if(e=k[p].split(f,b)){d&&(g=e[0],g.length&&(g.unshift(p,
1),Array.prototype.splice.apply(k,g),p+=g.length-2),e=e[1],0===e.length&&(e=[f.clone()]));g=0;for(var q=e.length;g<q;++g)0===g&&l.length?l[l.length-1].addComponent(e[g]):l.push(new OpenLayers.Geometry.MultiLineString([e[g]]));g=!0}g||(l.length?l[l.length-1].addComponent(f.clone()):l=[new OpenLayers.Geometry.MultiLineString([f.clone()])])}}else c=a.split(this);k&&1<k.length?h=!0:k=[];l&&1<l.length?g=!0:l=[];if(h||g)c=d?[k,l]:l;return c},CLASS_NAME:"OpenLayers.Geometry.MultiLineString"});OpenLayers.Geometry.LinearRing=OpenLayers.Class(OpenLayers.Geometry.LineString,{componentTypes:["OpenLayers.Geometry.Point"],addComponent:function(a,b){var c=!1,d=this.components.pop();null==b&&a.equals(d)||(c=OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,arguments));OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,[this.components[0]]);return c},removeComponent:function(a){var b=this.components&&3<this.components.length;b&&(this.components.pop(),OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
arguments),OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,[this.components[0]]));return b},move:function(a,b){for(var c=0,d=this.components.length;c<d-1;c++)this.components[c].move(a,b)},rotate:function(a,b){for(var c=0,d=this.components.length;c<d-1;++c)this.components[c].rotate(a,b)},resize:function(a,b,c){for(var d=0,e=this.components.length;d<e-1;++d)this.components[d].resize(a,b,c);return this},transform:function(a,b){if(a&&b){for(var c=0,d=this.components.length;c<d-1;c++)this.components[c].transform(a,
b);this.bounds=null}return this},getCentroid:function(){if(this.components){var a=this.components.length;if(0<a&&2>=a)return this.components[0].clone();if(2<a){var b=0,c=0,d=this.components[0].x,e=this.components[0].y,f=-1*this.getArea();if(0!=f){for(var g=0;g<a-1;g++)var h=this.components[g],k=this.components[g+1],b=b+(h.x+k.x-2*d)*((h.x-d)*(k.y-e)-(k.x-d)*(h.y-e)),c=c+(h.y+k.y-2*e)*((h.x-d)*(k.y-e)-(k.x-d)*(h.y-e));b=d+b/(6*f);a=e+c/(6*f)}else{for(g=0;g<a-1;g++)b+=this.components[g].x,c+=this.components[g].y;
b/=a-1;a=c/(a-1)}return new OpenLayers.Geometry.Point(b,a)}return null}},getArea:function(){var a=0;if(this.components&&2<this.components.length){for(var b=a=0,c=this.components.length;b<c-1;b++)var d=this.components[b],e=this.components[b+1],a=a+(d.x+e.x)*(e.y-d.y);a=-a/2}return a},getGeodesicArea:function(a){var b=this;if(a){var c=new OpenLayers.Projection("EPSG:4326");c.equals(a)||(b=this.clone().transform(a,c))}a=0;c=b.components&&b.components.length;if(2<c){for(var d,e,f=0;f<c-1;f++)d=b.components[f],
e=b.components[f+1],a+=OpenLayers.Util.rad(e.x-d.x)*(2+Math.sin(OpenLayers.Util.rad(d.y))+Math.sin(OpenLayers.Util.rad(e.y)));a=40680631590769*a/2}return a},containsPoint:function(a){var b=OpenLayers.Number.limitSigDigs,c=b(a.x,14);a=b(a.y,14);for(var d=this.components.length-1,e,f,g,h,k,l=0,m=0;m<d;++m)if(e=this.components[m],g=b(e.x,14),e=b(e.y,14),f=this.components[m+1],h=b(f.x,14),f=b(f.y,14),e==f){if(a==e&&(g<=h&&c>=g&&c<=h||g>=h&&c<=g&&c>=h)){l=-1;break}}else{k=b((a-f)*((h-g)/(f-e))+h,14);if(k==
c&&(e<f&&a>=e&&a<=f||e>f&&a<=e&&a>=f)){l=-1;break}k<=c||g!=h&&(k<Math.min(g,h)||k>Math.max(g,h))||(e<f&&a>=e&&a<f||e>f&&a<e&&a>=f)&&++l}return-1==l?1:!!(l&1)},intersects:function(a){var b=!1;if("OpenLayers.Geometry.Point"==a.CLASS_NAME)b=this.containsPoint(a);else if("OpenLayers.Geometry.LineString"==a.CLASS_NAME)b=a.intersects(this);else if("OpenLayers.Geometry.LinearRing"==a.CLASS_NAME)b=OpenLayers.Geometry.LineString.prototype.intersects.apply(this,[a]);else for(var c=0,d=a.components.length;c<
d&&!(b=a.components[c].intersects(this));++c);return b},getVertices:function(a){return!0===a?[]:this.components.slice(0,this.components.length-1)},CLASS_NAME:"OpenLayers.Geometry.LinearRing"});OpenLayers.Geometry.Polygon=OpenLayers.Class(OpenLayers.Geometry.Collection,{componentTypes:["OpenLayers.Geometry.LinearRing"],getArea:function(){var a=0;if(this.components&&0<this.components.length)for(var a=a+Math.abs(this.components[0].getArea()),b=1,c=this.components.length;b<c;b++)a-=Math.abs(this.components[b].getArea());return a},getGeodesicArea:function(a){var b=0;if(this.components&&0<this.components.length)for(var b=b+Math.abs(this.components[0].getGeodesicArea(a)),c=1,d=this.components.length;c<
d;c++)b-=Math.abs(this.components[c].getGeodesicArea(a));return b},containsPoint:function(a){var b=this.components.length,c=!1;if(0<b&&(c=this.components[0].containsPoint(a),1!==c&&c&&1<b))for(var d,e=1;e<b;++e)if(d=this.components[e].containsPoint(a)){c=1===d?1:!1;break}return c},intersects:function(a){var b=!1,c,d;if("OpenLayers.Geometry.Point"==a.CLASS_NAME)b=this.containsPoint(a);else if("OpenLayers.Geometry.LineString"==a.CLASS_NAME||"OpenLayers.Geometry.LinearRing"==a.CLASS_NAME){c=0;for(d=
this.components.length;c<d&&!(b=a.intersects(this.components[c]));++c);if(!b)for(c=0,d=a.components.length;c<d&&!(b=this.containsPoint(a.components[c]));++c);}else for(c=0,d=a.components.length;c<d&&!(b=this.intersects(a.components[c]));++c);if(!b&&"OpenLayers.Geometry.Polygon"==a.CLASS_NAME){var e=this.components[0];c=0;for(d=e.components.length;c<d&&!(b=a.containsPoint(e.components[c]));++c);}return b},distanceTo:function(a,b){return b&&!1===b.edge&&this.intersects(a)?0:OpenLayers.Geometry.Collection.prototype.distanceTo.apply(this,
[a,b])},CLASS_NAME:"OpenLayers.Geometry.Polygon"});OpenLayers.Geometry.Polygon.createRegularPolygon=function(a,b,c,d){var e=Math.PI*(1/c-0.5);d&&(e+=d/180*Math.PI);for(var f,g=[],h=0;h<c;++h)f=e+2*h*Math.PI/c,d=a.x+b*Math.cos(f),f=a.y+b*Math.sin(f),g.push(new OpenLayers.Geometry.Point(d,f));a=new OpenLayers.Geometry.LinearRing(g);return new OpenLayers.Geometry.Polygon([a])};OpenLayers.Geometry.MultiPolygon=OpenLayers.Class(OpenLayers.Geometry.Collection,{componentTypes:["OpenLayers.Geometry.Polygon"],CLASS_NAME:"OpenLayers.Geometry.MultiPolygon"});OpenLayers.Format.GML=OpenLayers.Class(OpenLayers.Format.XML,{featureNS:"http://mapserver.gis.umn.edu/mapserver",featurePrefix:"feature",featureName:"featureMember",layerName:"features",geometryName:"geometry",collectionName:"FeatureCollection",gmlns:"http://www.opengis.net/gml",extractAttributes:!0,xy:!0,initialize:function(a){this.regExes={trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g};OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},read:function(a){"string"==
typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a=this.getElementsByTagNameNS(a.documentElement,this.gmlns,this.featureName);for(var b=[],c=0;c<a.length;c++){var d=this.parseFeature(a[c]);d&&b.push(d)}return b},parseFeature:function(a){for(var b="MultiPolygon Polygon MultiLineString LineString MultiPoint Point Envelope".split(" "),c,d,e,f=0;f<b.length;++f)if(c=b[f],d=this.getElementsByTagNameNS(a,this.gmlns,c),0<d.length){if(e=this.parseGeometry[c.toLowerCase()])e=e.apply(this,
[d[0]]),this.internalProjection&&this.externalProjection&&e.transform(this.externalProjection,this.internalProjection);else throw new TypeError("Unsupported geometry type: "+c);break}var g;c=this.getElementsByTagNameNS(a,this.gmlns,"Box");for(f=0;f<c.length;++f)b=c[f],d=this.parseGeometry.box.apply(this,[b]),b=b.parentNode,"boundedBy"===(b.localName||b.nodeName.split(":").pop())?g=d:e=d.toGeometry();var h;this.extractAttributes&&(h=this.parseAttributes(a));h=new OpenLayers.Feature.Vector(e,h);h.bounds=
g;h.gml={featureType:a.firstChild.nodeName.split(":")[1],featureNS:a.firstChild.namespaceURI,featureNSPrefix:a.firstChild.prefix};a=a.firstChild;for(var k;a&&(1!=a.nodeType||!(k=a.getAttribute("fid")||a.getAttribute("id")));)a=a.nextSibling;h.fid=k;return h},parseGeometry:{point:function(a){var b,c;c=[];b=this.getElementsByTagNameNS(a,this.gmlns,"pos");0<b.length&&(c=b[0].firstChild.nodeValue,c=c.replace(this.regExes.trimSpace,""),c=c.split(this.regExes.splitSpace));0==c.length&&(b=this.getElementsByTagNameNS(a,
this.gmlns,"coordinates"),0<b.length&&(c=b[0].firstChild.nodeValue,c=c.replace(this.regExes.removeSpace,""),c=c.split(",")));0==c.length&&(b=this.getElementsByTagNameNS(a,this.gmlns,"coord"),0<b.length&&(a=this.getElementsByTagNameNS(b[0],this.gmlns,"X"),b=this.getElementsByTagNameNS(b[0],this.gmlns,"Y"),0<a.length&&0<b.length&&(c=[a[0].firstChild.nodeValue,b[0].firstChild.nodeValue])));2==c.length&&(c[2]=null);return this.xy?new OpenLayers.Geometry.Point(c[0],c[1],c[2]):new OpenLayers.Geometry.Point(c[1],
c[0],c[2])},multipoint:function(a){a=this.getElementsByTagNameNS(a,this.gmlns,"Point");var b=[];if(0<a.length)for(var c,d=0;d<a.length;++d)(c=this.parseGeometry.point.apply(this,[a[d]]))&&b.push(c);return new OpenLayers.Geometry.MultiPoint(b)},linestring:function(a,b){var c,d;d=[];var e=[];c=this.getElementsByTagNameNS(a,this.gmlns,"posList");if(0<c.length){d=this.getChildValue(c[0]);d=d.replace(this.regExes.trimSpace,"");d=d.split(this.regExes.splitSpace);var f=parseInt(c[0].getAttribute("dimension")),
g,h,k;for(c=0;c<d.length/f;++c)g=c*f,h=d[g],k=d[g+1],g=2==f?null:d[g+2],this.xy?e.push(new OpenLayers.Geometry.Point(h,k,g)):e.push(new OpenLayers.Geometry.Point(k,h,g))}if(0==d.length&&(c=this.getElementsByTagNameNS(a,this.gmlns,"coordinates"),0<c.length))for(d=this.getChildValue(c[0]),d=d.replace(this.regExes.trimSpace,""),d=d.replace(this.regExes.trimComma,","),f=d.split(this.regExes.splitSpace),c=0;c<f.length;++c)d=f[c].split(","),2==d.length&&(d[2]=null),this.xy?e.push(new OpenLayers.Geometry.Point(d[0],
d[1],d[2])):e.push(new OpenLayers.Geometry.Point(d[1],d[0],d[2]));d=null;0!=e.length&&(d=b?new OpenLayers.Geometry.LinearRing(e):new OpenLayers.Geometry.LineString(e));return d},multilinestring:function(a){a=this.getElementsByTagNameNS(a,this.gmlns,"LineString");var b=[];if(0<a.length)for(var c,d=0;d<a.length;++d)(c=this.parseGeometry.linestring.apply(this,[a[d]]))&&b.push(c);return new OpenLayers.Geometry.MultiLineString(b)},polygon:function(a){a=this.getElementsByTagNameNS(a,this.gmlns,"LinearRing");
var b=[];if(0<a.length)for(var c,d=0;d<a.length;++d)(c=this.parseGeometry.linestring.apply(this,[a[d],!0]))&&b.push(c);return new OpenLayers.Geometry.Polygon(b)},multipolygon:function(a){a=this.getElementsByTagNameNS(a,this.gmlns,"Polygon");var b=[];if(0<a.length)for(var c,d=0;d<a.length;++d)(c=this.parseGeometry.polygon.apply(this,[a[d]]))&&b.push(c);return new OpenLayers.Geometry.MultiPolygon(b)},envelope:function(a){var b=[],c,d,e=this.getElementsByTagNameNS(a,this.gmlns,"lowerCorner");if(0<e.length){c=
[];0<e.length&&(c=e[0].firstChild.nodeValue,c=c.replace(this.regExes.trimSpace,""),c=c.split(this.regExes.splitSpace));2==c.length&&(c[2]=null);var f=this.xy?new OpenLayers.Geometry.Point(c[0],c[1],c[2]):new OpenLayers.Geometry.Point(c[1],c[0],c[2])}a=this.getElementsByTagNameNS(a,this.gmlns,"upperCorner");if(0<a.length){c=[];0<a.length&&(c=a[0].firstChild.nodeValue,c=c.replace(this.regExes.trimSpace,""),c=c.split(this.regExes.splitSpace));2==c.length&&(c[2]=null);var g=this.xy?new OpenLayers.Geometry.Point(c[0],
c[1],c[2]):new OpenLayers.Geometry.Point(c[1],c[0],c[2])}f&&g&&(b.push(new OpenLayers.Geometry.Point(f.x,f.y)),b.push(new OpenLayers.Geometry.Point(g.x,f.y)),b.push(new OpenLayers.Geometry.Point(g.x,g.y)),b.push(new OpenLayers.Geometry.Point(f.x,g.y)),b.push(new OpenLayers.Geometry.Point(f.x,f.y)),b=new OpenLayers.Geometry.LinearRing(b),d=new OpenLayers.Geometry.Polygon([b]));return d},box:function(a){var b=this.getElementsByTagNameNS(a,this.gmlns,"coordinates"),c=a=null;0<b.length&&(b=b[0].firstChild.nodeValue,
b=b.split(" "),2==b.length&&(a=b[0].split(","),c=b[1].split(",")));if(null!==a&&null!==c)return new OpenLayers.Bounds(parseFloat(a[0]),parseFloat(a[1]),parseFloat(c[0]),parseFloat(c[1]))}},parseAttributes:function(a){var b={};a=a.firstChild;for(var c,d,e;a;){if(1==a.nodeType){a=a.childNodes;for(c=0;c<a.length;++c)if(d=a[c],1==d.nodeType)if(e=d.childNodes,1==e.length){if(e=e[0],3==e.nodeType||4==e.nodeType)d=d.prefix?d.nodeName.split(":")[1]:d.nodeName,e=e.nodeValue.replace(this.regExes.trimSpace,
""),b[d]=e}else b[d.nodeName.split(":").pop()]=null;break}a=a.nextSibling}return b},write:function(a){OpenLayers.Util.isArray(a)||(a=[a]);for(var b=this.createElementNS("http://www.opengis.net/wfs","wfs:"+this.collectionName),c=0;c<a.length;c++)b.appendChild(this.createFeatureXML(a[c]));return OpenLayers.Format.XML.prototype.write.apply(this,[b])},createFeatureXML:function(a){var b=this.buildGeometryNode(a.geometry),c=this.createElementNS(this.featureNS,this.featurePrefix+":"+this.geometryName);c.appendChild(b);
var b=this.createElementNS(this.gmlns,"gml:"+this.featureName),d=this.createElementNS(this.featureNS,this.featurePrefix+":"+this.layerName);d.setAttribute("fid",a.fid||a.id);d.appendChild(c);for(var e in a.attributes){var c=this.createTextNode(a.attributes[e]),f=e.substring(e.lastIndexOf(":")+1),f=this.createElementNS(this.featureNS,this.featurePrefix+":"+f);f.appendChild(c);d.appendChild(f)}b.appendChild(d);return b},buildGeometryNode:function(a){this.externalProjection&&this.internalProjection&&
(a=a.clone(),a.transform(this.internalProjection,this.externalProjection));var b=a.CLASS_NAME,b=b.substring(b.lastIndexOf(".")+1);return this.buildGeometry[b.toLowerCase()].apply(this,[a])},buildGeometry:{point:function(a){var b=this.createElementNS(this.gmlns,"gml:Point");b.appendChild(this.buildCoordinatesNode(a));return b},multipoint:function(a){var b=this.createElementNS(this.gmlns,"gml:MultiPoint");a=a.components;for(var c,d,e=0;e<a.length;e++)c=this.createElementNS(this.gmlns,"gml:pointMember"),
d=this.buildGeometry.point.apply(this,[a[e]]),c.appendChild(d),b.appendChild(c);return b},linestring:function(a){var b=this.createElementNS(this.gmlns,"gml:LineString");b.appendChild(this.buildCoordinatesNode(a));return b},multilinestring:function(a){var b=this.createElementNS(this.gmlns,"gml:MultiLineString");a=a.components;for(var c,d,e=0;e<a.length;++e)c=this.createElementNS(this.gmlns,"gml:lineStringMember"),d=this.buildGeometry.linestring.apply(this,[a[e]]),c.appendChild(d),b.appendChild(c);
return b},linearring:function(a){var b=this.createElementNS(this.gmlns,"gml:LinearRing");b.appendChild(this.buildCoordinatesNode(a));return b},polygon:function(a){var b=this.createElementNS(this.gmlns,"gml:Polygon");a=a.components;for(var c,d,e=0;e<a.length;++e)c=0==e?"outerBoundaryIs":"innerBoundaryIs",c=this.createElementNS(this.gmlns,"gml:"+c),d=this.buildGeometry.linearring.apply(this,[a[e]]),c.appendChild(d),b.appendChild(c);return b},multipolygon:function(a){var b=this.createElementNS(this.gmlns,
"gml:MultiPolygon");a=a.components;for(var c,d,e=0;e<a.length;++e)c=this.createElementNS(this.gmlns,"gml:polygonMember"),d=this.buildGeometry.polygon.apply(this,[a[e]]),c.appendChild(d),b.appendChild(c);return b},bounds:function(a){var b=this.createElementNS(this.gmlns,"gml:Box");b.appendChild(this.buildCoordinatesNode(a));return b}},buildCoordinatesNode:function(a){var b=this.createElementNS(this.gmlns,"gml:coordinates");b.setAttribute("decimal",".");b.setAttribute("cs",",");b.setAttribute("ts",
" ");var c=[];if(a instanceof OpenLayers.Bounds)c.push(a.left+","+a.bottom),c.push(a.right+","+a.top);else{a=a.components?a.components:[a];for(var d=0;d<a.length;d++)c.push(a[d].x+","+a[d].y)}c=this.createTextNode(c.join(" "));b.appendChild(c);return b},CLASS_NAME:"OpenLayers.Format.GML"});OpenLayers.Format.GML||(OpenLayers.Format.GML={});
OpenLayers.Format.GML.Base=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{gml:"http://www.opengis.net/gml",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance",wfs:"http://www.opengis.net/wfs"},defaultPrefix:"gml",schemaLocation:null,featureType:null,featureNS:null,geometryName:"geometry",extractAttributes:!0,srsName:null,xy:!0,geometryTypes:null,singleFeatureType:null,regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g,featureMember:/^(.*:)?featureMembers?$/},
initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a]);this.setGeometryTypes();a&&a.featureNS&&this.setNamespace("feature",a.featureNS);this.singleFeatureType=!a||"string"===typeof a.featureType},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b=[];this.readNode(a,{features:b},!0);if(0==b.length){var c=this.getElementsByTagNameNS(a,this.namespaces.gml,"featureMember");if(c.length){a=
0;for(var d=c.length;a<d;++a)this.readNode(c[a],{features:b},!0)}else c=this.getElementsByTagNameNS(a,this.namespaces.gml,"featureMembers"),c.length&&this.readNode(c[0],{features:b},!0)}return b},readNode:function(a,b,c){!0===c&&!0===this.autoConfig&&(this.featureType=null,delete this.namespaceAlias[this.featureNS],delete this.namespaces.feature,this.featureNS=null);this.featureNS||(a.prefix in this.namespaces||a.parentNode.namespaceURI!=this.namespaces.gml||!this.regExes.featureMember.test(a.parentNode.nodeName))||
(this.featureType=a.nodeName.split(":").pop(),this.setNamespace("feature",a.namespaceURI),this.featureNS=a.namespaceURI,this.autoConfig=!0);return OpenLayers.Format.XML.prototype.readNode.apply(this,[a,b])},readers:{gml:{_inherit:function(a,b,c){},featureMember:function(a,b){this.readChildNodes(a,b)},featureMembers:function(a,b){this.readChildNodes(a,b)},name:function(a,b){b.name=this.getChildValue(a)},boundedBy:function(a,b){var c={};this.readChildNodes(a,c);c.components&&0<c.components.length&&
(b.bounds=c.components[0])},Point:function(a,b){var c={points:[]};this.readChildNodes(a,c);b.components||(b.components=[]);b.components.push(c.points[0])},coordinates:function(a,b){for(var c=this.getChildValue(a).replace(this.regExes.trimSpace,""),c=c.replace(this.regExes.trimComma,","),c=c.split(this.regExes.splitSpace),d,e=c.length,f=Array(e),g=0;g<e;++g)d=c[g].split(","),f[g]=this.xy?new OpenLayers.Geometry.Point(d[0],d[1],d[2]):new OpenLayers.Geometry.Point(d[1],d[0],d[2]);b.points=f},coord:function(a,
b){var c={};this.readChildNodes(a,c);b.points||(b.points=[]);b.points.push(new OpenLayers.Geometry.Point(c.x,c.y,c.z))},X:function(a,b){b.x=this.getChildValue(a)},Y:function(a,b){b.y=this.getChildValue(a)},Z:function(a,b){b.z=this.getChildValue(a)},MultiPoint:function(a,b){var c={components:[]};this.readers.gml._inherit.apply(this,[a,c,b]);this.readChildNodes(a,c);b.components=[new OpenLayers.Geometry.MultiPoint(c.components)]},pointMember:function(a,b){this.readChildNodes(a,b)},LineString:function(a,
b){var c={};this.readers.gml._inherit.apply(this,[a,c,b]);this.readChildNodes(a,c);b.components||(b.components=[]);b.components.push(new OpenLayers.Geometry.LineString(c.points))},MultiLineString:function(a,b){var c={components:[]};this.readers.gml._inherit.apply(this,[a,c,b]);this.readChildNodes(a,c);b.components=[new OpenLayers.Geometry.MultiLineString(c.components)]},lineStringMember:function(a,b){this.readChildNodes(a,b)},Polygon:function(a,b){var c={outer:null,inner:[]};this.readers.gml._inherit.apply(this,
[a,c,b]);this.readChildNodes(a,c);c.inner.unshift(c.outer);b.components||(b.components=[]);b.components.push(new OpenLayers.Geometry.Polygon(c.inner))},LinearRing:function(a,b){var c={};this.readers.gml._inherit.apply(this,[a,c]);this.readChildNodes(a,c);b.components=[new OpenLayers.Geometry.LinearRing(c.points)]},MultiPolygon:function(a,b){var c={components:[]};this.readers.gml._inherit.apply(this,[a,c,b]);this.readChildNodes(a,c);b.components=[new OpenLayers.Geometry.MultiPolygon(c.components)]},
polygonMember:function(a,b){this.readChildNodes(a,b)},GeometryCollection:function(a,b){var c={components:[]};this.readers.gml._inherit.apply(this,[a,c,b]);this.readChildNodes(a,c);b.components=[new OpenLayers.Geometry.Collection(c.components)]},geometryMember:function(a,b){this.readChildNodes(a,b)}},feature:{"*":function(a,b){var c,d=a.localName||a.nodeName.split(":").pop();b.features?this.singleFeatureType||-1===OpenLayers.Util.indexOf(this.featureType,d)?d===this.featureType&&(c="_typeName"):c=
"_typeName":0==a.childNodes.length||1==a.childNodes.length&&3==a.firstChild.nodeType?this.extractAttributes&&(c="_attribute"):c="_geometry";c&&this.readers.feature[c].apply(this,[a,b])},_typeName:function(a,b){var c={components:[],attributes:{}};this.readChildNodes(a,c);c.name&&(c.attributes.name=c.name);var d=new OpenLayers.Feature.Vector(c.components[0],c.attributes);this.singleFeatureType||(d.type=a.nodeName.split(":").pop(),d.namespace=a.namespaceURI);var e=a.getAttribute("fid")||this.getAttributeNS(a,
this.namespaces.gml,"id");e&&(d.fid=e);this.internalProjection&&(this.externalProjection&&d.geometry)&&d.geometry.transform(this.externalProjection,this.internalProjection);c.bounds&&(d.bounds=c.bounds);b.features.push(d)},_geometry:function(a,b){this.geometryName||(this.geometryName=a.nodeName.split(":").pop());this.readChildNodes(a,b)},_attribute:function(a,b){var c=a.localName||a.nodeName.split(":").pop(),d=this.getChildValue(a);b.attributes[c]=d}},wfs:{FeatureCollection:function(a,b){this.readChildNodes(a,
b)}}},write:function(a){var b;b=OpenLayers.Util.isArray(a)?"featureMembers":"featureMember";a=this.writeNode("gml:"+b,a);this.setAttributeNS(a,this.namespaces.xsi,"xsi:schemaLocation",this.schemaLocation);return OpenLayers.Format.XML.prototype.write.apply(this,[a])},writers:{gml:{featureMember:function(a){var b=this.createElementNSPlus("gml:featureMember");this.writeNode("feature:_typeName",a,b);return b},MultiPoint:function(a){var b=this.createElementNSPlus("gml:MultiPoint");a=a.components||[a];
for(var c=0,d=a.length;c<d;++c)this.writeNode("pointMember",a[c],b);return b},pointMember:function(a){var b=this.createElementNSPlus("gml:pointMember");this.writeNode("Point",a,b);return b},MultiLineString:function(a){var b=this.createElementNSPlus("gml:MultiLineString");a=a.components||[a];for(var c=0,d=a.length;c<d;++c)this.writeNode("lineStringMember",a[c],b);return b},lineStringMember:function(a){var b=this.createElementNSPlus("gml:lineStringMember");this.writeNode("LineString",a,b);return b},
MultiPolygon:function(a){var b=this.createElementNSPlus("gml:MultiPolygon");a=a.components||[a];for(var c=0,d=a.length;c<d;++c)this.writeNode("polygonMember",a[c],b);return b},polygonMember:function(a){var b=this.createElementNSPlus("gml:polygonMember");this.writeNode("Polygon",a,b);return b},GeometryCollection:function(a){for(var b=this.createElementNSPlus("gml:GeometryCollection"),c=0,d=a.components.length;c<d;++c)this.writeNode("geometryMember",a.components[c],b);return b},geometryMember:function(a){var b=
this.createElementNSPlus("gml:geometryMember");a=this.writeNode("feature:_geometry",a);b.appendChild(a.firstChild);return b}},feature:{_typeName:function(a){var b=this.createElementNSPlus("feature:"+this.featureType,{attributes:{fid:a.fid}});a.geometry&&this.writeNode("feature:_geometry",a.geometry,b);for(var c in a.attributes){var d=a.attributes[c];null!=d&&this.writeNode("feature:_attribute",{name:c,value:d},b)}return b},_geometry:function(a){this.externalProjection&&this.internalProjection&&(a=
a.clone().transform(this.internalProjection,this.externalProjection));var b=this.createElementNSPlus("feature:"+this.geometryName);a=this.writeNode("gml:"+this.geometryTypes[a.CLASS_NAME],a,b);this.srsName&&a.setAttribute("srsName",this.srsName);return b},_attribute:function(a){return this.createElementNSPlus("feature:"+a.name,{value:a.value})}},wfs:{FeatureCollection:function(a){for(var b=this.createElementNSPlus("wfs:FeatureCollection"),c=0,d=a.length;c<d;++c)this.writeNode("gml:featureMember",
a[c],b);return b}}},setGeometryTypes:function(){this.geometryTypes={"OpenLayers.Geometry.Point":"Point","OpenLayers.Geometry.MultiPoint":"MultiPoint","OpenLayers.Geometry.LineString":"LineString","OpenLayers.Geometry.MultiLineString":"MultiLineString","OpenLayers.Geometry.Polygon":"Polygon","OpenLayers.Geometry.MultiPolygon":"MultiPolygon","OpenLayers.Geometry.Collection":"GeometryCollection"}},CLASS_NAME:"OpenLayers.Format.GML.Base"});OpenLayers.Format.GML.v3=OpenLayers.Class(OpenLayers.Format.GML.Base,{schemaLocation:"http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd",curve:!1,multiCurve:!0,surface:!1,multiSurface:!0,initialize:function(a){OpenLayers.Format.GML.Base.prototype.initialize.apply(this,[a])},readers:{gml:OpenLayers.Util.applyDefaults({_inherit:function(a,b,c){if(a=parseInt(a.getAttribute("srsDimension"),10)||c&&c.srsDimension)b.srsDimension=a},featureMembers:function(a,
b){this.readChildNodes(a,b)},Curve:function(a,b){var c={points:[]};this.readers.gml._inherit.apply(this,[a,c,b]);this.readChildNodes(a,c);b.components||(b.components=[]);b.components.push(new OpenLayers.Geometry.LineString(c.points))},segments:function(a,b){this.readChildNodes(a,b)},LineStringSegment:function(a,b){var c={};this.readChildNodes(a,c);c.points&&Array.prototype.push.apply(b.points,c.points)},pos:function(a,b){var c=this.getChildValue(a).replace(this.regExes.trimSpace,"").split(this.regExes.splitSpace),
c=this.xy?new OpenLayers.Geometry.Point(c[0],c[1],c[2]):new OpenLayers.Geometry.Point(c[1],c[0],c[2]);b.points=[c]},posList:function(a,b){for(var c=this.getChildValue(a).replace(this.regExes.trimSpace,"").split(this.regExes.splitSpace),d=b.srsDimension||parseInt(a.getAttribute("srsDimension")||a.getAttribute("dimension"),10)||2,e,f,g,h=Array(c.length/d),k=0,l=c.length;k<l;k+=d)e=c[k],f=c[k+1],g=2==d?void 0:c[k+2],h[k/d]=this.xy?new OpenLayers.Geometry.Point(e,f,g):new OpenLayers.Geometry.Point(f,
e,g);b.points=h},Surface:function(a,b){this.readChildNodes(a,b)},patches:function(a,b){this.readChildNodes(a,b)},PolygonPatch:function(a,b){this.readers.gml.Polygon.apply(this,[a,b])},exterior:function(a,b){var c={};this.readChildNodes(a,c);b.outer=c.components[0]},interior:function(a,b){var c={};this.readChildNodes(a,c);b.inner.push(c.components[0])},MultiCurve:function(a,b){var c={components:[]};this.readers.gml._inherit.apply(this,[a,c,b]);this.readChildNodes(a,c);0<c.components.length&&(b.components=
[new OpenLayers.Geometry.MultiLineString(c.components)])},curveMember:function(a,b){this.readChildNodes(a,b)},MultiSurface:function(a,b){var c={components:[]};this.readers.gml._inherit.apply(this,[a,c,b]);this.readChildNodes(a,c);0<c.components.length&&(b.components=[new OpenLayers.Geometry.MultiPolygon(c.components)])},surfaceMember:function(a,b){this.readChildNodes(a,b)},surfaceMembers:function(a,b){this.readChildNodes(a,b)},pointMembers:function(a,b){this.readChildNodes(a,b)},lineStringMembers:function(a,
b){this.readChildNodes(a,b)},polygonMembers:function(a,b){this.readChildNodes(a,b)},geometryMembers:function(a,b){this.readChildNodes(a,b)},Envelope:function(a,b){var c={points:Array(2)};this.readChildNodes(a,c);b.components||(b.components=[]);var d=c.points[0],c=c.points[1];b.components.push(new OpenLayers.Bounds(d.x,d.y,c.x,c.y))},lowerCorner:function(a,b){var c={};this.readers.gml.pos.apply(this,[a,c]);b.points[0]=c.points[0]},upperCorner:function(a,b){var c={};this.readers.gml.pos.apply(this,
[a,c]);b.points[1]=c.points[0]}},OpenLayers.Format.GML.Base.prototype.readers.gml),feature:OpenLayers.Format.GML.Base.prototype.readers.feature,wfs:OpenLayers.Format.GML.Base.prototype.readers.wfs},write:function(a){var b;b=OpenLayers.Util.isArray(a)?"featureMembers":"featureMember";a=this.writeNode("gml:"+b,a);this.setAttributeNS(a,this.namespaces.xsi,"xsi:schemaLocation",this.schemaLocation);return OpenLayers.Format.XML.prototype.write.apply(this,[a])},writers:{gml:OpenLayers.Util.applyDefaults({featureMembers:function(a){for(var b=
this.createElementNSPlus("gml:featureMembers"),c=0,d=a.length;c<d;++c)this.writeNode("feature:_typeName",a[c],b);return b},Point:function(a){var b=this.createElementNSPlus("gml:Point");this.writeNode("pos",a,b);return b},pos:function(a){return this.createElementNSPlus("gml:pos",{value:this.xy?a.x+" "+a.y:a.y+" "+a.x})},LineString:function(a){var b=this.createElementNSPlus("gml:LineString");this.writeNode("posList",a.components,b);return b},Curve:function(a){var b=this.createElementNSPlus("gml:Curve");
this.writeNode("segments",a,b);return b},segments:function(a){var b=this.createElementNSPlus("gml:segments");this.writeNode("LineStringSegment",a,b);return b},LineStringSegment:function(a){var b=this.createElementNSPlus("gml:LineStringSegment");this.writeNode("posList",a.components,b);return b},posList:function(a){for(var b=a.length,c=Array(b),d,e=0;e<b;++e)d=a[e],c[e]=this.xy?d.x+" "+d.y:d.y+" "+d.x;return this.createElementNSPlus("gml:posList",{value:c.join(" ")})},Surface:function(a){var b=this.createElementNSPlus("gml:Surface");
this.writeNode("patches",a,b);return b},patches:function(a){var b=this.createElementNSPlus("gml:patches");this.writeNode("PolygonPatch",a,b);return b},PolygonPatch:function(a){var b=this.createElementNSPlus("gml:PolygonPatch",{attributes:{interpolation:"planar"}});this.writeNode("exterior",a.components[0],b);for(var c=1,d=a.components.length;c<d;++c)this.writeNode("interior",a.components[c],b);return b},Polygon:function(a){var b=this.createElementNSPlus("gml:Polygon");this.writeNode("exterior",a.components[0],
b);for(var c=1,d=a.components.length;c<d;++c)this.writeNode("interior",a.components[c],b);return b},exterior:function(a){var b=this.createElementNSPlus("gml:exterior");this.writeNode("LinearRing",a,b);return b},interior:function(a){var b=this.createElementNSPlus("gml:interior");this.writeNode("LinearRing",a,b);return b},LinearRing:function(a){var b=this.createElementNSPlus("gml:LinearRing");this.writeNode("posList",a.components,b);return b},MultiCurve:function(a){var b=this.createElementNSPlus("gml:MultiCurve");
a=a.components||[a];for(var c=0,d=a.length;c<d;++c)this.writeNode("curveMember",a[c],b);return b},curveMember:function(a){var b=this.createElementNSPlus("gml:curveMember");this.curve?this.writeNode("Curve",a,b):this.writeNode("LineString",a,b);return b},MultiSurface:function(a){var b=this.createElementNSPlus("gml:MultiSurface");a=a.components||[a];for(var c=0,d=a.length;c<d;++c)this.writeNode("surfaceMember",a[c],b);return b},surfaceMember:function(a){var b=this.createElementNSPlus("gml:surfaceMember");
this.surface?this.writeNode("Surface",a,b):this.writeNode("Polygon",a,b);return b},Envelope:function(a){var b=this.createElementNSPlus("gml:Envelope");this.writeNode("lowerCorner",a,b);this.writeNode("upperCorner",a,b);this.srsName&&b.setAttribute("srsName",this.srsName);return b},lowerCorner:function(a){return this.createElementNSPlus("gml:lowerCorner",{value:this.xy?a.left+" "+a.bottom:a.bottom+" "+a.left})},upperCorner:function(a){return this.createElementNSPlus("gml:upperCorner",{value:this.xy?
a.right+" "+a.top:a.top+" "+a.right})}},OpenLayers.Format.GML.Base.prototype.writers.gml),feature:OpenLayers.Format.GML.Base.prototype.writers.feature,wfs:OpenLayers.Format.GML.Base.prototype.writers.wfs},setGeometryTypes:function(){this.geometryTypes={"OpenLayers.Geometry.Point":"Point","OpenLayers.Geometry.MultiPoint":"MultiPoint","OpenLayers.Geometry.LineString":!0===this.curve?"Curve":"LineString","OpenLayers.Geometry.MultiLineString":!1===this.multiCurve?"MultiLineString":"MultiCurve","OpenLayers.Geometry.Polygon":!0===
this.surface?"Surface":"Polygon","OpenLayers.Geometry.MultiPolygon":!1===this.multiSurface?"MultiPolygon":"MultiSurface","OpenLayers.Geometry.Collection":"GeometryCollection"}},CLASS_NAME:"OpenLayers.Format.GML.v3"});OpenLayers.Format.Filter.v1_1_0=OpenLayers.Class(OpenLayers.Format.GML.v3,OpenLayers.Format.Filter.v1,{VERSION:"1.1.0",schemaLocation:"http://www.opengis.net/ogc/filter/1.1.0/filter.xsd",initialize:function(a){OpenLayers.Format.GML.v3.prototype.initialize.apply(this,[a])},readers:{ogc:OpenLayers.Util.applyDefaults({PropertyIsEqualTo:function(a,b){var c=a.getAttribute("matchCase"),c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.EQUAL_TO,matchCase:!("false"===c||"0"===c)});this.readChildNodes(a,
c);b.filters.push(c)},PropertyIsNotEqualTo:function(a,b){var c=a.getAttribute("matchCase"),c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.NOT_EQUAL_TO,matchCase:!("false"===c||"0"===c)});this.readChildNodes(a,c);b.filters.push(c)},PropertyIsLike:function(a,b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.LIKE});this.readChildNodes(a,c);var d=a.getAttribute("wildCard"),e=a.getAttribute("singleChar"),f=a.getAttribute("escapeChar");c.value2regex(d,e,
f);b.filters.push(c)}},OpenLayers.Format.Filter.v1.prototype.readers.ogc),gml:OpenLayers.Format.GML.v3.prototype.readers.gml,feature:OpenLayers.Format.GML.v3.prototype.readers.feature},writers:{ogc:OpenLayers.Util.applyDefaults({PropertyIsEqualTo:function(a){var b=this.createElementNSPlus("ogc:PropertyIsEqualTo",{attributes:{matchCase:a.matchCase}});this.writeNode("PropertyName",a,b);this.writeOgcExpression(a.value,b);return b},PropertyIsNotEqualTo:function(a){var b=this.createElementNSPlus("ogc:PropertyIsNotEqualTo",
{attributes:{matchCase:a.matchCase}});this.writeNode("PropertyName",a,b);this.writeOgcExpression(a.value,b);return b},PropertyIsLike:function(a){var b=this.createElementNSPlus("ogc:PropertyIsLike",{attributes:{matchCase:a.matchCase,wildCard:"*",singleChar:".",escapeChar:"!"}});this.writeNode("PropertyName",a,b);this.writeNode("Literal",a.regex2value(),b);return b},BBOX:function(a){var b=this.createElementNSPlus("ogc:BBOX");a.property&&this.writeNode("PropertyName",a,b);var c=this.writeNode("gml:Envelope",
a.value);a.projection&&c.setAttribute("srsName",a.projection);b.appendChild(c);return b},SortBy:function(a){for(var b=this.createElementNSPlus("ogc:SortBy"),c=0,d=a.length;c<d;c++)this.writeNode("ogc:SortProperty",a[c],b);return b},SortProperty:function(a){var b=this.createElementNSPlus("ogc:SortProperty");this.writeNode("ogc:PropertyName",a,b);this.writeNode("ogc:SortOrder","DESC"==a.order?"DESC":"ASC",b);return b},SortOrder:function(a){return this.createElementNSPlus("ogc:SortOrder",{value:a})}},
OpenLayers.Format.Filter.v1.prototype.writers.ogc),gml:OpenLayers.Format.GML.v3.prototype.writers.gml,feature:OpenLayers.Format.GML.v3.prototype.writers.feature},writeSpatial:function(a,b){var c=this.createElementNSPlus("ogc:"+b);this.writeNode("PropertyName",a,c);if(a.value instanceof OpenLayers.Filter.Function)this.writeNode("Function",a.value,c);else{var d;d=a.value instanceof OpenLayers.Geometry?this.writeNode("feature:_geometry",a.value).firstChild:this.writeNode("gml:Envelope",a.value);a.projection&&
d.setAttribute("srsName",a.projection);c.appendChild(d)}return c},CLASS_NAME:"OpenLayers.Format.Filter.v1_1_0"});OpenLayers.Format.OWSCommon=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.0.0",getVersion:function(a,b){var c=this.version;if(!c){var d=a.getAttribute("xmlns:ows");d&&"1.1"===d.substring(d.lastIndexOf("/")+1)&&(c="1.1.0");c||(c=this.defaultVersion)}return c},CLASS_NAME:"OpenLayers.Format.OWSCommon"});OpenLayers.Format.OWSCommon.v1=OpenLayers.Class(OpenLayers.Format.XML,{regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},read:function(a,b){OpenLayers.Util.applyDefaults(b,this.options);var c={};this.readChildNodes(a,c);return c},readers:{ows:{Exception:function(a,b){var c={code:a.getAttribute("exceptionCode"),locator:a.getAttribute("locator"),texts:[]};b.exceptions.push(c);this.readChildNodes(a,c)},ExceptionText:function(a,b){var c=this.getChildValue(a);b.texts.push(c)},
ServiceIdentification:function(a,b){b.serviceIdentification={};this.readChildNodes(a,b.serviceIdentification)},Title:function(a,b){b.title=this.getChildValue(a)},Abstract:function(a,b){b["abstract"]=this.getChildValue(a)},Keywords:function(a,b){b.keywords={};this.readChildNodes(a,b.keywords)},Keyword:function(a,b){b[this.getChildValue(a)]=!0},ServiceType:function(a,b){b.serviceType={codeSpace:a.getAttribute("codeSpace"),value:this.getChildValue(a)}},ServiceTypeVersion:function(a,b){b.serviceTypeVersion=
this.getChildValue(a)},Fees:function(a,b){b.fees=this.getChildValue(a)},AccessConstraints:function(a,b){b.accessConstraints=this.getChildValue(a)},ServiceProvider:function(a,b){b.serviceProvider={};this.readChildNodes(a,b.serviceProvider)},ProviderName:function(a,b){b.providerName=this.getChildValue(a)},ProviderSite:function(a,b){b.providerSite=this.getAttributeNS(a,this.namespaces.xlink,"href")},ServiceContact:function(a,b){b.serviceContact={};this.readChildNodes(a,b.serviceContact)},IndividualName:function(a,
b){b.individualName=this.getChildValue(a)},PositionName:function(a,b){b.positionName=this.getChildValue(a)},ContactInfo:function(a,b){b.contactInfo={};this.readChildNodes(a,b.contactInfo)},Phone:function(a,b){b.phone={};this.readChildNodes(a,b.phone)},Voice:function(a,b){b.voice=this.getChildValue(a)},Address:function(a,b){b.address={};this.readChildNodes(a,b.address)},DeliveryPoint:function(a,b){b.deliveryPoint=this.getChildValue(a)},City:function(a,b){b.city=this.getChildValue(a)},AdministrativeArea:function(a,
b){b.administrativeArea=this.getChildValue(a)},PostalCode:function(a,b){b.postalCode=this.getChildValue(a)},Country:function(a,b){b.country=this.getChildValue(a)},ElectronicMailAddress:function(a,b){b.electronicMailAddress=this.getChildValue(a)},Role:function(a,b){b.role=this.getChildValue(a)},OperationsMetadata:function(a,b){b.operationsMetadata={};this.readChildNodes(a,b.operationsMetadata)},Operation:function(a,b){var c=a.getAttribute("name");b[c]={};this.readChildNodes(a,b[c])},DCP:function(a,
b){b.dcp={};this.readChildNodes(a,b.dcp)},HTTP:function(a,b){b.http={};this.readChildNodes(a,b.http)},Get:function(a,b){b.get||(b.get=[]);var c={url:this.getAttributeNS(a,this.namespaces.xlink,"href")};this.readChildNodes(a,c);b.get.push(c)},Post:function(a,b){b.post||(b.post=[]);var c={url:this.getAttributeNS(a,this.namespaces.xlink,"href")};this.readChildNodes(a,c);b.post.push(c)},Parameter:function(a,b){b.parameters||(b.parameters={});var c=a.getAttribute("name");b.parameters[c]={};this.readChildNodes(a,
b.parameters[c])},Constraint:function(a,b){b.constraints||(b.constraints={});var c=a.getAttribute("name");b.constraints[c]={};this.readChildNodes(a,b.constraints[c])},Value:function(a,b){b[this.getChildValue(a)]=!0},OutputFormat:function(a,b){b.formats.push({value:this.getChildValue(a)});this.readChildNodes(a,b)},WGS84BoundingBox:function(a,b){var c={};c.crs=a.getAttribute("crs");b.BoundingBox?b.BoundingBox.push(c):(b.projection=c.crs,c=b);this.readChildNodes(a,c)},BoundingBox:function(a,b){this.readers.ows.WGS84BoundingBox.apply(this,
[a,b])},LowerCorner:function(a,b){var c=this.getChildValue(a).replace(this.regExes.trimSpace,""),c=c.replace(this.regExes.trimComma,","),c=c.split(this.regExes.splitSpace);b.left=c[0];b.bottom=c[1]},UpperCorner:function(a,b){var c=this.getChildValue(a).replace(this.regExes.trimSpace,""),c=c.replace(this.regExes.trimComma,","),c=c.split(this.regExes.splitSpace);b.right=c[0];b.top=c[1];b.bounds=new OpenLayers.Bounds(b.left,b.bottom,b.right,b.top);delete b.left;delete b.bottom;delete b.right;delete b.top},
Language:function(a,b){b.language=this.getChildValue(a)}}},writers:{ows:{BoundingBox:function(a,b){var c=this.createElementNSPlus(b||"ows:BoundingBox",{attributes:{crs:a.projection}});this.writeNode("ows:LowerCorner",a,c);this.writeNode("ows:UpperCorner",a,c);return c},LowerCorner:function(a){return this.createElementNSPlus("ows:LowerCorner",{value:a.bounds.left+" "+a.bounds.bottom})},UpperCorner:function(a){return this.createElementNSPlus("ows:UpperCorner",{value:a.bounds.right+" "+a.bounds.top})},
Identifier:function(a){return this.createElementNSPlus("ows:Identifier",{value:a})},Title:function(a){return this.createElementNSPlus("ows:Title",{value:a})},Abstract:function(a){return this.createElementNSPlus("ows:Abstract",{value:a})},OutputFormat:function(a){return this.createElementNSPlus("ows:OutputFormat",{value:a})}}},CLASS_NAME:"OpenLayers.Format.OWSCommon.v1"});OpenLayers.Format.OWSCommon.v1_0_0=OpenLayers.Class(OpenLayers.Format.OWSCommon.v1,{namespaces:{ows:"http://www.opengis.net/ows",xlink:"http://www.w3.org/1999/xlink"},readers:{ows:OpenLayers.Util.applyDefaults({ExceptionReport:function(a,b){b.success=!1;b.exceptionReport={version:a.getAttribute("version"),language:a.getAttribute("language"),exceptions:[]};this.readChildNodes(a,b.exceptionReport)}},OpenLayers.Format.OWSCommon.v1.prototype.readers.ows)},writers:{ows:OpenLayers.Format.OWSCommon.v1.prototype.writers.ows},
CLASS_NAME:"OpenLayers.Format.OWSCommon.v1_0_0"});OpenLayers.Format.WFST.v1_1_0=OpenLayers.Class(OpenLayers.Format.Filter.v1_1_0,OpenLayers.Format.WFST.v1,{version:"1.1.0",schemaLocations:{wfs:"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"},initialize:function(a){OpenLayers.Format.Filter.v1_1_0.prototype.initialize.apply(this,[a]);OpenLayers.Format.WFST.v1.prototype.initialize.apply(this,[a])},readNode:function(a,b,c){return OpenLayers.Format.GML.v3.prototype.readNode.apply(this,arguments)},readers:{wfs:OpenLayers.Util.applyDefaults({FeatureCollection:function(a,
b){b.numberOfFeatures=parseInt(a.getAttribute("numberOfFeatures"));OpenLayers.Format.WFST.v1.prototype.readers.wfs.FeatureCollection.apply(this,arguments)},TransactionResponse:function(a,b){b.insertIds=[];b.success=!1;this.readChildNodes(a,b)},TransactionSummary:function(a,b){b.success=!0},InsertResults:function(a,b){this.readChildNodes(a,b)},Feature:function(a,b){var c={fids:[]};this.readChildNodes(a,c);b.insertIds.push(c.fids[0])}},OpenLayers.Format.WFST.v1.prototype.readers.wfs),gml:OpenLayers.Format.GML.v3.prototype.readers.gml,
feature:OpenLayers.Format.GML.v3.prototype.readers.feature,ogc:OpenLayers.Format.Filter.v1_1_0.prototype.readers.ogc,ows:OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows},writers:{wfs:OpenLayers.Util.applyDefaults({GetFeature:function(a){var b=OpenLayers.Format.WFST.v1.prototype.writers.wfs.GetFeature.apply(this,arguments);a&&this.setAttributes(b,{resultType:a.resultType,startIndex:a.startIndex,count:a.count});return b},Query:function(a){a=OpenLayers.Util.extend({featureNS:this.featureNS,
featurePrefix:this.featurePrefix,featureType:this.featureType,srsName:this.srsName},a);var b=a.featurePrefix,c=this.createElementNSPlus("wfs:Query",{attributes:{typeName:(b?b+":":"")+a.featureType,srsName:a.srsName}});a.featureNS&&c.setAttribute("xmlns:"+b,a.featureNS);if(a.propertyNames)for(var b=0,d=a.propertyNames.length;b<d;b++)this.writeNode("wfs:PropertyName",{property:a.propertyNames[b]},c);a.filter&&(OpenLayers.Format.WFST.v1_1_0.prototype.setFilterProperty.call(this,a.filter),this.writeNode("ogc:Filter",
a.filter,c));return c},PropertyName:function(a){return this.createElementNSPlus("wfs:PropertyName",{value:a.property})}},OpenLayers.Format.WFST.v1.prototype.writers.wfs),gml:OpenLayers.Format.GML.v3.prototype.writers.gml,feature:OpenLayers.Format.GML.v3.prototype.writers.feature,ogc:OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc},CLASS_NAME:"OpenLayers.Format.WFST.v1_1_0"});OpenLayers.Protocol=OpenLayers.Class({format:null,options:null,autoDestroy:!0,defaultFilter:null,initialize:function(a){a=a||{};OpenLayers.Util.extend(this,a);this.options=a},mergeWithDefaultFilter:function(a){return a&&this.defaultFilter?new OpenLayers.Filter.Logical({type:OpenLayers.Filter.Logical.AND,filters:[this.defaultFilter,a]}):a||this.defaultFilter||void 0},destroy:function(){this.format=this.options=null},read:function(a){a=a||{};a.filter=this.mergeWithDefaultFilter(a.filter)},create:function(){},
update:function(){},"delete":function(){},commit:function(){},abort:function(a){},createCallback:function(a,b,c){return OpenLayers.Function.bind(function(){a.apply(this,[b,c])},this)},CLASS_NAME:"OpenLayers.Protocol"});OpenLayers.Protocol.Response=OpenLayers.Class({code:null,requestType:null,last:!0,features:null,data:null,reqFeatures:null,priv:null,error:null,initialize:function(a){OpenLayers.Util.extend(this,a)},success:function(){return 0<this.code},CLASS_NAME:"OpenLayers.Protocol.Response"});
OpenLayers.Protocol.Response.SUCCESS=1;OpenLayers.Protocol.Response.FAILURE=0;OpenLayers.Format.JSON=OpenLayers.Class(OpenLayers.Format,{indent:"    ",space:" ",newline:"\n",level:0,pretty:!1,nativeJSON:function(){return!(!window.JSON||"function"!=typeof JSON.parse||"function"!=typeof JSON.stringify)}(),read:function(a,b){var c;if(this.nativeJSON)c=JSON.parse(a,b);else try{if(/^[\],:{}\s]*$/.test(a.replace(/\\["\\\/bfnrtu]/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))&&(c=eval("("+a+")"),"function"===
typeof b)){var d=function(a,c){if(c&&"object"===typeof c)for(var e in c)c.hasOwnProperty(e)&&(c[e]=d(e,c[e]));return b(a,c)};c=d("",c)}}catch(e){}this.keepData&&(this.data=c);return c},write:function(a,b){this.pretty=!!b;var c=null,d=typeof a;if(this.serialize[d])try{c=!this.pretty&&this.nativeJSON?JSON.stringify(a):this.serialize[d].apply(this,[a])}catch(e){OpenLayers.Console.error("Trouble serializing: "+e)}return c},writeIndent:function(){var a=[];if(this.pretty)for(var b=0;b<this.level;++b)a.push(this.indent);
return a.join("")},writeNewline:function(){return this.pretty?this.newline:""},writeSpace:function(){return this.pretty?this.space:""},serialize:{object:function(a){if(null==a)return"null";if(a.constructor==Date)return this.serialize.date.apply(this,[a]);if(a.constructor==Array)return this.serialize.array.apply(this,[a]);var b=["{"];this.level+=1;var c,d,e,f=!1;for(c in a)a.hasOwnProperty(c)&&(d=OpenLayers.Format.JSON.prototype.write.apply(this,[c,this.pretty]),e=OpenLayers.Format.JSON.prototype.write.apply(this,
[a[c],this.pretty]),null!=d&&null!=e&&(f&&b.push(","),b.push(this.writeNewline(),this.writeIndent(),d,":",this.writeSpace(),e),f=!0));this.level-=1;b.push(this.writeNewline(),this.writeIndent(),"}");return b.join("")},array:function(a){var b,c=["["];this.level+=1;for(var d=0,e=a.length;d<e;++d)b=OpenLayers.Format.JSON.prototype.write.apply(this,[a[d],this.pretty]),null!=b&&(0<d&&c.push(","),c.push(this.writeNewline(),this.writeIndent(),b));this.level-=1;c.push(this.writeNewline(),this.writeIndent(),
"]");return c.join("")},string:function(a){var b={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};return/["\\\x00-\x1f]/.test(a)?'"'+a.replace(/([\x00-\x1f\\"])/g,function(a,d){var e=b[d];if(e)return e;e=d.charCodeAt();return"\\u00"+Math.floor(e/16).toString(16)+(e%16).toString(16)})+'"':'"'+a+'"'},number:function(a){return isFinite(a)?String(a):"null"},"boolean":function(a){return String(a)},date:function(a){function b(a){return 10>a?"0"+a:a}return'"'+a.getFullYear()+
"-"+b(a.getMonth()+1)+"-"+b(a.getDate())+"T"+b(a.getHours())+":"+b(a.getMinutes())+":"+b(a.getSeconds())+'"'}},CLASS_NAME:"OpenLayers.Format.JSON"});OpenLayers.Format.GeoJSON=OpenLayers.Class(OpenLayers.Format.JSON,{ignoreExtraDims:!1,read:function(a,b,c){b=b?b:"FeatureCollection";var d=null,e=null,e="string"==typeof a?OpenLayers.Format.JSON.prototype.read.apply(this,[a,c]):a;if(!e)OpenLayers.Console.error("Bad JSON: "+a);else if("string"!=typeof e.type)OpenLayers.Console.error("Bad GeoJSON - no type: "+a);else if(this.isValidType(e,b))switch(b){case "Geometry":try{d=this.parseGeometry(e)}catch(f){OpenLayers.Console.error(f)}break;case "Feature":try{d=
this.parseFeature(e),d.type="Feature"}catch(g){OpenLayers.Console.error(g)}break;case "FeatureCollection":switch(d=[],e.type){case "Feature":try{d.push(this.parseFeature(e))}catch(h){d=null,OpenLayers.Console.error(h)}break;case "FeatureCollection":a=0;for(b=e.features.length;a<b;++a)try{d.push(this.parseFeature(e.features[a]))}catch(k){d=null,OpenLayers.Console.error(k)}break;default:try{var l=this.parseGeometry(e);d.push(new OpenLayers.Feature.Vector(l))}catch(m){d=null,OpenLayers.Console.error(m)}}}return d},
isValidType:function(a,b){var c=!1;switch(b){case "Geometry":-1==OpenLayers.Util.indexOf("Point MultiPoint LineString MultiLineString Polygon MultiPolygon Box GeometryCollection".split(" "),a.type)?OpenLayers.Console.error("Unsupported geometry type: "+a.type):c=!0;break;case "FeatureCollection":c=!0;break;default:a.type==b?c=!0:OpenLayers.Console.error("Cannot convert types from "+a.type+" to "+b)}return c},parseFeature:function(a){var b,c,d;c=a.properties?a.properties:{};d=a.geometry&&a.geometry.bbox||
a.bbox;try{b=this.parseGeometry(a.geometry)}catch(e){throw e;}b=new OpenLayers.Feature.Vector(b,c);d&&(b.bounds=OpenLayers.Bounds.fromArray(d));a.id&&(b.fid=a.id);return b},parseGeometry:function(a){if(null==a)return null;var b,c=!1;if("GeometryCollection"==a.type){if(!OpenLayers.Util.isArray(a.geometries))throw"GeometryCollection must have geometries array: "+a;b=a.geometries.length;for(var c=Array(b),d=0;d<b;++d)c[d]=this.parseGeometry.apply(this,[a.geometries[d]]);b=new OpenLayers.Geometry.Collection(c);
c=!0}else{if(!OpenLayers.Util.isArray(a.coordinates))throw"Geometry must have coordinates array: "+a;if(!this.parseCoords[a.type.toLowerCase()])throw"Unsupported geometry type: "+a.type;try{b=this.parseCoords[a.type.toLowerCase()].apply(this,[a.coordinates])}catch(e){throw e;}}this.internalProjection&&(this.externalProjection&&!c)&&b.transform(this.externalProjection,this.internalProjection);return b},parseCoords:{point:function(a){if(!1==this.ignoreExtraDims&&2!=a.length)throw"Only 2D points are supported: "+
a;return new OpenLayers.Geometry.Point(a[0],a[1])},multipoint:function(a){for(var b=[],c=null,d=0,e=a.length;d<e;++d){try{c=this.parseCoords.point.apply(this,[a[d]])}catch(f){throw f;}b.push(c)}return new OpenLayers.Geometry.MultiPoint(b)},linestring:function(a){for(var b=[],c=null,d=0,e=a.length;d<e;++d){try{c=this.parseCoords.point.apply(this,[a[d]])}catch(f){throw f;}b.push(c)}return new OpenLayers.Geometry.LineString(b)},multilinestring:function(a){for(var b=[],c=null,d=0,e=a.length;d<e;++d){try{c=
this.parseCoords.linestring.apply(this,[a[d]])}catch(f){throw f;}b.push(c)}return new OpenLayers.Geometry.MultiLineString(b)},polygon:function(a){for(var b=[],c,d,e=0,f=a.length;e<f;++e){try{d=this.parseCoords.linestring.apply(this,[a[e]])}catch(g){throw g;}c=new OpenLayers.Geometry.LinearRing(d.components);b.push(c)}return new OpenLayers.Geometry.Polygon(b)},multipolygon:function(a){for(var b=[],c=null,d=0,e=a.length;d<e;++d){try{c=this.parseCoords.polygon.apply(this,[a[d]])}catch(f){throw f;}b.push(c)}return new OpenLayers.Geometry.MultiPolygon(b)},
box:function(a){if(2!=a.length)throw"GeoJSON box coordinates must have 2 elements";return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(a[0][0],a[0][1]),new OpenLayers.Geometry.Point(a[1][0],a[0][1]),new OpenLayers.Geometry.Point(a[1][0],a[1][1]),new OpenLayers.Geometry.Point(a[0][0],a[1][1]),new OpenLayers.Geometry.Point(a[0][0],a[0][1])])])}},write:function(a,b){var c={type:null};if(OpenLayers.Util.isArray(a)){c.type="FeatureCollection";var d=
a.length;c.features=Array(d);for(var e=0;e<d;++e){var f=a[e];if(!f instanceof OpenLayers.Feature.Vector)throw"FeatureCollection only supports collections of features: "+f;c.features[e]=this.extract.feature.apply(this,[f])}}else 0==a.CLASS_NAME.indexOf("OpenLayers.Geometry")?c=this.extract.geometry.apply(this,[a]):a instanceof OpenLayers.Feature.Vector&&(c=this.extract.feature.apply(this,[a]),a.layer&&a.layer.projection&&(c.crs=this.createCRSObject(a)));return OpenLayers.Format.JSON.prototype.write.apply(this,
[c,b])},createCRSObject:function(a){a=a.layer.projection.toString();var b={};a.match(/epsg:/i)&&(a=parseInt(a.substring(a.indexOf(":")+1)),b=4326==a?{type:"name",properties:{name:"urn:ogc:def:crs:OGC:1.3:CRS84"}}:{type:"name",properties:{name:"EPSG:"+a}});return b},extract:{feature:function(a){var b=this.extract.geometry.apply(this,[a.geometry]),b={type:"Feature",properties:a.attributes,geometry:b};null!=a.fid&&(b.id=a.fid);return b},geometry:function(a){if(null==a)return null;this.internalProjection&&
this.externalProjection&&(a=a.clone(),a.transform(this.internalProjection,this.externalProjection));var b=a.CLASS_NAME.split(".")[2];a=this.extract[b.toLowerCase()].apply(this,[a]);return"Collection"==b?{type:"GeometryCollection",geometries:a}:{type:b,coordinates:a}},point:function(a){return[a.x,a.y]},multipoint:function(a){for(var b=[],c=0,d=a.components.length;c<d;++c)b.push(this.extract.point.apply(this,[a.components[c]]));return b},linestring:function(a){for(var b=[],c=0,d=a.components.length;c<
d;++c)b.push(this.extract.point.apply(this,[a.components[c]]));return b},multilinestring:function(a){for(var b=[],c=0,d=a.components.length;c<d;++c)b.push(this.extract.linestring.apply(this,[a.components[c]]));return b},polygon:function(a){for(var b=[],c=0,d=a.components.length;c<d;++c)b.push(this.extract.linestring.apply(this,[a.components[c]]));return b},multipolygon:function(a){for(var b=[],c=0,d=a.components.length;c<d;++c)b.push(this.extract.polygon.apply(this,[a.components[c]]));return b},collection:function(a){for(var b=
a.components.length,c=Array(b),d=0;d<b;++d)c[d]=this.extract.geometry.apply(this,[a.components[d]]);return c}},CLASS_NAME:"OpenLayers.Format.GeoJSON"});OpenLayers.Protocol.Script=OpenLayers.Class(OpenLayers.Protocol,{url:null,params:null,callback:null,callbackTemplate:"OpenLayers.Protocol.Script.registry.${id}",callbackKey:"callback",callbackPrefix:"",scope:null,format:null,pendingRequests:null,srsInBBOX:!1,initialize:function(a){a=a||{};this.params={};this.pendingRequests={};OpenLayers.Protocol.prototype.initialize.apply(this,arguments);this.format||(this.format=new OpenLayers.Format.GeoJSON);if(!this.filterToParams&&OpenLayers.Format.QueryStringFilter){var b=
new OpenLayers.Format.QueryStringFilter({srsInBBOX:this.srsInBBOX});this.filterToParams=function(a,d){return b.write(a,d)}}},read:function(a){OpenLayers.Protocol.prototype.read.apply(this,arguments);a=OpenLayers.Util.applyDefaults(a,this.options);a.params=OpenLayers.Util.applyDefaults(a.params,this.options.params);a.filter&&this.filterToParams&&(a.params=this.filterToParams(a.filter,a.params));var b=new OpenLayers.Protocol.Response({requestType:"read"}),c=this.createRequest(a.url,a.params,OpenLayers.Function.bind(function(c){b.data=
c;this.handleRead(b,a)},this));b.priv=c;return b},createRequest:function(a,b,c){c=OpenLayers.Protocol.Script.register(c);var d=OpenLayers.String.format(this.callbackTemplate,{id:c});b=OpenLayers.Util.extend({},b);b[this.callbackKey]=this.callbackPrefix+d;a=OpenLayers.Util.urlAppend(a,OpenLayers.Util.getParameterString(b));b=document.createElement("script");b.type="text/javascript";b.src=a;b.id="OpenLayers_Protocol_Script_"+c;this.pendingRequests[b.id]=b;document.getElementsByTagName("head")[0].appendChild(b);
return b},destroyRequest:function(a){OpenLayers.Protocol.Script.unregister(a.id.split("_").pop());delete this.pendingRequests[a.id];a.parentNode&&a.parentNode.removeChild(a)},handleRead:function(a,b){this.handleResponse(a,b)},handleResponse:function(a,b){b.callback&&(a.data?(a.features=this.parseFeatures(a.data),a.code=OpenLayers.Protocol.Response.SUCCESS):a.code=OpenLayers.Protocol.Response.FAILURE,this.destroyRequest(a.priv),b.callback.call(b.scope,a))},parseFeatures:function(a){return this.format.read(a)},
abort:function(a){if(a)this.destroyRequest(a.priv);else for(var b in this.pendingRequests)this.destroyRequest(this.pendingRequests[b])},destroy:function(){this.abort();delete this.params;delete this.format;OpenLayers.Protocol.prototype.destroy.apply(this)},CLASS_NAME:"OpenLayers.Protocol.Script"});(function(){var a=OpenLayers.Protocol.Script,b=0;a.registry={};a.register=function(c){var d="c"+ ++b;a.registry[d]=function(){c.apply(this,arguments)};return d};a.unregister=function(b){delete a.registry[b]}})();OpenLayers.Format.EncodedPolyline=OpenLayers.Class(OpenLayers.Format,{geometryType:"linestring",initialize:function(a){OpenLayers.Format.prototype.initialize.apply(this,[a])},read:function(a){var b;if("linestring"==this.geometryType)b=OpenLayers.Geometry.LineString;else if("linearring"==this.geometryType)b=OpenLayers.Geometry.LinearRing;else if("multipoint"==this.geometryType)b=OpenLayers.Geometry.MultiPoint;else if("point"!=this.geometryType&&"polygon"!=this.geometryType)return null;a=this.decodeDeltas(a,
2);for(var c=a.length,d=[],e=0;e+1<c;){var f=a[e++],g=a[e++];d.push(new OpenLayers.Geometry.Point(g,f))}return"point"==this.geometryType?new OpenLayers.Feature.Vector(d[0]):"polygon"==this.geometryType?new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(d)])):new OpenLayers.Feature.Vector(new b(d))},decode:function(a,b,c){a=this.decodeDeltas(a,b,c||1E5);c=a.length;for(var d=[],e=0;e+(b-1)<c;){for(var f=[],g=0;g<b;++g)f.push(a[e++]);d.push(f)}return d},
write:function(a){a=(a.constructor==Array?a[0]:a).geometry;var b=a.CLASS_NAME.split(".")[2].toLowerCase();if("point"==b)a=Array(a);else if("linestring"==b||"linearring"==b||"multipoint"==b)a=a.components;else if("polygon"==b)a=a.components[0].components;else return null;for(var b=[],c=a.length,d=0;d<c;++d){var e=a[d];b.push(e.y);b.push(e.x)}return this.encodeDeltas(b,2)},encode:function(a,b,c){c=c||1E5;for(var d=[],e=a.length,f=0;f<e;++f)for(var g=a[f],h=0;h<b;++h)d.push(g[h]);return this.encodeDeltas(d,
b,c)},encodeDeltas:function(a,b,c){var d,e=Array(b);for(d=0;d<b;++d)e[d]=0;for(var f=a.length,g=0;g<f;)for(d=0;d<b;++d,++g){var h=a[g],k=h-e[d];e[d]=h;a[g]=k}return this.encodeFloats(a,c||1E5)},decodeDeltas:function(a,b,c){var d,e=Array(b);for(d=0;d<b;++d)e[d]=0;a=this.decodeFloats(a,c||1E5);c=a.length;for(var f=0;f<c;)for(d=0;d<b;++d,++f)e[d]+=a[f],a[f]=e[d];return a},encodeFloats:function(a,b){for(var c=b||1E5,d=a.length,e=0;e<d;++e)a[e]=Math.round(a[e]*c);return this.encodeSignedIntegers(a)},decodeFloats:function(a,
b){for(var c=b||1E5,d=this.decodeSignedIntegers(a),e=d.length,f=0;f<e;++f)d[f]/=c;return d},encodeSignedIntegers:function(a){for(var b=a.length,c=0;c<b;++c){var d=a[c],e=d<<1;0>d&&(e=~e);a[c]=e}return this.encodeUnsignedIntegers(a)},decodeSignedIntegers:function(a){a=this.decodeUnsignedIntegers(a);for(var b=a.length,c=0;c<b;++c){var d=a[c];a[c]=d&1?~(d>>1):d>>1}return a},encodeUnsignedIntegers:function(a){for(var b="",c=a.length,d=0;d<c;++d)b+=this.encodeUnsignedInteger(a[d]);return b},decodeUnsignedIntegers:function(a){for(var b=
[],c=0,d=0,e=a.length,f=0;f<e;++f){var g=a.charCodeAt(f)-63,c=c|(g&31)<<d;32>g?(b.push(c),d=c=0):d+=5}return b},encodeFloat:function(a,b){a=Math.round(a*(b||1E5));return this.encodeSignedInteger(a)},decodeFloat:function(a,b){return this.decodeSignedInteger(a)/(b||1E5)},encodeSignedInteger:function(a){var b=a<<1;0>a&&(b=~b);return this.encodeUnsignedInteger(b)},decodeSignedInteger:function(a){a=this.decodeUnsignedInteger(a);return a&1?~(a>>1):a>>1},encodeUnsignedInteger:function(a){for(var b,c="";32<=
a;)b=(32|a&31)+63,c+=String.fromCharCode(b),a>>=5;return c+=String.fromCharCode(a+63)},decodeUnsignedInteger:function(a){for(var b=0,c=0,d=a.length,e=0;e<d;++e){var f=a.charCodeAt(e)-63,b=b|(f&31)<<c;if(32>f)break;c+=5}return b},CLASS_NAME:"OpenLayers.Format.EncodedPolyline"});OpenLayers.Control.Panel=OpenLayers.Class(OpenLayers.Control,{controls:null,autoActivate:!0,defaultControl:null,saveState:!1,allowDepress:!1,activeState:null,initialize:function(a){OpenLayers.Control.prototype.initialize.apply(this,[a]);this.controls=[];this.activeState={}},destroy:function(){this.map&&this.map.events.unregister("buttonclick",this,this.onButtonClick);OpenLayers.Control.prototype.destroy.apply(this,arguments);for(var a,b=this.controls.length-1;0<=b;b--)a=this.controls[b],a.events&&
a.events.un({activate:this.iconOn,deactivate:this.iconOff}),a.panel_div=null;this.activeState=null},activate:function(){if(OpenLayers.Control.prototype.activate.apply(this,arguments)){for(var a,b=0,c=this.controls.length;b<c;b++)a=this.controls[b],(a===this.defaultControl||this.saveState&&this.activeState[a.id])&&a.activate();!0===this.saveState&&(this.defaultControl=null);this.redraw();return!0}return!1},deactivate:function(){if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)){for(var a,
b=0,c=this.controls.length;b<c;b++)a=this.controls[b],this.activeState[a.id]=a.deactivate();this.redraw();return!0}return!1},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);this.outsideViewport?(this.events.attachToElement(this.div),this.events.register("buttonclick",this,this.onButtonClick)):this.map.events.register("buttonclick",this,this.onButtonClick);this.addControlsToMap(this.controls);return this.div},redraw:function(){for(var a=this.div.childNodes.length-1;0<=a;a--)this.div.removeChild(this.div.childNodes[a]);
this.div.innerHTML="";if(this.active)for(var a=0,b=this.controls.length;a<b;a++)this.div.appendChild(this.controls[a].panel_div)},activateControl:function(a){if(!this.active)return!1;if(a.type==OpenLayers.Control.TYPE_BUTTON)a.trigger();else if(a.type==OpenLayers.Control.TYPE_TOGGLE)a.active?a.deactivate():a.activate();else if(this.allowDepress&&a.active)a.deactivate();else{for(var b,c=0,d=this.controls.length;c<d;c++)b=this.controls[c],b==a||b.type!==OpenLayers.Control.TYPE_TOOL&&null!=b.type||b.deactivate();
a.activate()}},addControls:function(a){OpenLayers.Util.isArray(a)||(a=[a]);this.controls=this.controls.concat(a);for(var b=0,c=a.length;b<c;b++){var d=a[b],e=this.createControlMarkup(d);OpenLayers.Element.addClass(e,d.displayClass+"ItemInactive");OpenLayers.Element.addClass(e,"olButton");""==d.title||e.title||(e.title=d.title);d.panel_div=e}this.map&&(this.addControlsToMap(a),this.redraw())},createControlMarkup:function(a){return document.createElement("div")},addControlsToMap:function(a){for(var b,
c=0,d=a.length;c<d;c++)b=a[c],!0===b.autoActivate?(b.autoActivate=!1,this.map.addControl(b),b.autoActivate=!0):(this.map.addControl(b),b.deactivate()),b.events.on({activate:this.iconOn,deactivate:this.iconOff})},iconOn:function(){var a=this.panel_div;a.className=a.className.replace(RegExp("\\b("+this.displayClass+"Item)Inactive\\b"),"$1Active")},iconOff:function(){var a=this.panel_div;a.className=a.className.replace(RegExp("\\b("+this.displayClass+"Item)Active\\b"),"$1Inactive")},onButtonClick:function(a){var b=
this.controls;a=a.buttonElement;for(var c=b.length-1;0<=c;--c)if(b[c].panel_div===a){this.activateControl(b[c]);break}},getControlsBy:function(a,b){var c="function"==typeof b.test;return OpenLayers.Array.filter(this.controls,function(d){return d[a]==b||c&&b.test(d[a])})},getControlsByName:function(a){return this.getControlsBy("name",a)},getControlsByClass:function(a){return this.getControlsBy("CLASS_NAME",a)},CLASS_NAME:"OpenLayers.Control.Panel"});OpenLayers.Control.Button=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_BUTTON,trigger:function(){},CLASS_NAME:"OpenLayers.Control.Button"});OpenLayers.Control.ZoomIn=OpenLayers.Class(OpenLayers.Control.Button,{trigger:function(){this.map&&this.map.zoomIn()},CLASS_NAME:"OpenLayers.Control.ZoomIn"});OpenLayers.Control.ZoomOut=OpenLayers.Class(OpenLayers.Control.Button,{trigger:function(){this.map&&this.map.zoomOut()},CLASS_NAME:"OpenLayers.Control.ZoomOut"});OpenLayers.Control.ZoomToMaxExtent=OpenLayers.Class(OpenLayers.Control.Button,{trigger:function(){this.map&&this.map.zoomToMaxExtent()},CLASS_NAME:"OpenLayers.Control.ZoomToMaxExtent"});OpenLayers.Control.ZoomPanel=OpenLayers.Class(OpenLayers.Control.Panel,{initialize:function(a){OpenLayers.Control.Panel.prototype.initialize.apply(this,[a]);this.addControls([new OpenLayers.Control.ZoomIn,new OpenLayers.Control.ZoomToMaxExtent,new OpenLayers.Control.ZoomOut])},CLASS_NAME:"OpenLayers.Control.ZoomPanel"});OpenLayers.Layer.HTTPRequest=OpenLayers.Class(OpenLayers.Layer,{URL_HASH_FACTOR:(Math.sqrt(5)-1)/2,url:null,params:null,reproject:!1,initialize:function(a,b,c,d){OpenLayers.Layer.prototype.initialize.apply(this,[a,d]);this.url=b;this.params||(this.params=OpenLayers.Util.extend({},c))},destroy:function(){this.params=this.url=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments)},clone:function(a){null==a&&(a=new OpenLayers.Layer.HTTPRequest(this.name,this.url,this.params,this.getOptions()));
return a=OpenLayers.Layer.prototype.clone.apply(this,[a])},setUrl:function(a){this.url=a},mergeNewParams:function(a){this.params=OpenLayers.Util.extend(this.params,a);a=this.redraw();null!=this.map&&this.map.events.triggerEvent("changelayer",{layer:this,property:"params"});return a},redraw:function(a){return a?this.mergeNewParams({_olSalt:Math.random()}):OpenLayers.Layer.prototype.redraw.apply(this,[])},selectUrl:function(a,b){for(var c=1,d=0,e=a.length;d<e;d++)c*=a.charCodeAt(d)*this.URL_HASH_FACTOR,
c-=Math.floor(c);return b[Math.floor(c*b.length)]},getFullRequestString:function(a,b){var c=b||this.url,d=OpenLayers.Util.extend({},this.params),d=OpenLayers.Util.extend(d,a),e=OpenLayers.Util.getParameterString(d);OpenLayers.Util.isArray(c)&&(c=this.selectUrl(e,c));var e=OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(c)),f;for(f in d)f.toUpperCase()in e&&delete d[f];e=OpenLayers.Util.getParameterString(d);return OpenLayers.Util.urlAppend(c,e)},CLASS_NAME:"OpenLayers.Layer.HTTPRequest"});OpenLayers.Tile=OpenLayers.Class({events:null,eventListeners:null,id:null,layer:null,url:null,bounds:null,size:null,position:null,isLoading:!1,initialize:function(a,b,c,d,e,f){this.layer=a;this.position=b.clone();this.setBounds(c);this.url=d;e&&(this.size=e.clone());this.id=OpenLayers.Util.createUniqueID("Tile_");OpenLayers.Util.extend(this,f);this.events=new OpenLayers.Events(this);if(this.eventListeners instanceof Object)this.events.on(this.eventListeners)},unload:function(){this.isLoading&&(this.isLoading=
!1,this.events.triggerEvent("unload"))},destroy:function(){this.position=this.size=this.bounds=this.layer=null;this.eventListeners&&this.events.un(this.eventListeners);this.events.destroy();this.events=this.eventListeners=null},draw:function(a){a||this.clear();var b=this.shouldDraw();b&&(!a&&!1===this.events.triggerEvent("beforedraw"))&&(b=null);return b},shouldDraw:function(){var a=!1,b=this.layer.maxExtent;if(b){var c=this.layer.map,c=c.baseLayer.wrapDateLine&&c.getMaxExtent();this.bounds.intersectsBounds(b,
{inclusive:!1,worldBounds:c})&&(a=!0)}return a||this.layer.displayOutsideMaxExtent},setBounds:function(a){a=a.clone();if(this.layer.map.baseLayer.wrapDateLine){var b=this.layer.map.getMaxExtent(),c=this.layer.map.getResolution();a=a.wrapDateLine(b,{leftTolerance:c,rightTolerance:c})}this.bounds=a},moveTo:function(a,b,c){null==c&&(c=!0);this.setBounds(a);this.position=b.clone();c&&this.draw()},clear:function(a){},CLASS_NAME:"OpenLayers.Tile"});OpenLayers.Tile.Image=OpenLayers.Class(OpenLayers.Tile,{url:null,imgDiv:null,frame:null,imageReloadAttempts:null,layerAlphaHack:null,asyncRequestId:null,maxGetUrlLength:null,canvasContext:null,crossOriginKeyword:null,initialize:function(a,b,c,d,e,f){OpenLayers.Tile.prototype.initialize.apply(this,arguments);this.url=d;this.layerAlphaHack=this.layer.alpha&&OpenLayers.Util.alphaHack();if(null!=this.maxGetUrlLength||this.layer.gutter||this.layerAlphaHack)this.frame=document.createElement("div"),this.frame.style.position=
"absolute",this.frame.style.overflow="hidden";null!=this.maxGetUrlLength&&OpenLayers.Util.extend(this,OpenLayers.Tile.Image.IFrame)},destroy:function(){this.imgDiv&&(this.clear(),this.frame=this.imgDiv=null);this.asyncRequestId=null;OpenLayers.Tile.prototype.destroy.apply(this,arguments)},draw:function(){var a=OpenLayers.Tile.prototype.draw.apply(this,arguments);a?(this.layer!=this.layer.map.baseLayer&&this.layer.reproject&&(this.bounds=this.getBoundsFromBaseLayer(this.position)),this.isLoading?this._loadEvent=
"reload":(this.isLoading=!0,this._loadEvent="loadstart"),this.renderTile(),this.positionTile()):!1===a&&this.unload();return a},renderTile:function(){if(this.layer.async){var a=this.asyncRequestId=(this.asyncRequestId||0)+1;this.layer.getURLasync(this.bounds,function(b){a==this.asyncRequestId&&(this.url=b,this.initImage())},this)}else this.url=this.layer.getURL(this.bounds),this.initImage()},positionTile:function(){var a=this.getTile().style,b=this.frame?this.size:this.layer.getImageSize(this.bounds),
c=1;this.layer instanceof OpenLayers.Layer.Grid&&(c=this.layer.getServerResolution()/this.layer.map.getResolution());a.left=this.position.x+"px";a.top=this.position.y+"px";a.width=Math.round(c*b.w)+"px";a.height=Math.round(c*b.h)+"px"},clear:function(){OpenLayers.Tile.prototype.clear.apply(this,arguments);var a=this.imgDiv;if(a){var b=this.getTile();b.parentNode===this.layer.div&&this.layer.div.removeChild(b);this.setImgSrc();!0===this.layerAlphaHack&&(a.style.filter="");OpenLayers.Element.removeClass(a,
"olImageLoadError")}this.canvasContext=null},getImage:function(){if(!this.imgDiv){this.imgDiv=OpenLayers.Tile.Image.IMAGE.cloneNode(!1);var a=this.imgDiv.style;if(this.frame){var b=0,c=0;this.layer.gutter&&(b=100*(this.layer.gutter/this.layer.tileSize.w),c=100*(this.layer.gutter/this.layer.tileSize.h));a.left=-b+"%";a.top=-c+"%";a.width=2*b+100+"%";a.height=2*c+100+"%"}a.visibility="hidden";a.opacity=0;1>this.layer.opacity&&(a.filter="alpha(opacity="+100*this.layer.opacity+")");a.position="absolute";
this.layerAlphaHack&&(a.paddingTop=a.height,a.height="0",a.width="100%");this.frame&&this.frame.appendChild(this.imgDiv)}return this.imgDiv},setImage:function(a){this.imgDiv=a},initImage:function(){this.events.triggerEvent("beforeload");this.layer.div.appendChild(this.getTile());this.events.triggerEvent(this._loadEvent);var a=this.getImage();this.url&&OpenLayers.Util.isEquivalentUrl(a.src,this.url)?this._loadTimeout=window.setTimeout(OpenLayers.Function.bind(this.onImageLoad,this),0):(this.stopLoading(),
this.crossOriginKeyword&&a.removeAttribute("crossorigin"),OpenLayers.Event.observe(a,"load",OpenLayers.Function.bind(this.onImageLoad,this)),OpenLayers.Event.observe(a,"error",OpenLayers.Function.bind(this.onImageError,this)),this.imageReloadAttempts=0,this.setImgSrc(this.url))},setImgSrc:function(a){var b=this.imgDiv;a?(b.style.visibility="hidden",b.style.opacity=0,this.crossOriginKeyword&&("data:"!==a.substr(0,5)?b.setAttribute("crossorigin",this.crossOriginKeyword):b.removeAttribute("crossorigin")),
b.src=a):(this.stopLoading(),this.imgDiv=null,b.parentNode&&b.parentNode.removeChild(b))},getTile:function(){return this.frame?this.frame:this.getImage()},createBackBuffer:function(){if(this.imgDiv&&!this.isLoading){var a;this.frame?(a=this.frame.cloneNode(!1),a.appendChild(this.imgDiv)):a=this.imgDiv;this.imgDiv=null;return a}},onImageLoad:function(){var a=this.imgDiv;this.stopLoading();a.style.visibility="inherit";a.style.opacity=this.layer.opacity;this.isLoading=!1;this.canvasContext=null;this.events.triggerEvent("loadend");
!0===this.layerAlphaHack&&(a.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+a.src+"', sizingMethod='scale')")},onImageError:function(){var a=this.imgDiv;null!=a.src&&(this.imageReloadAttempts++,this.imageReloadAttempts<=OpenLayers.IMAGE_RELOAD_ATTEMPTS?this.setImgSrc(this.layer.getURL(this.bounds)):(OpenLayers.Element.addClass(a,"olImageLoadError"),this.events.triggerEvent("loaderror"),this.onImageLoad()))},stopLoading:function(){OpenLayers.Event.stopObservingElement(this.imgDiv);
window.clearTimeout(this._loadTimeout);delete this._loadTimeout},getCanvasContext:function(){if(OpenLayers.CANVAS_SUPPORTED&&this.imgDiv&&!this.isLoading){if(!this.canvasContext){var a=document.createElement("canvas");a.width=this.size.w;a.height=this.size.h;this.canvasContext=a.getContext("2d");this.canvasContext.drawImage(this.imgDiv,0,0)}return this.canvasContext}},CLASS_NAME:"OpenLayers.Tile.Image"});
OpenLayers.Tile.Image.IMAGE=function(){var a=new Image;a.className="olTileImage";a.galleryImg="no";return a}();OpenLayers.Layer.Grid=OpenLayers.Class(OpenLayers.Layer.HTTPRequest,{tileSize:null,tileOriginCorner:"bl",tileOrigin:null,tileOptions:null,tileClass:OpenLayers.Tile.Image,grid:null,singleTile:!1,ratio:1.5,buffer:0,transitionEffect:"resize",numLoadingTiles:0,serverResolutions:null,loading:!1,backBuffer:null,gridResolution:null,backBufferResolution:null,backBufferLonLat:null,backBufferTimerId:null,removeBackBufferDelay:null,className:null,gridLayout:null,rowSign:null,transitionendEvents:["transitionend",
"webkitTransitionEnd","otransitionend","oTransitionEnd"],initialize:function(a,b,c,d){OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,arguments);this.grid=[];this._removeBackBuffer=OpenLayers.Function.bind(this.removeBackBuffer,this);this.initProperties();this.rowSign="t"===this.tileOriginCorner.substr(0,1)?1:-1},initProperties:function(){void 0===this.options.removeBackBufferDelay&&(this.removeBackBufferDelay=this.singleTile?0:2500);void 0===this.options.className&&(this.className=this.singleTile?
"olLayerGridSingleTile":"olLayerGrid")},setMap:function(a){OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this,a);OpenLayers.Element.addClass(this.div,this.className)},removeMap:function(a){this.removeBackBuffer()},destroy:function(){this.removeBackBuffer();this.clearGrid();this.tileSize=this.grid=null;OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this,arguments)},clearGrid:function(){if(this.grid){for(var a=0,b=this.grid.length;a<b;a++)for(var c=this.grid[a],d=0,e=c.length;d<e;d++)this.destroyTile(c[d]);
this.grid=[];this.gridLayout=this.gridResolution=null}},addOptions:function(a,b){var c=void 0!==a.singleTile&&a.singleTile!==this.singleTile;OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this,arguments);this.map&&c&&(this.initProperties(),this.clearGrid(),this.tileSize=this.options.tileSize,this.setTileSize(),this.moveTo(null,!0))},clone:function(a){null==a&&(a=new OpenLayers.Layer.Grid(this.name,this.url,this.params,this.getOptions()));a=OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this,
[a]);null!=this.tileSize&&(a.tileSize=this.tileSize.clone());a.grid=[];a.gridResolution=null;a.backBuffer=null;a.backBufferTimerId=null;a.loading=!1;a.numLoadingTiles=0;return a},moveTo:function(a,b,c){OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this,arguments);a=a||this.map.getExtent();if(null!=a){var d=!this.grid.length||b,e=this.getTilesBounds(),f=this.map.getResolution();this.getServerResolution(f);if(this.singleTile){if(d||!c&&!e.containsBounds(a))b&&"resize"!==this.transitionEffect&&
this.removeBackBuffer(),b&&"resize"!==this.transitionEffect||this.applyBackBuffer(f),this.initSingleTile(a)}else(d=d||!e.intersectsBounds(a,{worldBounds:this.map.baseLayer.wrapDateLine&&this.map.getMaxExtent()}))?(!b||"resize"!==this.transitionEffect&&this.gridResolution!==f||this.applyBackBuffer(f),this.initGriddedTiles(a)):this.moveGriddedTiles()}},getTileData:function(a){var b=null,c=a.lon,d=a.lat,e=this.grid.length;if(this.map&&e){var f=this.map.getResolution();a=this.tileSize.w;var g=this.tileSize.h,
h=this.grid[0][0].bounds,k=h.left,h=h.top;if(c<k&&this.map.baseLayer.wrapDateLine)var l=this.map.getMaxExtent().getWidth(),m=Math.ceil((k-c)/l),c=c+l*m;c=(c-k)/(f*a);d=(h-d)/(f*g);f=Math.floor(c);k=Math.floor(d);0<=k&&k<e&&(e=this.grid[k][f])&&(b={tile:e,i:Math.floor((c-f)*a),j:Math.floor((d-k)*g)})}return b},destroyTile:function(a){this.removeTileMonitoringHooks(a);a.destroy()},getServerResolution:function(a){var b=Number.POSITIVE_INFINITY;a=a||this.map.getResolution();if(this.serverResolutions&&
-1===OpenLayers.Util.indexOf(this.serverResolutions,a)){var c,d,e,f;for(c=this.serverResolutions.length-1;0<=c;c--){e=this.serverResolutions[c];d=Math.abs(e-a);if(d>b)break;b=d;f=e}a=f}return a},getServerZoom:function(){var a=this.getServerResolution();return this.serverResolutions?OpenLayers.Util.indexOf(this.serverResolutions,a):this.map.getZoomForResolution(a)+(this.zoomOffset||0)},applyBackBuffer:function(a){null!==this.backBufferTimerId&&this.removeBackBuffer();var b=this.backBuffer;if(!b){b=
this.createBackBuffer();if(!b)return;a===this.gridResolution?this.div.insertBefore(b,this.div.firstChild):this.map.baseLayer.div.parentNode.insertBefore(b,this.map.baseLayer.div);this.backBuffer=b;var c=this.grid[0][0].bounds;this.backBufferLonLat={lon:c.left,lat:c.top};this.backBufferResolution=this.gridResolution}for(var c=this.backBufferResolution/a,d=b.childNodes,e,f=d.length-1;0<=f;--f)e=d[f],e.style.top=(c*e._i*e._h|0)+"px",e.style.left=(c*e._j*e._w|0)+"px",e.style.width=Math.round(c*e._w)+
"px",e.style.height=Math.round(c*e._h)+"px";a=this.getViewPortPxFromLonLat(this.backBufferLonLat,a);c=this.map.layerContainerOriginPx.y;b.style.left=Math.round(a.x-this.map.layerContainerOriginPx.x)+"px";b.style.top=Math.round(a.y-c)+"px"},createBackBuffer:function(){var a;if(0<this.grid.length){a=document.createElement("div");a.id=this.div.id+"_bb";a.className="olBackBuffer";a.style.position="absolute";var b=this.map;a.style.zIndex="resize"===this.transitionEffect?this.getZIndex()-1:b.Z_INDEX_BASE.BaseLayer-
(b.getNumLayers()-b.getLayerIndex(this));for(var b=0,c=this.grid.length;b<c;b++)for(var d=0,e=this.grid[b].length;d<e;d++){var f=this.grid[b][d],g=this.grid[b][d].createBackBuffer();g&&(g._i=b,g._j=d,g._w=f.size.w,g._h=f.size.h,g.id=f.id+"_bb",a.appendChild(g))}}return a},removeBackBuffer:function(){if(this._transitionElement){for(var a=this.transitionendEvents.length-1;0<=a;--a)OpenLayers.Event.stopObserving(this._transitionElement,this.transitionendEvents[a],this._removeBackBuffer);delete this._transitionElement}this.backBuffer&&
(this.backBuffer.parentNode&&this.backBuffer.parentNode.removeChild(this.backBuffer),this.backBufferResolution=this.backBuffer=null,null!==this.backBufferTimerId&&(window.clearTimeout(this.backBufferTimerId),this.backBufferTimerId=null))},moveByPx:function(a,b){this.singleTile||this.moveGriddedTiles()},setTileSize:function(a){this.singleTile&&(a=this.map.getSize(),a.h=parseInt(a.h*this.ratio,10),a.w=parseInt(a.w*this.ratio,10));OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this,[a])},getTilesBounds:function(){var a=
null,b=this.grid.length;if(b)var a=this.grid[b-1][0].bounds,b=this.grid[0].length*a.getWidth(),c=this.grid.length*a.getHeight(),a=new OpenLayers.Bounds(a.left,a.bottom,a.left+b,a.bottom+c);return a},initSingleTile:function(a){this.events.triggerEvent("retile");var b=a.getCenterLonLat(),c=a.getWidth()*this.ratio;a=a.getHeight()*this.ratio;b=new OpenLayers.Bounds(b.lon-c/2,b.lat-a/2,b.lon+c/2,b.lat+a/2);c=this.map.getLayerPxFromLonLat({lon:b.left,lat:b.top});this.grid.length||(this.grid[0]=[]);(a=this.grid[0][0])?
a.moveTo(b,c):(a=this.addTile(b,c),this.addTileMonitoringHooks(a),a.draw(),this.grid[0][0]=a);this.removeExcessTiles(1,1);this.gridResolution=this.getServerResolution()},calculateGridLayout:function(a,b,c){var d=c*this.tileSize.w;c*=this.tileSize.h;var e=Math.floor((a.left-b.lon)/d)-this.buffer,f=this.rowSign;a=Math[~f?"floor":"ceil"](f*(b.lat-a.top+c)/c)-this.buffer*f;return{tilelon:d,tilelat:c,startcol:e,startrow:a}},getTileOrigin:function(){var a=this.tileOrigin;if(!a)var a=this.getMaxExtent(),
b={tl:["left","top"],tr:["right","top"],bl:["left","bottom"],br:["right","bottom"]}[this.tileOriginCorner],a=new OpenLayers.LonLat(a[b[0]],a[b[1]]);return a},getTileBoundsForGridIndex:function(a,b){var c=this.getTileOrigin(),d=this.gridLayout,e=d.tilelon,f=d.tilelat,g=d.startcol,d=d.startrow,h=this.rowSign;return new OpenLayers.Bounds(c.lon+(g+b)*e,c.lat-(d+a*h)*f*h,c.lon+(g+b+1)*e,c.lat-(d+(a-1)*h)*f*h)},initGriddedTiles:function(a){this.events.triggerEvent("retile");var b=this.map.getSize(),c=this.getTileOrigin(),
d=this.map.getResolution(),e=this.getServerResolution(),f=d/e,d=this.tileSize.w/f,f=this.tileSize.h/f,g=Math.ceil(b.h/f)+2*this.buffer+1,b=Math.ceil(b.w/d)+2*this.buffer+1;this.gridLayout=e=this.calculateGridLayout(a,c,e);var c=e.tilelon,h=e.tilelat,e=this.map.layerContainerOriginPx.x,k=this.map.layerContainerOriginPx.y,l=this.getTileBoundsForGridIndex(0,0),m=this.map.getViewPortPxFromLonLat(new OpenLayers.LonLat(l.left,l.top));m.x=Math.round(m.x)-e;m.y=Math.round(m.y)-k;var e=[],k=this.map.getCenter(),
n=0;do{var p=this.grid[n];p||(p=[],this.grid.push(p));var q=0;do{var l=this.getTileBoundsForGridIndex(n,q),r=m.clone();r.x+=q*Math.round(d);r.y+=n*Math.round(f);var s=p[q];s?s.moveTo(l,r,!1):(s=this.addTile(l,r),this.addTileMonitoringHooks(s),p.push(s));r=l.getCenterLonLat();e.push({tile:s,distance:Math.pow(r.lon-k.lon,2)+Math.pow(r.lat-k.lat,2)});q+=1}while(l.right<=a.right+c*this.buffer||q<b);n+=1}while(l.bottom>=a.bottom-h*this.buffer||n<g);this.removeExcessTiles(n,q);this.gridResolution=d=this.getServerResolution();
e.sort(function(a,b){return a.distance-b.distance});a=0;for(d=e.length;a<d;++a)e[a].tile.draw()},getMaxExtent:function(){return this.maxExtent},addTile:function(a,b){var c=new this.tileClass(this,b,a,null,this.tileSize,this.tileOptions);this.events.triggerEvent("addtile",{tile:c});return c},addTileMonitoringHooks:function(a){a.onLoadStart=function(){!1===this.loading&&(this.loading=!0,this.events.triggerEvent("loadstart"));this.events.triggerEvent("tileloadstart",{tile:a});this.numLoadingTiles++;
!this.singleTile&&(this.backBuffer&&this.gridResolution===this.backBufferResolution)&&OpenLayers.Element.addClass(a.getTile(),"olTileReplacing")};a.onLoadEnd=function(b){this.numLoadingTiles--;b="unload"===b.type;this.events.triggerEvent("tileloaded",{tile:a,aborted:b});if(!this.singleTile&&!b&&this.backBuffer&&this.gridResolution===this.backBufferResolution){var c=a.getTile();if("none"===OpenLayers.Element.getStyle(c,"display")){var d=document.getElementById(a.id+"_bb");d&&d.parentNode.removeChild(d)}OpenLayers.Element.removeClass(c,
"olTileReplacing")}if(0===this.numLoadingTiles){if(this.backBuffer)if(0===this.backBuffer.childNodes.length)this.removeBackBuffer();else{this._transitionElement=b?this.div.lastChild:a.imgDiv;b=this.transitionendEvents;for(c=b.length-1;0<=c;--c)OpenLayers.Event.observe(this._transitionElement,b[c],this._removeBackBuffer);this.backBufferTimerId=window.setTimeout(this._removeBackBuffer,this.removeBackBufferDelay)}this.loading=!1;this.events.triggerEvent("loadend")}};a.onLoadError=function(){this.events.triggerEvent("tileerror",
{tile:a})};a.events.on({loadstart:a.onLoadStart,loadend:a.onLoadEnd,unload:a.onLoadEnd,loaderror:a.onLoadError,scope:this})},removeTileMonitoringHooks:function(a){a.unload();a.events.un({loadstart:a.onLoadStart,loadend:a.onLoadEnd,unload:a.onLoadEnd,loaderror:a.onLoadError,scope:this})},moveGriddedTiles:function(){for(var a=this.buffer+1;;){var b=this.grid[0][0],c=b.position.x+this.map.layerContainerOriginPx.x,b=b.position.y+this.map.layerContainerOriginPx.y,d=this.getServerResolution()/this.map.getResolution(),
d={w:Math.round(this.tileSize.w*d),h:Math.round(this.tileSize.h*d)};if(c>-d.w*(a-1))this.shiftColumn(!0,d);else if(c<-d.w*a)this.shiftColumn(!1,d);else if(b>-d.h*(a-1))this.shiftRow(!0,d);else if(b<-d.h*a)this.shiftRow(!1,d);else break}},shiftRow:function(a,b){var c=this.grid,d=a?0:c.length-1,e=a?-1:1;this.gridLayout.startrow+=e*this.rowSign;for(var f=c[d],g=c[a?"pop":"shift"](),h=0,k=g.length;h<k;h++){var l=g[h],m=f[h].position.clone();m.y+=b.h*e;l.moveTo(this.getTileBoundsForGridIndex(d,h),m)}c[a?
"unshift":"push"](g)},shiftColumn:function(a,b){var c=this.grid,d=a?0:c[0].length-1,e=a?-1:1;this.gridLayout.startcol+=e;for(var f=0,g=c.length;f<g;f++){var h=c[f],k=h[d].position.clone(),l=h[a?"pop":"shift"]();k.x+=b.w*e;l.moveTo(this.getTileBoundsForGridIndex(f,d),k);h[a?"unshift":"push"](l)}},removeExcessTiles:function(a,b){for(var c,d;this.grid.length>a;){var e=this.grid.pop();c=0;for(d=e.length;c<d;c++){var f=e[c];this.destroyTile(f)}}c=0;for(d=this.grid.length;c<d;c++)for(;this.grid[c].length>
b;)e=this.grid[c],f=e.pop(),this.destroyTile(f)},onMapResize:function(){this.singleTile&&(this.clearGrid(),this.setTileSize())},getTileBounds:function(a){var b=this.maxExtent,c=this.getResolution(),d=c*this.tileSize.w,c=c*this.tileSize.h,e=this.getLonLatFromViewPortPx(a);a=b.left+d*Math.floor((e.lon-b.left)/d);b=b.bottom+c*Math.floor((e.lat-b.bottom)/c);return new OpenLayers.Bounds(a,b,a+d,b+c)},CLASS_NAME:"OpenLayers.Layer.Grid"});OpenLayers.Format.ArcXML=OpenLayers.Class(OpenLayers.Format.XML,{fontStyleKeys:"antialiasing blockout font fontcolor fontsize fontstyle glowing interval outline printmode shadow transparency".split(" "),request:null,response:null,initialize:function(a){this.request=new OpenLayers.Format.ArcXML.Request;this.response=new OpenLayers.Format.ArcXML.Response;if(a)if("feature"==a.requesttype){this.request.get_image=null;var b=this.request.get_feature.query;this.addCoordSys(b.featurecoordsys,a.featureCoordSys);
this.addCoordSys(b.filtercoordsys,a.filterCoordSys);a.polygon?(b.isspatial=!0,b.spatialfilter.polygon=a.polygon):a.envelope&&(b.isspatial=!0,b.spatialfilter.envelope={minx:0,miny:0,maxx:0,maxy:0},this.parseEnvelope(b.spatialfilter.envelope,a.envelope))}else"image"==a.requesttype?(this.request.get_feature=null,b=this.request.get_image.properties,this.parseEnvelope(b.envelope,a.envelope),this.addLayers(b.layerlist,a.layers),this.addImageSize(b.imagesize,a.tileSize),this.addCoordSys(b.featurecoordsys,
a.featureCoordSys),this.addCoordSys(b.filtercoordsys,a.filterCoordSys)):this.request=null;OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},parseEnvelope:function(a,b){b&&4==b.length&&(a.minx=b[0],a.miny=b[1],a.maxx=b[2],a.maxy=b[3])},addLayers:function(a,b){for(var c=0,d=b.length;c<d;c++)a.push(b[c])},addImageSize:function(a,b){null!==b&&(a.width=b.w,a.height=b.h,a.printwidth=b.w,a.printheight=b.h)},addCoordSys:function(a,b){"string"==typeof b?(a.id=parseInt(b),a.string=b):"object"==typeof b&&
null!==b.proj&&(a.id=b.proj.srsProjNumber,a.string=b.proj.srsCode)},iserror:function(a){var b=null;a?(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]),a=a.documentElement.getElementsByTagName("ERROR"),b=null!==a&&0<a.length):b=""!==this.response.error;return b},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));var b=null;a&&a.documentElement&&(b="ARCXML"==a.documentElement.nodeName?a.documentElement:a.documentElement.getElementsByTagName("ARCXML")[0]);
if(!b||"parsererror"===b.firstChild.nodeName){var c,d;try{c=a.firstChild.nodeValue,d=a.firstChild.childNodes[1].firstChild.nodeValue}catch(e){}throw{message:"Error parsing the ArcXML request",error:c,source:d};}return this.parseResponse(b)},write:function(a){a||(a=this.request);var b=this.createElementNS("","ARCXML");b.setAttribute("version","1.1");var c=this.createElementNS("","REQUEST");if(null!=a.get_image){var d=this.createElementNS("","GET_IMAGE");c.appendChild(d);var e=this.createElementNS("",
"PROPERTIES");d.appendChild(e);a=a.get_image.properties;null!=a.featurecoordsys&&(d=this.createElementNS("","FEATURECOORDSYS"),e.appendChild(d),0===a.featurecoordsys.id?d.setAttribute("string",a.featurecoordsys.string):d.setAttribute("id",a.featurecoordsys.id));null!=a.filtercoordsys&&(d=this.createElementNS("","FILTERCOORDSYS"),e.appendChild(d),0===a.filtercoordsys.id?d.setAttribute("string",a.filtercoordsys.string):d.setAttribute("id",a.filtercoordsys.id));null!=a.envelope&&(d=this.createElementNS("",
"ENVELOPE"),e.appendChild(d),d.setAttribute("minx",a.envelope.minx),d.setAttribute("miny",a.envelope.miny),d.setAttribute("maxx",a.envelope.maxx),d.setAttribute("maxy",a.envelope.maxy));d=this.createElementNS("","IMAGESIZE");e.appendChild(d);d.setAttribute("height",a.imagesize.height);d.setAttribute("width",a.imagesize.width);if(a.imagesize.height!=a.imagesize.printheight||a.imagesize.width!=a.imagesize.printwidth)d.setAttribute("printheight",a.imagesize.printheight),d.setArrtibute("printwidth",a.imagesize.printwidth);
null!=a.background&&(d=this.createElementNS("","BACKGROUND"),e.appendChild(d),d.setAttribute("color",a.background.color.r+","+a.background.color.g+","+a.background.color.b),null!==a.background.transcolor&&d.setAttribute("transcolor",a.background.transcolor.r+","+a.background.transcolor.g+","+a.background.transcolor.b));if(null!=a.layerlist&&0<a.layerlist.length)for(d=this.createElementNS("","LAYERLIST"),e.appendChild(d),e=0;e<a.layerlist.length;e++){var f=this.createElementNS("","LAYERDEF");d.appendChild(f);
f.setAttribute("id",a.layerlist[e].id);f.setAttribute("visible",a.layerlist[e].visible);if("object"==typeof a.layerlist[e].query){var g=a.layerlist[e].query;if(0>g.where.length)continue;var h=null,h="boolean"==typeof g.spatialfilter&&g.spatialfilter?this.createElementNS("","SPATIALQUERY"):this.createElementNS("","QUERY");h.setAttribute("where",g.where);"number"==typeof g.accuracy&&0<g.accuracy&&h.setAttribute("accuracy",g.accuracy);"number"==typeof g.featurelimit&&2E3>g.featurelimit&&h.setAttribute("featurelimit",
g.featurelimit);"string"==typeof g.subfields&&"#ALL#"!=g.subfields&&h.setAttribute("subfields",g.subfields);"string"==typeof g.joinexpression&&0<g.joinexpression.length&&h.setAttribute("joinexpression",g.joinexpression);"string"==typeof g.jointables&&0<g.jointables.length&&h.setAttribute("jointables",g.jointables);f.appendChild(h)}"object"==typeof a.layerlist[e].renderer&&this.addRenderer(f,a.layerlist[e].renderer)}}else null!=a.get_feature&&(d=this.createElementNS("","GET_FEATURES"),d.setAttribute("outputmode",
"newxml"),d.setAttribute("checkesc","true"),a.get_feature.geometry?d.setAttribute("geometry",a.get_feature.geometry):d.setAttribute("geometry","false"),a.get_feature.compact&&d.setAttribute("compact",a.get_feature.compact),"number"==a.get_feature.featurelimit&&d.setAttribute("featurelimit",a.get_feature.featurelimit),d.setAttribute("globalenvelope","true"),c.appendChild(d),null!=a.get_feature.layer&&0<a.get_feature.layer.length&&(e=this.createElementNS("","LAYER"),e.setAttribute("id",a.get_feature.layer),
d.appendChild(e)),a=a.get_feature.query,null!=a&&(e=null,e=a.isspatial?this.createElementNS("","SPATIALQUERY"):this.createElementNS("","QUERY"),d.appendChild(e),"number"==typeof a.accuracy&&e.setAttribute("accuracy",a.accuracy),null!=a.featurecoordsys&&(d=this.createElementNS("","FEATURECOORDSYS"),0==a.featurecoordsys.id?d.setAttribute("string",a.featurecoordsys.string):d.setAttribute("id",a.featurecoordsys.id),e.appendChild(d)),null!=a.filtercoordsys&&(d=this.createElementNS("","FILTERCOORDSYS"),
0===a.filtercoordsys.id?d.setAttribute("string",a.filtercoordsys.string):d.setAttribute("id",a.filtercoordsys.id),e.appendChild(d)),0<a.buffer&&(d=this.createElementNS("","BUFFER"),d.setAttribute("distance",a.buffer),e.appendChild(d)),a.isspatial&&(d=this.createElementNS("","SPATIALFILTER"),d.setAttribute("relation",a.spatialfilter.relation),e.appendChild(d),a.spatialfilter.envelope?(f=this.createElementNS("","ENVELOPE"),f.setAttribute("minx",a.spatialfilter.envelope.minx),f.setAttribute("miny",a.spatialfilter.envelope.miny),
f.setAttribute("maxx",a.spatialfilter.envelope.maxx),f.setAttribute("maxy",a.spatialfilter.envelope.maxy),d.appendChild(f)):"object"==typeof a.spatialfilter.polygon&&d.appendChild(this.writePolygonGeometry(a.spatialfilter.polygon))),null!=a.where&&0<a.where.length&&e.setAttribute("where",a.where)));b.appendChild(c);return OpenLayers.Format.XML.prototype.write.apply(this,[b])},addGroupRenderer:function(a,b){var c=this.createElementNS("","GROUPRENDERER");a.appendChild(c);for(var d=0;d<b.length;d++)this.addRenderer(c,
b[d])},addRenderer:function(a,b){if(OpenLayers.Util.isArray(b))this.addGroupRenderer(a,b);else{var c=this.createElementNS("",b.type.toUpperCase()+"RENDERER");a.appendChild(c);"VALUEMAPRENDERER"==c.tagName?this.addValueMapRenderer(c,b):"VALUEMAPLABELRENDERER"==c.tagName?this.addValueMapLabelRenderer(c,b):"SIMPLELABELRENDERER"==c.tagName?this.addSimpleLabelRenderer(c,b):"SCALEDEPENDENTRENDERER"==c.tagName&&this.addScaleDependentRenderer(c,b)}},addScaleDependentRenderer:function(a,b){"string"!=typeof b.lower&&
"number"!=typeof b.lower||a.setAttribute("lower",b.lower);"string"!=typeof b.upper&&"number"!=typeof b.upper||a.setAttribute("upper",b.upper);this.addRenderer(a,b.renderer)},addValueMapLabelRenderer:function(a,b){a.setAttribute("lookupfield",b.lookupfield);a.setAttribute("labelfield",b.labelfield);if("object"==typeof b.exacts)for(var c=0,d=b.exacts.length;c<d;c++){var e=b.exacts[c],f=this.createElementNS("","EXACT");"string"==typeof e.value&&f.setAttribute("value",e.value);"string"==typeof e.label&&
f.setAttribute("label",e.label);"string"==typeof e.method&&f.setAttribute("method",e.method);a.appendChild(f);if("object"==typeof e.symbol){var g=null;"text"==e.symbol.type&&(g=this.createElementNS("","TEXTSYMBOL"));if(null!=g){for(var h=this.fontStyleKeys,k=0,l=h.length;k<l;k++){var m=h[k];e.symbol[m]&&g.setAttribute(m,e.symbol[m])}f.appendChild(g)}}}},addValueMapRenderer:function(a,b){a.setAttribute("lookupfield",b.lookupfield);if("object"==typeof b.ranges)for(var c=0,d=b.ranges.length;c<d;c++){var e=
b.ranges[c],f=this.createElementNS("","RANGE");f.setAttribute("lower",e.lower);f.setAttribute("upper",e.upper);a.appendChild(f);if("object"==typeof e.symbol){var g=null;"simplepolygon"==e.symbol.type&&(g=this.createElementNS("","SIMPLEPOLYGONSYMBOL"));null!=g&&("string"==typeof e.symbol.boundarycolor&&g.setAttribute("boundarycolor",e.symbol.boundarycolor),"string"==typeof e.symbol.fillcolor&&g.setAttribute("fillcolor",e.symbol.fillcolor),"number"==typeof e.symbol.filltransparency&&g.setAttribute("filltransparency",
e.symbol.filltransparency),f.appendChild(g))}}else if("object"==typeof b.exacts)for(c=0,d=b.exacts.length;c<d;c++)e=b.exacts[c],f=this.createElementNS("","EXACT"),"string"==typeof e.value&&f.setAttribute("value",e.value),"string"==typeof e.label&&f.setAttribute("label",e.label),"string"==typeof e.method&&f.setAttribute("method",e.method),a.appendChild(f),"object"==typeof e.symbol&&(g=null,"simplemarker"==e.symbol.type&&(g=this.createElementNS("","SIMPLEMARKERSYMBOL")),null!=g&&("string"==typeof e.symbol.antialiasing&&
g.setAttribute("antialiasing",e.symbol.antialiasing),"string"==typeof e.symbol.color&&g.setAttribute("color",e.symbol.color),"string"==typeof e.symbol.outline&&g.setAttribute("outline",e.symbol.outline),"string"==typeof e.symbol.overlap&&g.setAttribute("overlap",e.symbol.overlap),"string"==typeof e.symbol.shadow&&g.setAttribute("shadow",e.symbol.shadow),"number"==typeof e.symbol.transparency&&g.setAttribute("transparency",e.symbol.transparency),"string"==typeof e.symbol.usecentroid&&g.setAttribute("usecentroid",
e.symbol.usecentroid),"number"==typeof e.symbol.width&&g.setAttribute("width",e.symbol.width),f.appendChild(g)))},addSimpleLabelRenderer:function(a,b){a.setAttribute("field",b.field);for(var c="featureweight howmanylabels labelbufferratio labelpriorities labelweight linelabelposition rotationalangles".split(" "),d=0,e=c.length;d<e;d++){var f=c[d];b[f]&&a.setAttribute(f,b[f])}if("text"==b.symbol.type){var g=b.symbol,h=this.createElementNS("","TEXTSYMBOL");a.appendChild(h);c=this.fontStyleKeys;d=0;
for(e=c.length;d<e;d++)f=c[d],g[f]&&h.setAttribute(f,b[f])}},writePolygonGeometry:function(a){if(!(a instanceof OpenLayers.Geometry.Polygon))throw{message:"Cannot write polygon geometry to ArcXML with an "+a.CLASS_NAME+" object.",geometry:a};for(var b=this.createElementNS("","POLYGON"),c=0,d=a.components.length;c<d;c++){for(var e=a.components[c],f=this.createElementNS("","RING"),g=0,h=e.components.length;g<h;g++){var k=e.components[g],l=this.createElementNS("","POINT");l.setAttribute("x",k.x);l.setAttribute("y",
k.y);f.appendChild(l)}b.appendChild(f)}return b},parseResponse:function(a){"string"==typeof a&&(a=(new OpenLayers.Format.XML).read(a));var b=new OpenLayers.Format.ArcXML.Response,c=a.getElementsByTagName("ERROR");if(null!=c&&0<c.length)b.error=this.getChildValue(c,"Unknown error.");else{c=a.getElementsByTagName("RESPONSE");if(null==c||0==c.length)return b.error="No RESPONSE tag found in ArcXML response.",b;var d=c[0].firstChild.nodeName;"#text"==d&&(d=c[0].firstChild.nextSibling.nodeName);if("IMAGE"==
d)c=a.getElementsByTagName("ENVELOPE"),a=a.getElementsByTagName("OUTPUT"),null==c||0==c.length?b.error="No ENVELOPE tag found in ArcXML response.":null==a||0==a.length?b.error="No OUTPUT tag found in ArcXML response.":(c=this.parseAttributes(c[0]),d=this.parseAttributes(a[0]),b.image="string"==typeof d.type?{envelope:c,output:{type:d.type,data:this.getChildValue(a[0])}}:{envelope:c,output:d});else if("FEATURES"==d){if(a=c[0].getElementsByTagName("FEATURES"),c=a[0].getElementsByTagName("FEATURECOUNT"),
b.features.featurecount=c[0].getAttribute("count"),0<b.features.featurecount)for(c=a[0].getElementsByTagName("ENVELOPE"),b.features.envelope=this.parseAttributes(c[0],"number"),a=a[0].getElementsByTagName("FEATURE"),c=0;c<a.length;c++){for(var d=new OpenLayers.Feature.Vector,e=a[c].getElementsByTagName("FIELD"),f=0;f<e.length;f++){var g=e[f].getAttribute("name"),h=e[f].getAttribute("value");d.attributes[g]=h}e=a[c].getElementsByTagName("POLYGON");if(0<e.length){e=e[0].getElementsByTagName("RING");
f=[];for(g=0;g<e.length;g++){h=[];h.push(this.parsePointGeometry(e[g]));for(var k=e[g].getElementsByTagName("HOLE"),l=0;l<k.length;l++)h.push(this.parsePointGeometry(k[l]));f.push(new OpenLayers.Geometry.Polygon(h))}d.geometry=1==f.length?f[0]:new OpenLayers.Geometry.MultiPolygon(f)}b.features.feature.push(d)}}else b.error="Unidentified response type."}return b},parseAttributes:function(a,b){for(var c={},d=0;d<a.attributes.length;d++)c[a.attributes[d].nodeName]="number"==b?parseFloat(a.attributes[d].nodeValue):
a.attributes[d].nodeValue;return c},parsePointGeometry:function(a){var b=[],c=a.getElementsByTagName("COORDS");if(0<c.length)for(a=this.getChildValue(c[0]),a=a.split(/;/),c=0;c<a.length;c++){var d=a[c].split(/ /);b.push(new OpenLayers.Geometry.Point(d[0],d[1]))}else if(a=a.getElementsByTagName("POINT"),0<a.length)for(c=0;c<a.length;c++)b.push(new OpenLayers.Geometry.Point(parseFloat(a[c].getAttribute("x")),parseFloat(a[c].getAttribute("y"))));return new OpenLayers.Geometry.LinearRing(b)},CLASS_NAME:"OpenLayers.Format.ArcXML"});
OpenLayers.Format.ArcXML.Request=OpenLayers.Class({initialize:function(a){return OpenLayers.Util.extend(this,{get_image:{properties:{background:null,draw:!0,envelope:{minx:0,miny:0,maxx:0,maxy:0},featurecoordsys:{id:0,string:"",datumtransformid:0,datumtransformstring:""},filtercoordsys:{id:0,string:"",datumtransformid:0,datumtransformstring:""},imagesize:{height:0,width:0,dpi:96,printheight:0,printwidth:0,scalesymbols:!1},layerlist:[],output:{baseurl:"",legendbaseurl:"",legendname:"",legendpath:"",
legendurl:"",name:"",path:"",type:"jpg",url:""}}},get_feature:{layer:"",query:{isspatial:!1,featurecoordsys:{id:0,string:"",datumtransformid:0,datumtransformstring:""},filtercoordsys:{id:0,string:"",datumtransformid:0,datumtransformstring:""},buffer:0,where:"",spatialfilter:{relation:"envelope_intersection",envelope:null}}},environment:{separators:{cs:" ",ts:";"}},layer:[],workspaces:[]})},CLASS_NAME:"OpenLayers.Format.ArcXML.Request"});
OpenLayers.Format.ArcXML.Response=OpenLayers.Class({initialize:function(a){return OpenLayers.Util.extend(this,{image:{envelope:null,output:""},features:{featurecount:0,envelope:null,feature:[]},error:""})},CLASS_NAME:"OpenLayers.Format.ArcXML.Response"});(function(){function a(){this._object=f&&!k?new f:new window.ActiveXObject("Microsoft.XMLHTTP");this._listeners=[]}function b(){return new a}function c(a){b.onreadystatechange&&b.onreadystatechange.apply(a);a.dispatchEvent({type:"readystatechange",bubbles:!1,cancelable:!1,timeStamp:new Date+0})}function d(a){try{a.responseText=a._object.responseText}catch(b){}try{var c;var d=a._object,e=d.responseXML,f=d.responseText;h&&(f&&e&&!e.documentElement&&d.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/))&&
(e=new window.ActiveXObject("Microsoft.XMLDOM"),e.async=!1,e.validateOnParse=!1,e.loadXML(f));c=e&&(h&&0!=e.parseError||!e.documentElement||e.documentElement&&"parsererror"==e.documentElement.tagName)?null:e;a.responseXML=c}catch(g){}try{a.status=a._object.status}catch(k){}try{a.statusText=a._object.statusText}catch(u){}}function e(a){a._object.onreadystatechange=new window.Function}var f=window.XMLHttpRequest,g=!!window.controllers,h=window.document.all&&!window.opera,k=h&&window.navigator.userAgent.match(/MSIE 7.0/);
b.prototype=a.prototype;g&&f.wrapped&&(b.wrapped=f.wrapped);b.UNSENT=0;b.OPENED=1;b.HEADERS_RECEIVED=2;b.LOADING=3;b.DONE=4;b.prototype.readyState=b.UNSENT;b.prototype.responseText="";b.prototype.responseXML=null;b.prototype.status=0;b.prototype.statusText="";b.prototype.priority="NORMAL";b.prototype.onreadystatechange=null;b.onreadystatechange=null;b.onopen=null;b.onsend=null;b.onabort=null;b.prototype.open=function(a,f,k,p,q){delete this._headers;3>arguments.length&&(k=!0);this._async=k;var r=this,
s=this.readyState,t;h&&k&&(t=function(){s!=b.DONE&&(e(r),r.abort())},window.attachEvent("onunload",t));b.onopen&&b.onopen.apply(this,arguments);4<arguments.length?this._object.open(a,f,k,p,q):3<arguments.length?this._object.open(a,f,k,p):this._object.open(a,f,k);this.readyState=b.OPENED;c(this);this._object.onreadystatechange=function(){if(!g||k)r.readyState=r._object.readyState,d(r),r._aborted?r.readyState=b.UNSENT:(r.readyState==b.DONE&&(delete r._data,e(r),h&&k&&window.detachEvent("onunload",t)),
s!=r.readyState&&c(r),s=r.readyState)}};b.prototype.send=function(a){b.onsend&&b.onsend.apply(this,arguments);arguments.length||(a=null);a&&a.nodeType&&(a=window.XMLSerializer?(new window.XMLSerializer).serializeToString(a):a.xml,this._headers["Content-Type"]||this._object.setRequestHeader("Content-Type","application/xml"));this._data=a;a:if(this._object.send(this._data),g&&!this._async)for(this.readyState=b.OPENED,d(this);this.readyState<b.DONE;)if(this.readyState++,c(this),this._aborted)break a};
b.prototype.abort=function(){b.onabort&&b.onabort.apply(this,arguments);this.readyState>b.UNSENT&&(this._aborted=!0);this._object.abort();e(this);this.readyState=b.UNSENT;delete this._data};b.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders()};b.prototype.getResponseHeader=function(a){return this._object.getResponseHeader(a)};b.prototype.setRequestHeader=function(a,b){this._headers||(this._headers={});this._headers[a]=b;return this._object.setRequestHeader(a,b)};
b.prototype.addEventListener=function(a,b,c){for(var d=0,e;e=this._listeners[d];d++)if(e[0]==a&&e[1]==b&&e[2]==c)return;this._listeners.push([a,b,c])};b.prototype.removeEventListener=function(a,b,c){for(var d=0,e;(e=this._listeners[d])&&(e[0]!=a||e[1]!=b||e[2]!=c);d++);e&&this._listeners.splice(d,1)};b.prototype.dispatchEvent=function(a){a={type:a.type,target:this,currentTarget:this,eventPhase:2,bubbles:a.bubbles,cancelable:a.cancelable,timeStamp:a.timeStamp,stopPropagation:function(){},preventDefault:function(){},
initEvent:function(){}};"readystatechange"==a.type&&this.onreadystatechange&&(this.onreadystatechange.handleEvent||this.onreadystatechange).apply(this,[a]);for(var b=0,c;c=this._listeners[b];b++)c[0]!=a.type||c[2]||(c[1].handleEvent||c[1]).apply(this,[a])};b.prototype.toString=function(){return"[object XMLHttpRequest]"};b.toString=function(){return"[XMLHttpRequest]"};window.Function.prototype.apply||(window.Function.prototype.apply=function(a,b){b||(b=[]);a.__func=this;a.__func(b[0],b[1],b[2],b[3],
b[4]);delete a.__func});OpenLayers.Request||(OpenLayers.Request={});OpenLayers.Request.XMLHttpRequest=b})();OpenLayers.ProxyHost="";OpenLayers.Request||(OpenLayers.Request={});
OpenLayers.Util.extend(OpenLayers.Request,{DEFAULT_CONFIG:{method:"GET",url:window.location.href,async:!0,user:void 0,password:void 0,params:null,proxy:OpenLayers.ProxyHost,headers:{},data:null,callback:function(){},success:null,failure:null,scope:null},URL_SPLIT_REGEX:/([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,events:new OpenLayers.Events(this),makeSameOrigin:function(a,b){var c=0!==a.indexOf("http"),d=!c&&a.match(this.URL_SPLIT_REGEX);if(d){var e=window.location,c=d[1]==e.protocol&&d[3]==
e.hostname,d=d[4],e=e.port;if(80!=d&&""!=d||"80"!=e&&""!=e)c=c&&d==e}c||b&&(a="function"==typeof b?b(a):b+encodeURIComponent(a));return a},issue:function(a){var b=OpenLayers.Util.extend(this.DEFAULT_CONFIG,{proxy:OpenLayers.ProxyHost});a=a||{};a.headers=a.headers||{};a=OpenLayers.Util.applyDefaults(a,b);a.headers=OpenLayers.Util.applyDefaults(a.headers,b.headers);var b=!1,c;for(c in a.headers)a.headers.hasOwnProperty(c)&&"x-requested-with"===c.toLowerCase()&&(b=!0);!1===b&&(a.headers["X-Requested-With"]=
"XMLHttpRequest");var d=new OpenLayers.Request.XMLHttpRequest,e=OpenLayers.Util.urlAppend(a.url,OpenLayers.Util.getParameterString(a.params||{})),e=OpenLayers.Request.makeSameOrigin(e,a.proxy);d.open(a.method,e,a.async,a.user,a.password);for(var f in a.headers)d.setRequestHeader(f,a.headers[f]);var g=this.events,h=this;d.onreadystatechange=function(){d.readyState==OpenLayers.Request.XMLHttpRequest.DONE&&!1!==g.triggerEvent("complete",{request:d,config:a,requestUrl:e})&&h.runCallbacks({request:d,config:a,
requestUrl:e})};!1===a.async?d.send(a.data):window.setTimeout(function(){0!==d.readyState&&d.send(a.data)},0);return d},runCallbacks:function(a){var b=a.request,c=a.config,d=c.scope?OpenLayers.Function.bind(c.callback,c.scope):c.callback,e;c.success&&(e=c.scope?OpenLayers.Function.bind(c.success,c.scope):c.success);var f;c.failure&&(f=c.scope?OpenLayers.Function.bind(c.failure,c.scope):c.failure);"file:"==OpenLayers.Util.createUrlObject(c.url).protocol&&b.responseText&&(b.status=200);d(b);if(!b.status||
200<=b.status&&300>b.status)this.events.triggerEvent("success",a),e&&e(b);b.status&&(200>b.status||300<=b.status)&&(this.events.triggerEvent("failure",a),f&&f(b))},GET:function(a){a=OpenLayers.Util.extend(a,{method:"GET"});return OpenLayers.Request.issue(a)},POST:function(a){a=OpenLayers.Util.extend(a,{method:"POST"});a.headers=a.headers?a.headers:{};"CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(a.headers)||(a.headers["Content-Type"]="application/xml");return OpenLayers.Request.issue(a)},PUT:function(a){a=
OpenLayers.Util.extend(a,{method:"PUT"});a.headers=a.headers?a.headers:{};"CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(a.headers)||(a.headers["Content-Type"]="application/xml");return OpenLayers.Request.issue(a)},DELETE:function(a){a=OpenLayers.Util.extend(a,{method:"DELETE"});return OpenLayers.Request.issue(a)},HEAD:function(a){a=OpenLayers.Util.extend(a,{method:"HEAD"});return OpenLayers.Request.issue(a)},OPTIONS:function(a){a=OpenLayers.Util.extend(a,{method:"OPTIONS"});return OpenLayers.Request.issue(a)}});OpenLayers.Layer.ArcIMS=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{ClientVersion:"9.2",ServiceName:""},featureCoordSys:"4326",filterCoordSys:"4326",layers:null,async:!0,name:"ArcIMS",isBaseLayer:!0,DEFAULT_OPTIONS:{tileSize:new OpenLayers.Size(512,512),featureCoordSys:"4326",filterCoordSys:"4326",layers:null,isBaseLayer:!0,async:!0,name:"ArcIMS"},initialize:function(a,b,c){this.tileSize=new OpenLayers.Size(512,512);this.params=OpenLayers.Util.applyDefaults({ServiceName:c.serviceName},
this.DEFAULT_PARAMS);this.options=OpenLayers.Util.applyDefaults(c,this.DEFAULT_OPTIONS);OpenLayers.Layer.Grid.prototype.initialize.apply(this,[a,b,this.params,c]);this.transparent&&(this.isBaseLayer||(this.isBaseLayer=!1),"image/jpeg"==this.format&&(this.format=OpenLayers.Util.alphaHack()?"image/gif":"image/png"));null===this.options.layers&&(this.options.layers=[])},getURL:function(a){var b="";a=this.adjustBounds(a);a=new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options,{requesttype:"image",
envelope:a.toArray(),tileSize:this.tileSize}));a=new OpenLayers.Request.POST({url:this.getFullRequestString(),data:a.write(),async:!1});null!=a&&(b=a.responseXML,b&&b.documentElement||(b=a.responseText),b=(new OpenLayers.Format.ArcXML).read(b),b=this.getUrlOrImage(b.image.output));return b},getURLasync:function(a,b,c){a=this.adjustBounds(a);a=new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options,{requesttype:"image",envelope:a.toArray(),tileSize:this.tileSize}));OpenLayers.Request.POST({url:this.getFullRequestString(),
async:!0,data:a.write(),callback:function(a){var e=a.responseXML;e&&e.documentElement||(e=a.responseText);a=(new OpenLayers.Format.ArcXML).read(e);b.call(c,this.getUrlOrImage(a.image.output))},scope:this})},getUrlOrImage:function(a){var b="";a.url?b=a.url:a.data&&(b="data:image/"+a.type+";base64,"+a.data);return b},setLayerQuery:function(a,b){for(var c=0;c<this.options.layers.length;c++)if(a==this.options.layers[c].id){this.options.layers[c].query=b;return}this.options.layers.push({id:a,visible:!0,
query:b})},getFeatureInfo:function(a,b,c){var d=c.buffer||1,e=c.callback||function(){},f=c.scope||window,g={};OpenLayers.Util.extend(g,this.options);g.requesttype="feature";a instanceof OpenLayers.LonLat?(g.polygon=null,g.envelope=[a.lon-d,a.lat-d,a.lon+d,a.lat+d]):a instanceof OpenLayers.Geometry.Polygon&&(g.envelope=null,g.polygon=a);var h=new OpenLayers.Format.ArcXML(g);OpenLayers.Util.extend(h.request.get_feature,c);h.request.get_feature.layer=b.id;"number"==typeof b.query.accuracy?h.request.get_feature.query.accuracy=
b.query.accuracy:(a=this.map.getCenter(),c=this.map.getViewPortPxFromLonLat(a),c.x++,c=this.map.getLonLatFromPixel(c),h.request.get_feature.query.accuracy=c.lon-a.lon);h.request.get_feature.query.where=b.query.where;h.request.get_feature.query.spatialfilter.relation="area_intersection";OpenLayers.Request.POST({url:this.getFullRequestString({CustomService:"Query"}),data:h.write(),callback:function(a){a=h.parseResponse(a.responseText);h.iserror()?e.call(f,null):e.call(f,a.features)}})},clone:function(a){null==
a&&(a=new OpenLayers.Layer.ArcIMS(this.name,this.url,this.getOptions()));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,[a])},CLASS_NAME:"OpenLayers.Layer.ArcIMS"});OpenLayers.Control.PanZoom=OpenLayers.Class(OpenLayers.Control,{slideFactor:50,slideRatio:null,buttons:null,position:null,initialize:function(a){this.position=new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,OpenLayers.Control.PanZoom.Y);OpenLayers.Control.prototype.initialize.apply(this,arguments)},destroy:function(){this.map&&this.map.events.unregister("buttonclick",this,this.onButtonClick);this.removeButtons();this.position=this.buttons=null;OpenLayers.Control.prototype.destroy.apply(this,arguments)},
setMap:function(a){OpenLayers.Control.prototype.setMap.apply(this,arguments);this.map.events.register("buttonclick",this,this.onButtonClick)},draw:function(a){OpenLayers.Control.prototype.draw.apply(this,arguments);a=this.position;this.buttons=[];var b={w:18,h:18},c=new OpenLayers.Pixel(a.x+b.w/2,a.y);this._addButton("panup","north-mini.png",c,b);a.y=c.y+b.h;this._addButton("panleft","west-mini.png",a,b);this._addButton("panright","east-mini.png",a.add(b.w,0),b);this._addButton("pandown","south-mini.png",
c.add(0,2*b.h),b);this._addButton("zoomin","zoom-plus-mini.png",c.add(0,3*b.h+5),b);this._addButton("zoomworld","zoom-world-mini.png",c.add(0,4*b.h+5),b);this._addButton("zoomout","zoom-minus-mini.png",c.add(0,5*b.h+5),b);return this.div},_addButton:function(a,b,c,d){b=OpenLayers.Util.getImageLocation(b);c=OpenLayers.Util.createAlphaImageDiv(this.id+"_"+a,c,d,b,"absolute");c.style.cursor="pointer";this.div.appendChild(c);c.action=a;c.className="olButton";this.buttons.push(c);return c},_removeButton:function(a){this.div.removeChild(a);
OpenLayers.Util.removeItem(this.buttons,a)},removeButtons:function(){for(var a=this.buttons.length-1;0<=a;--a)this._removeButton(this.buttons[a])},onButtonClick:function(a){switch(a.buttonElement.action){case "panup":this.map.pan(0,-this.getSlideFactor("h"));break;case "pandown":this.map.pan(0,this.getSlideFactor("h"));break;case "panleft":this.map.pan(-this.getSlideFactor("w"),0);break;case "panright":this.map.pan(this.getSlideFactor("w"),0);break;case "zoomin":this.map.zoomIn();break;case "zoomout":this.map.zoomOut();
break;case "zoomworld":this.map.zoomToMaxExtent()}},getSlideFactor:function(a){return this.slideRatio?this.map.getSize()[a]*this.slideRatio:this.slideFactor},CLASS_NAME:"OpenLayers.Control.PanZoom"});OpenLayers.Control.PanZoom.X=4;OpenLayers.Control.PanZoom.Y=4;OpenLayers.Control.PanZoomBar=OpenLayers.Class(OpenLayers.Control.PanZoom,{zoomStopWidth:18,zoomStopHeight:11,slider:null,sliderEvents:null,zoombarDiv:null,zoomWorldIcon:!1,panIcons:!0,forceFixedZoomLevel:!1,mouseDragStart:null,deltaY:null,zoomStart:null,destroy:function(){this._removeZoomBar();this.map.events.un({changebaselayer:this.redraw,updatesize:this.redraw,scope:this});OpenLayers.Control.PanZoom.prototype.destroy.apply(this,arguments);delete this.mouseDragStart;delete this.zoomStart},setMap:function(a){OpenLayers.Control.PanZoom.prototype.setMap.apply(this,
arguments);this.map.events.on({changebaselayer:this.redraw,updatesize:this.redraw,scope:this})},redraw:function(){null!=this.div&&(this.removeButtons(),this._removeZoomBar());this.draw()},draw:function(a){OpenLayers.Control.prototype.draw.apply(this,arguments);a=this.position.clone();this.buttons=[];var b={w:18,h:18};if(this.panIcons){var c=new OpenLayers.Pixel(a.x+b.w/2,a.y),d=b.w;this.zoomWorldIcon&&(c=new OpenLayers.Pixel(a.x+b.w,a.y));this._addButton("panup","north-mini.png",c,b);a.y=c.y+b.h;
this._addButton("panleft","west-mini.png",a,b);this.zoomWorldIcon&&(this._addButton("zoomworld","zoom-world-mini.png",a.add(b.w,0),b),d*=2);this._addButton("panright","east-mini.png",a.add(d,0),b);this._addButton("pandown","south-mini.png",c.add(0,2*b.h),b);this._addButton("zoomin","zoom-plus-mini.png",c.add(0,3*b.h+5),b);c=this._addZoomBar(c.add(0,4*b.h+5));this._addButton("zoomout","zoom-minus-mini.png",c,b)}else this._addButton("zoomin","zoom-plus-mini.png",a,b),c=this._addZoomBar(a.add(0,b.h)),
this._addButton("zoomout","zoom-minus-mini.png",c,b),this.zoomWorldIcon&&(c=c.add(0,b.h+3),this._addButton("zoomworld","zoom-world-mini.png",c,b));return this.div},_addZoomBar:function(a){var b=OpenLayers.Util.getImageLocation("slider.png"),c=this.id+"_"+this.map.id,d=this.map.getMinZoom(),e=this.map.getNumZoomLevels()-1-this.map.getZoom(),e=OpenLayers.Util.createAlphaImageDiv(c,a.add(-1,e*this.zoomStopHeight),{w:20,h:9},b,"absolute");e.style.cursor="move";this.slider=e;this.sliderEvents=new OpenLayers.Events(this,
e,null,!0,{includeXY:!0});this.sliderEvents.on({touchstart:this.zoomBarDown,touchmove:this.zoomBarDrag,touchend:this.zoomBarUp,mousedown:this.zoomBarDown,mousemove:this.zoomBarDrag,mouseup:this.zoomBarUp});var f={w:this.zoomStopWidth,h:this.zoomStopHeight*(this.map.getNumZoomLevels()-d)},b=OpenLayers.Util.getImageLocation("zoombar.png"),c=null;OpenLayers.Util.alphaHack()?(c=this.id+"_"+this.map.id,c=OpenLayers.Util.createAlphaImageDiv(c,a,{w:f.w,h:this.zoomStopHeight},b,"absolute",null,"crop"),c.style.height=
f.h+"px"):c=OpenLayers.Util.createDiv("OpenLayers_Control_PanZoomBar_Zoombar"+this.map.id,a,f,b);c.style.cursor="pointer";c.className="olButton";this.zoombarDiv=c;this.div.appendChild(c);this.startTop=parseInt(c.style.top);this.div.appendChild(e);this.map.events.register("zoomend",this,this.moveZoomBar);return a=a.add(0,this.zoomStopHeight*(this.map.getNumZoomLevels()-d))},_removeZoomBar:function(){this.sliderEvents.un({touchstart:this.zoomBarDown,touchmove:this.zoomBarDrag,touchend:this.zoomBarUp,
mousedown:this.zoomBarDown,mousemove:this.zoomBarDrag,mouseup:this.zoomBarUp});this.sliderEvents.destroy();this.div.removeChild(this.zoombarDiv);this.zoombarDiv=null;this.div.removeChild(this.slider);this.slider=null;this.map.events.unregister("zoomend",this,this.moveZoomBar)},onButtonClick:function(a){OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this,arguments);if(a.buttonElement===this.zoombarDiv){var b=a.buttonXY.y/this.zoomStopHeight;if(this.forceFixedZoomLevel||!this.map.fractionalZoom)b=
Math.floor(b);b=this.map.getNumZoomLevels()-1-b;b=Math.min(Math.max(b,0),this.map.getNumZoomLevels()-1);this.map.zoomTo(b)}},passEventToSlider:function(a){this.sliderEvents.handleBrowserEvent(a)},zoomBarDown:function(a){if(OpenLayers.Event.isLeftClick(a)||OpenLayers.Event.isSingleTouch(a))this.map.events.on({touchmove:this.passEventToSlider,mousemove:this.passEventToSlider,mouseup:this.passEventToSlider,scope:this}),this.mouseDragStart=a.xy.clone(),this.zoomStart=a.xy.clone(),this.div.style.cursor=
"move",this.zoombarDiv.offsets=null,OpenLayers.Event.stop(a)},zoomBarDrag:function(a){if(null!=this.mouseDragStart){var b=this.mouseDragStart.y-a.xy.y,c=OpenLayers.Util.pagePosition(this.zoombarDiv);0<a.clientY-c[1]&&a.clientY-c[1]<parseInt(this.zoombarDiv.style.height)-2&&(b=parseInt(this.slider.style.top)-b,this.slider.style.top=b+"px",this.mouseDragStart=a.xy.clone());this.deltaY=this.zoomStart.y-a.xy.y;OpenLayers.Event.stop(a)}},zoomBarUp:function(a){if((OpenLayers.Event.isLeftClick(a)||"touchend"===
a.type)&&this.mouseDragStart){this.div.style.cursor="";this.map.events.un({touchmove:this.passEventToSlider,mouseup:this.passEventToSlider,mousemove:this.passEventToSlider,scope:this});var b=this.map.zoom;!this.forceFixedZoomLevel&&this.map.fractionalZoom?(b+=this.deltaY/this.zoomStopHeight,b=Math.min(Math.max(b,0),this.map.getNumZoomLevels()-1)):(b+=this.deltaY/this.zoomStopHeight,b=Math.max(Math.round(b),0));this.map.zoomTo(b);this.zoomStart=this.mouseDragStart=null;this.deltaY=0;OpenLayers.Event.stop(a)}},
moveZoomBar:function(){var a=(this.map.getNumZoomLevels()-1-this.map.getZoom())*this.zoomStopHeight+this.startTop+1;this.slider.style.top=a+"px"},CLASS_NAME:"OpenLayers.Control.PanZoomBar"});OpenLayers.Format.WFSCapabilities=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.1.0",CLASS_NAME:"OpenLayers.Format.WFSCapabilities"});OpenLayers.Format.WFSCapabilities.v1=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{wfs:"http://www.opengis.net/wfs",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance",ows:"http://www.opengis.net/ows"},errorProperty:"featureTypeList",defaultPrefix:"wfs",read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={};this.readNode(a,b);return b},readers:{wfs:{WFS_Capabilities:function(a,
b){this.readChildNodes(a,b)},FeatureTypeList:function(a,b){b.featureTypeList={featureTypes:[]};this.readChildNodes(a,b.featureTypeList)},FeatureType:function(a,b){var c={};this.readChildNodes(a,c);b.featureTypes.push(c)},Name:function(a,b){var c=this.getChildValue(a);c&&(c=c.split(":"),b.name=c.pop(),0<c.length&&(b.featureNS=this.lookupNamespaceURI(a,c[0])))},Title:function(a,b){var c=this.getChildValue(a);c&&(b.title=c)},Abstract:function(a,b){var c=this.getChildValue(a);c&&(b["abstract"]=c)}}},
CLASS_NAME:"OpenLayers.Format.WFSCapabilities.v1"});OpenLayers.Format.WFSCapabilities.v1_1_0=OpenLayers.Class(OpenLayers.Format.WFSCapabilities.v1,{regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},readers:{wfs:OpenLayers.Util.applyDefaults({DefaultSRS:function(a,b){var c=this.getChildValue(a);c&&(b.srs=c)}},OpenLayers.Format.WFSCapabilities.v1.prototype.readers.wfs),ows:OpenLayers.Format.OWSCommon.v1.prototype.readers.ows},CLASS_NAME:"OpenLayers.Format.WFSCapabilities.v1_1_0"});OpenLayers.Layer.Image=OpenLayers.Class(OpenLayers.Layer,{isBaseLayer:!0,url:null,extent:null,size:null,tile:null,aspectRatio:null,initialize:function(a,b,c,d,e){this.url=b;this.maxExtent=this.extent=c;this.size=d;OpenLayers.Layer.prototype.initialize.apply(this,[a,e]);this.aspectRatio=this.extent.getHeight()/this.size.h/(this.extent.getWidth()/this.size.w)},destroy:function(){this.tile&&(this.removeTileMonitoringHooks(this.tile),this.tile.destroy(),this.tile=null);OpenLayers.Layer.prototype.destroy.apply(this,
arguments)},clone:function(a){null==a&&(a=new OpenLayers.Layer.Image(this.name,this.url,this.extent,this.size,this.getOptions()));return a=OpenLayers.Layer.prototype.clone.apply(this,[a])},setMap:function(a){null==this.options.maxResolution&&(this.options.maxResolution=this.aspectRatio*this.extent.getWidth()/this.size.w);OpenLayers.Layer.prototype.setMap.apply(this,arguments)},moveTo:function(a,b,c){OpenLayers.Layer.prototype.moveTo.apply(this,arguments);var d=null==this.tile;if(b||d){this.setTileSize();
var e=this.map.getLayerPxFromLonLat({lon:this.extent.left,lat:this.extent.top});d?(this.tile=new OpenLayers.Tile.Image(this,e,this.extent,null,this.tileSize),this.addTileMonitoringHooks(this.tile)):(this.tile.size=this.tileSize.clone(),this.tile.position=e.clone());this.tile.draw()}},setTileSize:function(){var a=this.extent.getWidth()/this.map.getResolution(),b=this.extent.getHeight()/this.map.getResolution();this.tileSize=new OpenLayers.Size(a,b)},addTileMonitoringHooks:function(a){a.onLoadStart=
function(){this.events.triggerEvent("loadstart")};a.events.register("loadstart",this,a.onLoadStart);a.onLoadEnd=function(){this.events.triggerEvent("loadend")};a.events.register("loadend",this,a.onLoadEnd);a.events.register("unload",this,a.onLoadEnd)},removeTileMonitoringHooks:function(a){a.unload();a.events.un({loadstart:a.onLoadStart,loadend:a.onLoadEnd,unload:a.onLoadEnd,scope:this})},setUrl:function(a){this.url=a;this.tile.draw()},getURL:function(a){return this.url},CLASS_NAME:"OpenLayers.Layer.Image"});OpenLayers.Strategy=OpenLayers.Class({layer:null,options:null,active:null,autoActivate:!0,autoDestroy:!0,initialize:function(a){OpenLayers.Util.extend(this,a);this.options=a;this.active=!1},destroy:function(){this.deactivate();this.options=this.layer=null},setLayer:function(a){this.layer=a},activate:function(){return this.active?!1:this.active=!0},deactivate:function(){return this.active?(this.active=!1,!0):!1},CLASS_NAME:"OpenLayers.Strategy"});OpenLayers.Strategy.Save=OpenLayers.Class(OpenLayers.Strategy,{events:null,auto:!1,timer:null,initialize:function(a){OpenLayers.Strategy.prototype.initialize.apply(this,[a]);this.events=new OpenLayers.Events(this)},activate:function(){var a=OpenLayers.Strategy.prototype.activate.call(this);if(a&&this.auto)if("number"===typeof this.auto)this.timer=window.setInterval(OpenLayers.Function.bind(this.save,this),1E3*this.auto);else this.layer.events.on({featureadded:this.triggerSave,afterfeaturemodified:this.triggerSave,
scope:this});return a},deactivate:function(){var a=OpenLayers.Strategy.prototype.deactivate.call(this);a&&this.auto&&("number"===typeof this.auto?window.clearInterval(this.timer):this.layer.events.un({featureadded:this.triggerSave,afterfeaturemodified:this.triggerSave,scope:this}));return a},triggerSave:function(a){var b=a.feature;b.state!==OpenLayers.State.INSERT&&b.state!==OpenLayers.State.UPDATE&&b.state!==OpenLayers.State.DELETE||this.save([a.feature])},save:function(a){a||(a=this.layer.features);
this.events.triggerEvent("start",{features:a});var b=this.layer.projection,c=this.layer.map.getProjectionObject();if(!c.equals(b)){for(var d=a.length,e=Array(d),f,g,h=0;h<d;++h)f=a[h],g=f.clone(),g.fid=f.fid,g.state=f.state,f.url&&(g.url=f.url),g._original=f,g.geometry.transform(c,b),e[h]=g;a=e}this.layer.protocol.commit(a,{callback:this.onCommit,scope:this})},onCommit:function(a){var b={response:a};if(a.success()){for(var c=a.reqFeatures,d,e=[],f=a.insertIds||[],g=0,h=0,k=c.length;h<k;++h)if(d=c[h],
d=d._original||d,a=d.state)a==OpenLayers.State.DELETE?e.push(d):a==OpenLayers.State.INSERT&&(d.fid=f[g],++g),d.state=null;0<e.length&&this.layer.destroyFeatures(e);this.events.triggerEvent("success",b)}else this.events.triggerEvent("fail",b)},CLASS_NAME:"OpenLayers.Strategy.Save"});OpenLayers.Events.featureclick=OpenLayers.Class({cache:null,map:null,provides:["featureclick","nofeatureclick","featureover","featureout"],initialize:function(a){this.target=a;if(a.object instanceof OpenLayers.Map)this.setMap(a.object);else if(a.object instanceof OpenLayers.Layer.Vector)a.object.map?this.setMap(a.object.map):a.object.events.register("added",this,function(b){this.setMap(a.object.map)});else throw"Listeners for '"+this.provides.join("', '")+"' events can only be registered for OpenLayers.Layer.Vector or OpenLayers.Map instances";
for(var b=this.provides.length-1;0<=b;--b)a.extensions[this.provides[b]]=!0},setMap:function(a){this.map=a;this.cache={};a.events.register("mousedown",this,this.start,{extension:!0});a.events.register("mouseup",this,this.onClick,{extension:!0});a.events.register("touchstart",this,this.start,{extension:!0});a.events.register("touchmove",this,this.cancel,{extension:!0});a.events.register("touchend",this,this.onClick,{extension:!0});a.events.register("mousemove",this,this.onMousemove,{extension:!0})},
start:function(a){this.startEvt=a},cancel:function(a){delete this.startEvt},onClick:function(a){if(this.startEvt&&("touchend"===a.type||OpenLayers.Event.isLeftClick(a))){a=this.getFeatures(this.startEvt);delete this.startEvt;for(var b,c,d={},e=0,f=a.length;e<f&&(b=a[e],c=b.layer,d[c.id]=!0,b=this.triggerEvent("featureclick",{feature:b}),!1!==b);++e);e=0;for(f=this.map.layers.length;e<f;++e)c=this.map.layers[e],c instanceof OpenLayers.Layer.Vector&&!d[c.id]&&this.triggerEvent("nofeatureclick",{layer:c})}},
onMousemove:function(a){delete this.startEvt;var b=this.getFeatures(a),c={};a=[];for(var d,e=0,f=b.length;e<f;++e)d=b[e],c[d.id]=d,this.cache[d.id]||a.push(d);var b=[],g;for(g in this.cache)d=this.cache[g],d.layer&&d.layer.map?c[d.id]||b.push(d):delete this.cache[g];e=0;for(f=a.length;e<f&&(d=a[e],this.cache[d.id]=d,g=this.triggerEvent("featureover",{feature:d}),!1!==g);++e);e=0;for(f=b.length;e<f&&(d=b[e],delete this.cache[d.id],g=this.triggerEvent("featureout",{feature:d}),!1!==g);++e);},triggerEvent:function(a,
b){var c=b.feature?b.feature.layer:b.layer,d=this.target.object;if(d instanceof OpenLayers.Map||d===c)return this.target.triggerEvent(a,b)},getFeatures:function(a){var b=a.clientX,c=a.clientY,d=[],e=[],f=[],g,h,k,l;for(l=this.map.layers.length-1;0<=l;--l)if(g=this.map.layers[l],"none"!==g.div.style.display)if(g.renderer instanceof OpenLayers.Renderer.Elements){if(g instanceof OpenLayers.Layer.Vector)for(h=document.elementFromPoint(b,c);h&&h._featureId;)(k=g.getFeatureById(h._featureId))?(d.push(k),
h.style.display="none",e.push(h),h=document.elementFromPoint(b,c)):h=!1;f.push(g);g.div.style.display="none"}else g.renderer instanceof OpenLayers.Renderer.Canvas&&(k=g.renderer.getFeatureIdFromEvent(a))&&(d.push(k),f.push(g));l=0;for(a=e.length;l<a;++l)e[l].style.display="";for(l=f.length-1;0<=l;--l)f[l].div.style.display="block";return d},destroy:function(){for(var a=this.provides.length-1;0<=a;--a)delete this.target.extensions[this.provides[a]];this.map.events.un({mousemove:this.onMousemove,mousedown:this.start,
mouseup:this.onClick,touchstart:this.start,touchmove:this.cancel,touchend:this.onClick,scope:this});delete this.cache;delete this.map;delete this.target}});OpenLayers.Events.nofeatureclick=OpenLayers.Events.featureclick;OpenLayers.Events.featureover=OpenLayers.Events.featureclick;OpenLayers.Events.featureout=OpenLayers.Events.featureclick;OpenLayers.Format.GPX=OpenLayers.Class(OpenLayers.Format.XML,{defaultDesc:"No description available",extractWaypoints:!0,extractTracks:!0,extractRoutes:!0,extractAttributes:!0,namespaces:{gpx:"http://www.topografix.com/GPX/1/1",xsi:"http://www.w3.org/2001/XMLSchema-instance"},schemaLocation:"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd",creator:"OpenLayers",initialize:function(a){this.externalProjection=new OpenLayers.Projection("EPSG:4326");OpenLayers.Format.XML.prototype.initialize.apply(this,
[a])},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));var b=[];if(this.extractTracks)for(var c=a.getElementsByTagName("trk"),d=0,e=c.length;d<e;d++){var f={};this.extractAttributes&&(f=this.parseAttributes(c[d]));for(var g=this.getElementsByTagNameNS(c[d],c[d].namespaceURI,"trkseg"),h=0,k=g.length;h<k;h++){var l=this.extractSegment(g[h],"trkpt");b.push(new OpenLayers.Feature.Vector(l,f))}}if(this.extractRoutes)for(e=a.getElementsByTagName("rte"),c=0,d=
e.length;c<d;c++)f={},this.extractAttributes&&(f=this.parseAttributes(e[c])),g=this.extractSegment(e[c],"rtept"),b.push(new OpenLayers.Feature.Vector(g,f));if(this.extractWaypoints)for(a=a.getElementsByTagName("wpt"),c=0,e=a.length;c<e;c++)f={},this.extractAttributes&&(f=this.parseAttributes(a[c])),d=new OpenLayers.Geometry.Point(a[c].getAttribute("lon"),a[c].getAttribute("lat")),b.push(new OpenLayers.Feature.Vector(d,f));if(this.internalProjection&&this.externalProjection)for(f=0,a=b.length;f<a;f++)b[f].geometry.transform(this.externalProjection,
this.internalProjection);return b},extractSegment:function(a,b){for(var c=this.getElementsByTagNameNS(a,a.namespaceURI,b),d=[],e=0,f=c.length;e<f;e++)d.push(new OpenLayers.Geometry.Point(c[e].getAttribute("lon"),c[e].getAttribute("lat")));return new OpenLayers.Geometry.LineString(d)},parseAttributes:function(a){var b={};a=a.firstChild;for(var c,d;a;)1==a.nodeType&&a.firstChild&&(c=a.firstChild,3==c.nodeType||4==c.nodeType)&&(d=a.prefix?a.nodeName.split(":")[1]:a.nodeName,"trkseg"!=d&&"rtept"!=d&&
(b[d]=c.nodeValue)),a=a.nextSibling;return b},write:function(a,b){a=OpenLayers.Util.isArray(a)?a:[a];var c=this.createElementNS(this.namespaces.gpx,"gpx");c.setAttribute("version","1.1");c.setAttribute("creator",this.creator);this.setAttributes(c,{"xsi:schemaLocation":this.schemaLocation});b&&"object"==typeof b&&c.appendChild(this.buildMetadataNode(b));for(var d=0,e=a.length;d<e;d++)c.appendChild(this.buildFeatureNode(a[d]));return OpenLayers.Format.XML.prototype.write.apply(this,[c])},buildMetadataNode:function(a){for(var b=
["name","desc","author"],c=this.createElementNS(this.namespaces.gpx,"metadata"),d=0;d<b.length;d++){var e=b[d];if(a[e]){var f=this.createElementNS(this.namespaces.gpx,e);f.appendChild(this.createTextNode(a[e]));c.appendChild(f)}}return c},buildFeatureNode:function(a){var b=a.geometry,b=b.clone();this.internalProjection&&this.externalProjection&&b.transform(this.internalProjection,this.externalProjection);if("OpenLayers.Geometry.Point"==b.CLASS_NAME){var c=this.buildWptNode(b);this.appendAttributesNode(c,
a);return c}c=this.createElementNS(this.namespaces.gpx,"trk");this.appendAttributesNode(c,a);a=this.buildTrkSegNode(b);a=OpenLayers.Util.isArray(a)?a:[a];for(var b=0,d=a.length;b<d;b++)c.appendChild(a[b]);return c},buildTrkSegNode:function(a){var b,c,d,e;if("OpenLayers.Geometry.LineString"==a.CLASS_NAME||"OpenLayers.Geometry.LinearRing"==a.CLASS_NAME){b=this.createElementNS(this.namespaces.gpx,"trkseg");c=0;for(d=a.components.length;c<d;c++)e=a.components[c],b.appendChild(this.buildTrkPtNode(e));
return b}b=[];c=0;for(d=a.components.length;c<d;c++)b.push(this.buildTrkSegNode(a.components[c]));return b},buildTrkPtNode:function(a){var b=this.createElementNS(this.namespaces.gpx,"trkpt");b.setAttribute("lon",a.x);b.setAttribute("lat",a.y);return b},buildWptNode:function(a){var b=this.createElementNS(this.namespaces.gpx,"wpt");b.setAttribute("lon",a.x);b.setAttribute("lat",a.y);return b},appendAttributesNode:function(a,b){var c=this.createElementNS(this.namespaces.gpx,"name");c.appendChild(this.createTextNode(b.attributes.name||
b.id));a.appendChild(c);c=this.createElementNS(this.namespaces.gpx,"desc");c.appendChild(this.createTextNode(b.attributes.description||this.defaultDesc));a.appendChild(c)},CLASS_NAME:"OpenLayers.Format.GPX"});OpenLayers.Format.WMSDescribeLayer=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.1.1",CLASS_NAME:"OpenLayers.Format.WMSDescribeLayer"});OpenLayers.Format.WMSDescribeLayer.v1_1_1=OpenLayers.Class(OpenLayers.Format.WMSDescribeLayer,{initialize:function(a){OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this,[a])},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));for(var b=a.documentElement.childNodes,c={layerDescriptions:[]},d,e,f=0;f<b.length;++f)if(d=b[f],e=d.nodeName,"LayerDescription"==e){e=d.getAttribute("name");var g="",h="",k="";d.getAttribute("owsType")?(g=d.getAttribute("owsType"),
h=d.getAttribute("owsURL")):""!=d.getAttribute("wfs")?(g="WFS",h=d.getAttribute("wfs")):""!=d.getAttribute("wcs")&&(g="WCS",h=d.getAttribute("wcs"));d=d.getElementsByTagName("Query");0<d.length&&((k=d[0].getAttribute("typeName"))||(k=d[0].getAttribute("typename")));d={layerName:e,owsType:g,owsURL:h,typeName:k};c.layerDescriptions.push(d);c.length=c.layerDescriptions.length;c[c.length-1]=d}else if("ServiceException"==e)return{error:(new OpenLayers.Format.OGCExceptionReport).read(a)};return c},CLASS_NAME:"OpenLayers.Format.WMSDescribeLayer.v1_1_1"});
OpenLayers.Format.WMSDescribeLayer.v1_1_0=OpenLayers.Format.WMSDescribeLayer.v1_1_1;OpenLayers.Layer.XYZ=OpenLayers.Class(OpenLayers.Layer.Grid,{isBaseLayer:!0,sphericalMercator:!1,zoomOffset:0,serverResolutions:null,initialize:function(a,b,c){if(c&&c.sphericalMercator||this.sphericalMercator)c=OpenLayers.Util.extend({projection:"EPSG:900913",numZoomLevels:19},c);OpenLayers.Layer.Grid.prototype.initialize.apply(this,[a||this.name,b||this.url,{},c])},clone:function(a){null==a&&(a=new OpenLayers.Layer.XYZ(this.name,this.url,this.getOptions()));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,
[a])},getURL:function(a){a=this.getXYZ(a);var b=this.url;OpenLayers.Util.isArray(b)&&(b=this.selectUrl(""+a.x+a.y+a.z,b));return OpenLayers.String.format(b,a)},getXYZ:function(a){var b=this.getServerResolution(),c=Math.round((a.left-this.maxExtent.left)/(b*this.tileSize.w));a=Math.round((this.maxExtent.top-a.top)/(b*this.tileSize.h));b=this.getServerZoom();if(this.wrapDateLine)var d=Math.pow(2,b),c=(c%d+d)%d;return{x:c,y:a,z:b}},setMap:function(a){OpenLayers.Layer.Grid.prototype.setMap.apply(this,
arguments);this.tileOrigin||(this.tileOrigin=new OpenLayers.LonLat(this.maxExtent.left,this.maxExtent.bottom))},CLASS_NAME:"OpenLayers.Layer.XYZ"});OpenLayers.Layer.OSM=OpenLayers.Class(OpenLayers.Layer.XYZ,{name:"OpenStreetMap",url:["http://a.tile.openstreetmap.org/${z}/${x}/${y}.png","http://b.tile.openstreetmap.org/${z}/${x}/${y}.png","http://c.tile.openstreetmap.org/${z}/${x}/${y}.png"],attribution:"&copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors",sphericalMercator:!0,wrapDateLine:!0,tileOptions:null,initialize:function(a,b,c){OpenLayers.Layer.XYZ.prototype.initialize.apply(this,arguments);this.tileOptions=
OpenLayers.Util.extend({crossOriginKeyword:"anonymous"},this.options&&this.options.tileOptions)},clone:function(a){null==a&&(a=new OpenLayers.Layer.OSM(this.name,this.url,this.getOptions()));return a=OpenLayers.Layer.XYZ.prototype.clone.apply(this,[a])},CLASS_NAME:"OpenLayers.Layer.OSM"});OpenLayers.Renderer=OpenLayers.Class({container:null,root:null,extent:null,locked:!1,size:null,resolution:null,map:null,featureDx:0,initialize:function(a,b){this.container=OpenLayers.Util.getElement(a);OpenLayers.Util.extend(this,b)},destroy:function(){this.map=this.resolution=this.size=this.extent=this.container=null},supported:function(){return!1},setExtent:function(a,b){this.extent=a.clone();if(this.map.baseLayer&&this.map.baseLayer.wrapDateLine){var c=a.getWidth()/this.map.getExtent().getWidth();
a=a.scale(1/c);this.extent=a.wrapDateLine(this.map.getMaxExtent()).scale(c)}b&&(this.resolution=null);return!0},setSize:function(a){this.size=a.clone();this.resolution=null},getResolution:function(){return this.resolution=this.resolution||this.map.getResolution()},drawFeature:function(a,b){null==b&&(b=a.style);if(a.geometry){var c=a.geometry.getBounds();if(c){var d;this.map.baseLayer&&this.map.baseLayer.wrapDateLine&&(d=this.map.getMaxExtent());c.intersectsBounds(this.extent,{worldBounds:d})?this.calculateFeatureDx(c,
d):b={display:"none"};c=this.drawGeometry(a.geometry,b,a.id);if("none"!=b.display&&b.label&&!1!==c){d=a.geometry.getCentroid();if(b.labelXOffset||b.labelYOffset){var e=isNaN(b.labelXOffset)?0:b.labelXOffset,f=isNaN(b.labelYOffset)?0:b.labelYOffset,g=this.getResolution();d.move(e*g,f*g)}this.drawText(a.id,b,d)}else this.removeText(a.id);return c}}},calculateFeatureDx:function(a,b){this.featureDx=0;if(b){var c=b.getWidth();this.featureDx=Math.round(((a.left+a.right)/2-(this.extent.left+this.extent.right)/
2)/c)*c}},drawGeometry:function(a,b,c){},drawText:function(a,b,c){},removeText:function(a){},clear:function(){},getFeatureIdFromEvent:function(a){},eraseFeatures:function(a){OpenLayers.Util.isArray(a)||(a=[a]);for(var b=0,c=a.length;b<c;++b){var d=a[b];this.eraseGeometry(d.geometry,d.id);this.removeText(d.id)}},eraseGeometry:function(a,b){},moveRoot:function(a){},getRenderLayerId:function(){return this.container.id},applyDefaultSymbolizer:function(a){var b=OpenLayers.Util.extend({},OpenLayers.Renderer.defaultSymbolizer);
!1===a.stroke&&(delete b.strokeWidth,delete b.strokeColor);!1===a.fill&&delete b.fillColor;OpenLayers.Util.extend(b,a);return b},CLASS_NAME:"OpenLayers.Renderer"});OpenLayers.Renderer.defaultSymbolizer={fillColor:"#000000",strokeColor:"#000000",strokeWidth:2,fillOpacity:1,strokeOpacity:1,pointRadius:0,labelAlign:"cm"};
OpenLayers.Renderer.symbol={star:[350,75,379,161,469,161,397,215,423,301,350,250,277,301,303,215,231,161,321,161,350,75],cross:[4,0,6,0,6,4,10,4,10,6,6,6,6,10,4,10,4,6,0,6,0,4,4,4,4,0],x:[0,0,25,0,50,35,75,0,100,0,65,50,100,100,75,100,50,65,25,100,0,100,35,50,0,0],square:[0,0,0,1,1,1,1,0,0,0],triangle:[0,10,10,10,5,0,0,10]};OpenLayers.Renderer.Canvas=OpenLayers.Class(OpenLayers.Renderer,{hitDetection:!0,hitOverflow:0,canvas:null,features:null,pendingRedraw:!1,cachedSymbolBounds:{},initialize:function(a,b){OpenLayers.Renderer.prototype.initialize.apply(this,arguments);this.root=document.createElement("canvas");this.container.appendChild(this.root);this.canvas=this.root.getContext("2d");this.features={};this.hitDetection&&(this.hitCanvas=document.createElement("canvas"),this.hitContext=this.hitCanvas.getContext("2d"))},
setExtent:function(){OpenLayers.Renderer.prototype.setExtent.apply(this,arguments);return!1},eraseGeometry:function(a,b){this.eraseFeatures(this.features[b][0])},supported:function(){return OpenLayers.CANVAS_SUPPORTED},setSize:function(a){this.size=a.clone();var b=this.root;b.style.width=a.w+"px";b.style.height=a.h+"px";b.width=a.w;b.height=a.h;this.resolution=null;this.hitDetection&&(b=this.hitCanvas,b.style.width=a.w+"px",b.style.height=a.h+"px",b.width=a.w,b.height=a.h)},drawFeature:function(a,
b){var c;if(a.geometry){b=this.applyDefaultSymbolizer(b||a.style);c=a.geometry.getBounds();var d;this.map.baseLayer&&this.map.baseLayer.wrapDateLine&&(d=this.map.getMaxExtent());d=c&&c.intersectsBounds(this.extent,{worldBounds:d});(c="none"!==b.display&&!!c&&d)?this.features[a.id]=[a,b]:delete this.features[a.id];this.pendingRedraw=!0}this.pendingRedraw&&!this.locked&&(this.redraw(),this.pendingRedraw=!1);return c},drawGeometry:function(a,b,c){var d=a.CLASS_NAME;if("OpenLayers.Geometry.Collection"==
d||"OpenLayers.Geometry.MultiPoint"==d||"OpenLayers.Geometry.MultiLineString"==d||"OpenLayers.Geometry.MultiPolygon"==d)for(d=0;d<a.components.length;d++)this.drawGeometry(a.components[d],b,c);else switch(a.CLASS_NAME){case "OpenLayers.Geometry.Point":this.drawPoint(a,b,c);break;case "OpenLayers.Geometry.LineString":this.drawLineString(a,b,c);break;case "OpenLayers.Geometry.LinearRing":this.drawLinearRing(a,b,c);break;case "OpenLayers.Geometry.Polygon":this.drawPolygon(a,b,c)}},drawExternalGraphic:function(a,
b,c){var d=new Image,e=b.title||b.graphicTitle;e&&(d.title=e);var f=b.graphicWidth||b.graphicHeight,g=b.graphicHeight||b.graphicWidth,f=f?f:2*b.pointRadius,g=g?g:2*b.pointRadius,h=void 0!=b.graphicXOffset?b.graphicXOffset:-(0.5*f),k=void 0!=b.graphicYOffset?b.graphicYOffset:-(0.5*g),l=b.graphicOpacity||b.fillOpacity;d.onload=OpenLayers.Function.bind(function(){if(this.features[c]){var b=this.getLocalXY(a),e=b[0],b=b[1];if(!isNaN(e)&&!isNaN(b)){var e=e+h|0,b=b+k|0,p=this.canvas;p.globalAlpha=l;var q=
OpenLayers.Renderer.Canvas.drawImageScaleFactor||(OpenLayers.Renderer.Canvas.drawImageScaleFactor=/android 2.1/.test(navigator.userAgent.toLowerCase())?320/window.screen.width:1);p.drawImage(d,e*q,b*q,f*q,g*q);this.hitDetection&&(this.setHitContextStyle("fill",c),this.hitContext.fillRect(e,b,f,g))}}},this);d.src=b.externalGraphic},drawNamedSymbol:function(a,b,c){var d,e,f,g;f=Math.PI/180;var h=OpenLayers.Renderer.symbol[b.graphicName];if(!h)throw Error(b.graphicName+" is not a valid symbol name");
if(!(!h.length||2>h.length||(a=this.getLocalXY(a),e=a[0],g=a[1],isNaN(e)||isNaN(g)))){this.canvas.lineCap="round";this.canvas.lineJoin="round";this.hitDetection&&(this.hitContext.lineCap="round",this.hitContext.lineJoin="round");if(b.graphicName in this.cachedSymbolBounds)d=this.cachedSymbolBounds[b.graphicName];else{d=new OpenLayers.Bounds;for(a=0;a<h.length;a+=2)d.extend(new OpenLayers.LonLat(h[a],h[a+1]));this.cachedSymbolBounds[b.graphicName]=d}this.canvas.save();this.hitDetection&&this.hitContext.save();
this.canvas.translate(e,g);this.hitDetection&&this.hitContext.translate(e,g);a=f*b.rotation;isNaN(a)||(this.canvas.rotate(a),this.hitDetection&&this.hitContext.rotate(a));f=2*b.pointRadius/Math.max(d.getWidth(),d.getHeight());this.canvas.scale(f,f);this.hitDetection&&this.hitContext.scale(f,f);a=d.getCenterLonLat().lon;d=d.getCenterLonLat().lat;this.canvas.translate(-a,-d);this.hitDetection&&this.hitContext.translate(-a,-d);g=b.strokeWidth;b.strokeWidth=g/f;if(!1!==b.fill){this.setCanvasStyle("fill",
b);this.canvas.beginPath();for(a=0;a<h.length;a+=2)d=h[a],e=h[a+1],0==a&&this.canvas.moveTo(d,e),this.canvas.lineTo(d,e);this.canvas.closePath();this.canvas.fill();if(this.hitDetection){this.setHitContextStyle("fill",c,b);this.hitContext.beginPath();for(a=0;a<h.length;a+=2)d=h[a],e=h[a+1],0==a&&this.canvas.moveTo(d,e),this.hitContext.lineTo(d,e);this.hitContext.closePath();this.hitContext.fill()}}if(!1!==b.stroke){this.setCanvasStyle("stroke",b);this.canvas.beginPath();for(a=0;a<h.length;a+=2)d=h[a],
e=h[a+1],0==a&&this.canvas.moveTo(d,e),this.canvas.lineTo(d,e);this.canvas.closePath();this.canvas.stroke();if(this.hitDetection){this.setHitContextStyle("stroke",c,b,f);this.hitContext.beginPath();for(a=0;a<h.length;a+=2)d=h[a],e=h[a+1],0==a&&this.hitContext.moveTo(d,e),this.hitContext.lineTo(d,e);this.hitContext.closePath();this.hitContext.stroke()}}b.strokeWidth=g;this.canvas.restore();this.hitDetection&&this.hitContext.restore();this.setCanvasStyle("reset")}},setCanvasStyle:function(a,b){"fill"===
a?(this.canvas.globalAlpha=b.fillOpacity,this.canvas.fillStyle=b.fillColor):"stroke"===a?(this.canvas.globalAlpha=b.strokeOpacity,this.canvas.strokeStyle=b.strokeColor,this.canvas.lineWidth=b.strokeWidth):(this.canvas.globalAlpha=0,this.canvas.lineWidth=1)},featureIdToHex:function(a){a=Number(a.split("_").pop())+1;16777216<=a&&(this.hitOverflow=a-16777215,a=a%16777216+1);a="000000"+a.toString(16);var b=a.length;return a="#"+a.substring(b-6,b)},setHitContextStyle:function(a,b,c,d){b=this.featureIdToHex(b);
"fill"==a?(this.hitContext.globalAlpha=1,this.hitContext.fillStyle=b):"stroke"==a?(this.hitContext.globalAlpha=1,this.hitContext.strokeStyle=b,"undefined"===typeof d?this.hitContext.lineWidth=c.strokeWidth+2:isNaN(d)||(this.hitContext.lineWidth=c.strokeWidth+2/d)):(this.hitContext.globalAlpha=0,this.hitContext.lineWidth=1)},drawPoint:function(a,b,c){if(!1!==b.graphic)if(b.externalGraphic)this.drawExternalGraphic(a,b,c);else if(b.graphicName&&"circle"!=b.graphicName)this.drawNamedSymbol(a,b,c);else{var d=
this.getLocalXY(a);a=d[0];d=d[1];if(!isNaN(a)&&!isNaN(d)){var e=2*Math.PI,f=b.pointRadius;!1!==b.fill&&(this.setCanvasStyle("fill",b),this.canvas.beginPath(),this.canvas.arc(a,d,f,0,e,!0),this.canvas.fill(),this.hitDetection&&(this.setHitContextStyle("fill",c,b),this.hitContext.beginPath(),this.hitContext.arc(a,d,f,0,e,!0),this.hitContext.fill()));!1!==b.stroke&&(this.setCanvasStyle("stroke",b),this.canvas.beginPath(),this.canvas.arc(a,d,f,0,e,!0),this.canvas.stroke(),this.hitDetection&&(this.setHitContextStyle("stroke",
c,b),this.hitContext.beginPath(),this.hitContext.arc(a,d,f,0,e,!0),this.hitContext.stroke()),this.setCanvasStyle("reset"))}}},drawLineString:function(a,b,c){b=OpenLayers.Util.applyDefaults({fill:!1},b);this.drawLinearRing(a,b,c)},drawLinearRing:function(a,b,c){!1!==b.fill&&(this.setCanvasStyle("fill",b),this.renderPath(this.canvas,a,b,c,"fill"),this.hitDetection&&(this.setHitContextStyle("fill",c,b),this.renderPath(this.hitContext,a,b,c,"fill")));!1!==b.stroke&&(this.setCanvasStyle("stroke",b),this.renderPath(this.canvas,
a,b,c,"stroke"),this.hitDetection&&(this.setHitContextStyle("stroke",c,b),this.renderPath(this.hitContext,a,b,c,"stroke")));this.setCanvasStyle("reset")},renderPath:function(a,b,c,d,e){b=b.components;c=b.length;a.beginPath();d=this.getLocalXY(b[0]);var f=d[1];if(!isNaN(d[0])&&!isNaN(f)){a.moveTo(d[0],d[1]);for(d=1;d<c;++d)f=this.getLocalXY(b[d]),a.lineTo(f[0],f[1]);"fill"===e?a.fill():a.stroke()}},drawPolygon:function(a,b,c){a=a.components;var d=a.length;this.drawLinearRing(a[0],b,c);for(var e=1;e<
d;++e)this.canvas.globalCompositeOperation="destination-out",this.hitDetection&&(this.hitContext.globalCompositeOperation="destination-out"),this.drawLinearRing(a[e],OpenLayers.Util.applyDefaults({stroke:!1,fillOpacity:1},b),c),this.canvas.globalCompositeOperation="source-over",this.hitDetection&&(this.hitContext.globalCompositeOperation="source-over"),this.drawLinearRing(a[e],OpenLayers.Util.applyDefaults({fill:!1},b),c)},drawText:function(a,b){var c=this.getLocalXY(a);this.setCanvasStyle("reset");
this.canvas.fillStyle=b.fontColor;this.canvas.globalAlpha=b.fontOpacity||1;var d=[b.fontStyle?b.fontStyle:"normal","normal",b.fontWeight?b.fontWeight:"normal",b.fontSize?b.fontSize:"1em",b.fontFamily?b.fontFamily:"sans-serif"].join(" "),e=b.label.split("\n"),f=e.length;if(this.canvas.fillText){this.canvas.font=d;this.canvas.textAlign=OpenLayers.Renderer.Canvas.LABEL_ALIGN[b.labelAlign[0]]||"center";this.canvas.textBaseline=OpenLayers.Renderer.Canvas.LABEL_ALIGN[b.labelAlign[1]]||"middle";var g=OpenLayers.Renderer.Canvas.LABEL_FACTOR[b.labelAlign[1]];
null==g&&(g=-0.5);d=this.canvas.measureText("Mg").height||this.canvas.measureText("xx").width;c[1]+=d*g*(f-1);for(g=0;g<f;g++)b.labelOutlineWidth&&(this.canvas.save(),this.canvas.globalAlpha=b.labelOutlineOpacity||b.fontOpacity||1,this.canvas.strokeStyle=b.labelOutlineColor,this.canvas.lineWidth=b.labelOutlineWidth,this.canvas.strokeText(e[g],c[0],c[1]+d*g+1),this.canvas.restore()),this.canvas.fillText(e[g],c[0],c[1]+d*g)}else if(this.canvas.mozDrawText){this.canvas.mozTextStyle=d;var h=OpenLayers.Renderer.Canvas.LABEL_FACTOR[b.labelAlign[0]];
null==h&&(h=-0.5);g=OpenLayers.Renderer.Canvas.LABEL_FACTOR[b.labelAlign[1]];null==g&&(g=-0.5);d=this.canvas.mozMeasureText("xx");c[1]+=d*(1+g*f);for(g=0;g<f;g++){var k=c[0]+h*this.canvas.mozMeasureText(e[g]),l=c[1]+g*d;this.canvas.translate(k,l);this.canvas.mozDrawText(e[g]);this.canvas.translate(-k,-l)}}this.setCanvasStyle("reset")},getLocalXY:function(a){var b=this.getResolution(),c=this.extent;return[(a.x-this.featureDx)/b+-c.left/b,c.top/b-a.y/b]},clear:function(){var a=this.root.height,b=this.root.width;
this.canvas.clearRect(0,0,b,a);this.features={};this.hitDetection&&this.hitContext.clearRect(0,0,b,a)},getFeatureIdFromEvent:function(a){var b;if(this.hitDetection&&"none"!==this.root.style.display&&!this.map.dragging&&(a=a.xy,a=this.hitContext.getImageData(a.x|0,a.y|0,1,1).data,255===a[3]&&(a=a[2]+256*(a[1]+256*a[0])))){a="OpenLayers_Feature_Vector_"+(a-1+this.hitOverflow);try{b=this.features[a][0]}catch(c){}}return b},eraseFeatures:function(a){OpenLayers.Util.isArray(a)||(a=[a]);for(var b=0;b<a.length;++b)delete this.features[a[b].id];
this.redraw()},redraw:function(){if(!this.locked){var a=this.root.height,b=this.root.width;this.canvas.clearRect(0,0,b,a);this.hitDetection&&this.hitContext.clearRect(0,0,b,a);var a=[],c,d,e=this.map.baseLayer&&this.map.baseLayer.wrapDateLine&&this.map.getMaxExtent(),f;for(f in this.features)this.features.hasOwnProperty(f)&&(b=this.features[f][0],c=b.geometry,this.calculateFeatureDx(c.getBounds(),e),d=this.features[f][1],this.drawGeometry(c,d,b.id),d.label&&a.push([b,d]));b=0;for(c=a.length;b<c;++b)f=
a[b],this.drawText(f[0].geometry.getCentroid(),f[1])}},CLASS_NAME:"OpenLayers.Renderer.Canvas"});OpenLayers.Renderer.Canvas.LABEL_ALIGN={l:"left",r:"right",t:"top",b:"bottom"};OpenLayers.Renderer.Canvas.LABEL_FACTOR={l:0,r:-1,t:0,b:-1};OpenLayers.Renderer.Canvas.drawImageScaleFactor=null;OpenLayers.Format.OSM=OpenLayers.Class(OpenLayers.Format.XML,{checkTags:!1,interestingTagsExclude:null,areaTags:null,initialize:function(a){var b={interestingTagsExclude:"source source_ref source:ref history attribution created_by".split(" "),areaTags:"area building leisure tourism ruins historic landuse military natural sport".split(" ")},b=OpenLayers.Util.extend(b,a),c={};for(a=0;a<b.interestingTagsExclude.length;a++)c[b.interestingTagsExclude[a]]=!0;b.interestingTagsExclude=c;c={};for(a=0;a<b.areaTags.length;a++)c[b.areaTags[a]]=
!0;b.areaTags=c;this.externalProjection=new OpenLayers.Projection("EPSG:4326");OpenLayers.Format.XML.prototype.initialize.apply(this,[b])},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));var b=this.getNodes(a),c=this.getWays(a);a=Array(c.length);for(var d=0;d<c.length;d++){for(var e=Array(c[d].nodes.length),f=this.isWayArea(c[d])?1:0,g=0;g<c[d].nodes.length;g++){var h=b[c[d].nodes[g]],k=new OpenLayers.Geometry.Point(h.lon,h.lat);k.osm_id=parseInt(c[d].nodes[g]);
e[g]=k;h.used=!0}h=null;h=f?new OpenLayers.Geometry.Polygon(new OpenLayers.Geometry.LinearRing(e)):new OpenLayers.Geometry.LineString(e);this.internalProjection&&this.externalProjection&&h.transform(this.externalProjection,this.internalProjection);e=new OpenLayers.Feature.Vector(h,c[d].tags);e.osm_id=parseInt(c[d].id);e.fid="way."+e.osm_id;a[d]=e}for(var l in b){h=b[l];if(!h.used||this.checkTags){c=null;if(this.checkTags){c=this.getTags(h.node,!0);if(h.used&&!c[1])continue;c=c[0]}else c=this.getTags(h.node);
e=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(h.lon,h.lat),c);this.internalProjection&&this.externalProjection&&e.geometry.transform(this.externalProjection,this.internalProjection);e.osm_id=parseInt(l);e.fid="node."+e.osm_id;a.push(e)}h.node=null}return a},getNodes:function(a){a=a.getElementsByTagName("node");for(var b={},c=0;c<a.length;c++){var d=a[c],e=d.getAttribute("id");b[e]={lat:d.getAttribute("lat"),lon:d.getAttribute("lon"),node:d}}return b},getWays:function(a){a=a.getElementsByTagName("way");
for(var b=[],c=0;c<a.length;c++){var d=a[c],e={id:d.getAttribute("id")};e.tags=this.getTags(d);d=d.getElementsByTagName("nd");e.nodes=Array(d.length);for(var f=0;f<d.length;f++)e.nodes[f]=d[f].getAttribute("ref");b.push(e)}return b},getTags:function(a,b){for(var c=a.getElementsByTagName("tag"),d={},e=!1,f=0;f<c.length;f++){var g=c[f].getAttribute("k");d[g]=c[f].getAttribute("v");b&&(this.interestingTagsExclude[g]||(e=!0))}return b?[d,e]:d},isWayArea:function(a){var b=!1,c=!1;a.nodes[0]==a.nodes[a.nodes.length-
1]&&(b=!0);if(this.checkTags)for(var d in a.tags)if(this.areaTags[d]){c=!0;break}return b&&(this.checkTags?c:!0)},write:function(a){OpenLayers.Util.isArray(a)||(a=[a]);this.osm_id=1;this.created_nodes={};var b=this.createElementNS(null,"osm");b.setAttribute("version","0.5");b.setAttribute("generator","OpenLayers "+OpenLayers.VERSION_NUMBER);for(var c=a.length-1;0<=c;c--)for(var d=this.createFeatureNodes(a[c]),e=0;e<d.length;e++)b.appendChild(d[e]);return OpenLayers.Format.XML.prototype.write.apply(this,
[b])},createFeatureNodes:function(a){var b=[],c=a.geometry.CLASS_NAME,c=c.substring(c.lastIndexOf(".")+1),c=c.toLowerCase();(c=this.createXML[c])&&(b=c.apply(this,[a]));return b},createXML:{point:function(a){var b=null,c=a.geometry?a.geometry:a;this.internalProjection&&this.externalProjection&&(c=c.clone(),c.transform(this.internalProjection,this.externalProjection));var d=!1;a.osm_id?(b=a.osm_id,this.created_nodes[b]&&(d=!0)):(b=-this.osm_id,this.osm_id++);var e=d?this.created_nodes[b]:this.createElementNS(null,
"node");this.created_nodes[b]=e;e.setAttribute("id",b);e.setAttribute("lon",c.x);e.setAttribute("lat",c.y);a.attributes&&this.serializeTags(a,e);this.setState(a,e);return d?[]:[e]},linestring:function(a){var b,c=[],d=a.geometry;a.osm_id?b=a.osm_id:(b=-this.osm_id,this.osm_id++);var e=this.createElementNS(null,"way");e.setAttribute("id",b);for(b=0;b<d.components.length;b++){var f=this.createXML.point.apply(this,[d.components[b]]);if(f.length){var f=f[0],g=f.getAttribute("id");c.push(f)}else g=d.components[b].osm_id,
f=this.created_nodes[g];this.setState(a,f);f=this.createElementNS(null,"nd");f.setAttribute("ref",g);e.appendChild(f)}this.serializeTags(a,e);c.push(e);return c},polygon:function(a){var b=OpenLayers.Util.extend({area:"yes"},a.attributes),b=new OpenLayers.Feature.Vector(a.geometry.components[0],b);b.osm_id=a.osm_id;return this.createXML.linestring.apply(this,[b])}},serializeTags:function(a,b){for(var c in a.attributes){var d=this.createElementNS(null,"tag");d.setAttribute("k",c);d.setAttribute("v",
a.attributes[c]);b.appendChild(d)}},setState:function(a,b){if(a.state){var c=null;switch(a.state){case OpenLayers.State.UPDATE:case OpenLayers.State.DELETE:c="delete"}c&&b.setAttribute("action",c)}},CLASS_NAME:"OpenLayers.Format.OSM"});OpenLayers.Handler.Keyboard=OpenLayers.Class(OpenLayers.Handler,{KEY_EVENTS:["keydown","keyup"],eventListener:null,observeElement:null,initialize:function(a,b,c){OpenLayers.Handler.prototype.initialize.apply(this,arguments);this.eventListener=OpenLayers.Function.bindAsEventListener(this.handleKeyEvent,this)},destroy:function(){this.deactivate();this.eventListener=null;OpenLayers.Handler.prototype.destroy.apply(this,arguments)},activate:function(){if(OpenLayers.Handler.prototype.activate.apply(this,
arguments)){this.observeElement=this.observeElement||document;for(var a=0,b=this.KEY_EVENTS.length;a<b;a++)OpenLayers.Event.observe(this.observeElement,this.KEY_EVENTS[a],this.eventListener);return!0}return!1},deactivate:function(){var a=!1;if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){for(var a=0,b=this.KEY_EVENTS.length;a<b;a++)OpenLayers.Event.stopObserving(this.observeElement,this.KEY_EVENTS[a],this.eventListener);a=!0}return a},handleKeyEvent:function(a){this.checkModifiers(a)&&
this.callback(a.type,[a])},CLASS_NAME:"OpenLayers.Handler.Keyboard"});OpenLayers.Control.ModifyFeature=OpenLayers.Class(OpenLayers.Control,{documentDrag:!1,geometryTypes:null,clickout:!0,toggle:!0,standalone:!1,layer:null,feature:null,vertex:null,vertices:null,virtualVertices:null,handlers:null,deleteCodes:null,virtualStyle:null,vertexRenderIntent:null,mode:null,createVertices:!0,modified:!1,radiusHandle:null,dragHandle:null,onModificationStart:function(){},onModification:function(){},onModificationEnd:function(){},initialize:function(a,b){b=b||{};this.layer=a;this.vertices=
[];this.virtualVertices=[];this.virtualStyle=OpenLayers.Util.extend({},this.layer.style||this.layer.styleMap.createSymbolizer(null,b.vertexRenderIntent));this.virtualStyle.fillOpacity=0.3;this.virtualStyle.strokeOpacity=0.3;this.deleteCodes=[46,68];this.mode=OpenLayers.Control.ModifyFeature.RESHAPE;OpenLayers.Control.prototype.initialize.apply(this,[b]);OpenLayers.Util.isArray(this.deleteCodes)||(this.deleteCodes=[this.deleteCodes]);var c={documentDrag:this.documentDrag,stopDown:!1};this.handlers=
{keyboard:new OpenLayers.Handler.Keyboard(this,{keydown:this.handleKeypress}),drag:new OpenLayers.Handler.Drag(this,{down:function(a){this.vertex=null;(a=this.layer.getFeatureFromEvent(this.handlers.drag.evt))?this.dragStart(a):this.clickout&&(this._unselect=this.feature)},move:function(a){delete this._unselect;this.vertex&&this.dragVertex(this.vertex,a)},up:function(){this.handlers.drag.stopDown=!1;this._unselect&&(this.unselectFeature(this._unselect),delete this._unselect)},done:function(a){this.vertex&&
this.dragComplete(this.vertex)}},c)}},destroy:function(){this.map&&this.map.events.un({removelayer:this.handleMapEvents,changelayer:this.handleMapEvents,scope:this});this.layer=null;OpenLayers.Control.prototype.destroy.apply(this,[])},activate:function(){this.moveLayerToTop();this.map.events.on({removelayer:this.handleMapEvents,changelayer:this.handleMapEvents,scope:this});return this.handlers.keyboard.activate()&&this.handlers.drag.activate()&&OpenLayers.Control.prototype.activate.apply(this,arguments)},
deactivate:function(){var a=!1;OpenLayers.Control.prototype.deactivate.apply(this,arguments)&&(this.moveLayerBack(),this.map.events.un({removelayer:this.handleMapEvents,changelayer:this.handleMapEvents,scope:this}),this.layer.removeFeatures(this.vertices,{silent:!0}),this.layer.removeFeatures(this.virtualVertices,{silent:!0}),this.vertices=[],this.handlers.drag.deactivate(),this.handlers.keyboard.deactivate(),(a=this.feature)&&(a.geometry&&a.layer)&&this.unselectFeature(a),a=!0);return a},beforeSelectFeature:function(a){return this.layer.events.triggerEvent("beforefeaturemodified",
{feature:a})},selectFeature:function(a){if(!(this.feature===a||this.geometryTypes&&-1==OpenLayers.Util.indexOf(this.geometryTypes,a.geometry.CLASS_NAME))){!1!==this.beforeSelectFeature(a)&&(this.feature&&this.unselectFeature(this.feature),this.feature=a,this.layer.selectedFeatures.push(a),this.layer.drawFeature(a,"select"),this.modified=!1,this.resetVertices(),this.onModificationStart(this.feature));var b=a.modified;!a.geometry||b&&b.geometry||(this._originalGeometry=a.geometry.clone())}},unselectFeature:function(a){this.layer.removeFeatures(this.vertices,
{silent:!0});this.vertices=[];this.layer.destroyFeatures(this.virtualVertices,{silent:!0});this.virtualVertices=[];this.dragHandle&&(this.layer.destroyFeatures([this.dragHandle],{silent:!0}),delete this.dragHandle);this.radiusHandle&&(this.layer.destroyFeatures([this.radiusHandle],{silent:!0}),delete this.radiusHandle);this.layer.drawFeature(this.feature,"default");this.feature=null;OpenLayers.Util.removeItem(this.layer.selectedFeatures,a);this.onModificationEnd(a);this.layer.events.triggerEvent("afterfeaturemodified",
{feature:a,modified:this.modified});this.modified=!1},dragStart:function(a){var b="OpenLayers.Geometry.Point"==a.geometry.CLASS_NAME;this.standalone||(a._sketch||!b)&&a._sketch||(this.toggle&&this.feature===a&&(this._unselect=a),this.selectFeature(a));if(a._sketch||b)this.vertex=a,this.handlers.drag.stopDown=!0},dragVertex:function(a,b){var c=this.map.getLonLatFromViewPortPx(b),d=a.geometry;d.move(c.lon-d.x,c.lat-d.y);this.modified=!0;"OpenLayers.Geometry.Point"==this.feature.geometry.CLASS_NAME?
this.layer.events.triggerEvent("vertexmodified",{vertex:a.geometry,feature:this.feature,pixel:b}):(a._index?(a.geometry.parent.addComponent(a.geometry,a._index),delete a._index,OpenLayers.Util.removeItem(this.virtualVertices,a),this.vertices.push(a)):a==this.dragHandle?(this.layer.removeFeatures(this.vertices,{silent:!0}),this.vertices=[],this.radiusHandle&&(this.layer.destroyFeatures([this.radiusHandle],{silent:!0}),this.radiusHandle=null)):a!==this.radiusHandle&&this.layer.events.triggerEvent("vertexmodified",
{vertex:a.geometry,feature:this.feature,pixel:b}),0<this.virtualVertices.length&&(this.layer.destroyFeatures(this.virtualVertices,{silent:!0}),this.virtualVertices=[]),this.layer.drawFeature(this.feature,this.standalone?void 0:"select"));this.layer.drawFeature(a)},dragComplete:function(a){this.resetVertices();this.setFeatureState();this.onModification(this.feature);this.layer.events.triggerEvent("featuremodified",{feature:this.feature})},setFeatureState:function(){if(this.feature.state!=OpenLayers.State.INSERT&&
this.feature.state!=OpenLayers.State.DELETE&&(this.feature.state=OpenLayers.State.UPDATE,this.modified&&this._originalGeometry)){var a=this.feature;a.modified=OpenLayers.Util.extend(a.modified,{geometry:this._originalGeometry});delete this._originalGeometry}},resetVertices:function(){0<this.vertices.length&&(this.layer.removeFeatures(this.vertices,{silent:!0}),this.vertices=[]);0<this.virtualVertices.length&&(this.layer.removeFeatures(this.virtualVertices,{silent:!0}),this.virtualVertices=[]);this.dragHandle&&
(this.layer.destroyFeatures([this.dragHandle],{silent:!0}),this.dragHandle=null);this.radiusHandle&&(this.layer.destroyFeatures([this.radiusHandle],{silent:!0}),this.radiusHandle=null);this.feature&&"OpenLayers.Geometry.Point"!=this.feature.geometry.CLASS_NAME&&(this.mode&OpenLayers.Control.ModifyFeature.DRAG&&this.collectDragHandle(),this.mode&(OpenLayers.Control.ModifyFeature.ROTATE|OpenLayers.Control.ModifyFeature.RESIZE)&&this.collectRadiusHandle(),this.mode&OpenLayers.Control.ModifyFeature.RESHAPE&&
(this.mode&OpenLayers.Control.ModifyFeature.RESIZE||this.collectVertices()))},handleKeypress:function(a){var b=a.keyCode;this.feature&&-1!=OpenLayers.Util.indexOf(this.deleteCodes,b)&&(b=this.layer.getFeatureFromEvent(this.handlers.drag.evt))&&(-1!=OpenLayers.Util.indexOf(this.vertices,b)&&!this.handlers.drag.dragging&&b.geometry.parent)&&(b.geometry.parent.removeComponent(b.geometry),this.layer.events.triggerEvent("vertexremoved",{vertex:b.geometry,feature:this.feature,pixel:a.xy}),this.layer.drawFeature(this.feature,
this.standalone?void 0:"select"),this.modified=!0,this.resetVertices(),this.setFeatureState(),this.onModification(this.feature),this.layer.events.triggerEvent("featuremodified",{feature:this.feature}))},collectVertices:function(){function a(c){var d,e,f;if("OpenLayers.Geometry.Point"==c.CLASS_NAME)e=new OpenLayers.Feature.Vector(c),e._sketch=!0,e.renderIntent=b.vertexRenderIntent,b.vertices.push(e);else{f=c.components.length;"OpenLayers.Geometry.LinearRing"==c.CLASS_NAME&&(f-=1);for(d=0;d<f;++d)e=
c.components[d],"OpenLayers.Geometry.Point"==e.CLASS_NAME?(e=new OpenLayers.Feature.Vector(e),e._sketch=!0,e.renderIntent=b.vertexRenderIntent,b.vertices.push(e)):a(e);if(b.createVertices&&"OpenLayers.Geometry.MultiPoint"!=c.CLASS_NAME)for(d=0,f=c.components.length;d<f-1;++d){e=c.components[d];var g=c.components[d+1];"OpenLayers.Geometry.Point"==e.CLASS_NAME&&"OpenLayers.Geometry.Point"==g.CLASS_NAME&&(e=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point((e.x+g.x)/2,(e.y+g.y)/2),null,b.virtualStyle),
e.geometry.parent=c,e._index=d+1,e._sketch=!0,b.virtualVertices.push(e))}}}this.vertices=[];this.virtualVertices=[];var b=this;a.call(this,this.feature.geometry);this.layer.addFeatures(this.virtualVertices,{silent:!0});this.layer.addFeatures(this.vertices,{silent:!0})},collectDragHandle:function(){var a=this.feature.geometry,b=a.getBounds().getCenterLonLat(),b=new OpenLayers.Geometry.Point(b.lon,b.lat),c=new OpenLayers.Feature.Vector(b);b.move=function(b,c){OpenLayers.Geometry.Point.prototype.move.call(this,
b,c);a.move(b,c)};c._sketch=!0;this.dragHandle=c;this.dragHandle.renderIntent=this.vertexRenderIntent;this.layer.addFeatures([this.dragHandle],{silent:!0})},collectRadiusHandle:function(){var a=this.feature.geometry,b=a.getBounds(),c=b.getCenterLonLat(),d=new OpenLayers.Geometry.Point(c.lon,c.lat),b=new OpenLayers.Geometry.Point(b.right,b.bottom),c=new OpenLayers.Feature.Vector(b),e=this.mode&OpenLayers.Control.ModifyFeature.RESIZE,f=this.mode&OpenLayers.Control.ModifyFeature.RESHAPE,g=this.mode&
OpenLayers.Control.ModifyFeature.ROTATE;b.move=function(b,c){OpenLayers.Geometry.Point.prototype.move.call(this,b,c);var l=this.x-d.x,m=this.y-d.y,n=l-b,p=m-c;if(g){var q=Math.atan2(p,n),q=Math.atan2(m,l)-q,q=q*(180/Math.PI);a.rotate(q,d)}if(e){var r;f?(m/=p,r=l/n/m):(n=Math.sqrt(n*n+p*p),m=Math.sqrt(l*l+m*m)/n);a.resize(m,d,r)}};c._sketch=!0;this.radiusHandle=c;this.radiusHandle.renderIntent=this.vertexRenderIntent;this.layer.addFeatures([this.radiusHandle],{silent:!0})},setMap:function(a){this.handlers.drag.setMap(a);
OpenLayers.Control.prototype.setMap.apply(this,arguments)},handleMapEvents:function(a){"removelayer"!=a.type&&"order"!=a.property||this.moveLayerToTop()},moveLayerToTop:function(){var a=Math.max(this.map.Z_INDEX_BASE.Feature-1,this.layer.getZIndex())+1;this.layer.setZIndex(a)},moveLayerBack:function(){var a=this.layer.getZIndex()-1;a>=this.map.Z_INDEX_BASE.Feature?this.layer.setZIndex(a):this.map.setLayerZIndex(this.layer,this.map.getLayerIndex(this.layer))},CLASS_NAME:"OpenLayers.Control.ModifyFeature"});
OpenLayers.Control.ModifyFeature.RESHAPE=1;OpenLayers.Control.ModifyFeature.RESIZE=2;OpenLayers.Control.ModifyFeature.ROTATE=4;OpenLayers.Control.ModifyFeature.DRAG=8;OpenLayers.Layer.Bing=OpenLayers.Class(OpenLayers.Layer.XYZ,{key:null,serverResolutions:[156543.03390625,78271.516953125,39135.7584765625,19567.87923828125,9783.939619140625,4891.9698095703125,2445.9849047851562,1222.9924523925781,611.4962261962891,305.74811309814453,152.87405654907226,76.43702827453613,38.218514137268066,19.109257068634033,9.554628534317017,4.777314267158508,2.388657133579254,1.194328566789627,0.5971642833948135,0.29858214169740677,0.14929107084870338,0.07464553542435169],attributionTemplate:'<span class="olBingAttribution ${type}"><div><a target="_blank" href="http://www.bing.com/maps/"><img src="${logo}" /></a></div>${copyrights}<a style="white-space: nowrap" target="_blank" href="http://www.microsoft.com/maps/product/terms.html">Terms of Use</a></span>',
metadata:null,protocolRegex:/^http:/i,type:"Road",culture:"en-US",metadataParams:null,tileOptions:null,protocol:~window.location.href.indexOf("http")?"":"http:",initialize:function(a){a=OpenLayers.Util.applyDefaults({sphericalMercator:!0},a);OpenLayers.Layer.XYZ.prototype.initialize.apply(this,[a.name||"Bing "+(a.type||this.type),null,a]);this.tileOptions=OpenLayers.Util.extend({crossOriginKeyword:"anonymous"},this.options.tileOptions);this.loadMetadata()},loadMetadata:function(){this._callbackId=
"_callback_"+this.id.replace(/\./g,"_");window[this._callbackId]=OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata,this);var a=OpenLayers.Util.applyDefaults({key:this.key,jsonp:this._callbackId,include:"ImageryProviders"},this.metadataParams),a=this.protocol+"//dev.virtualearth.net/REST/v1/Imagery/Metadata/"+this.type+"?"+OpenLayers.Util.getParameterString(a),b=document.createElement("script");b.type="text/javascript";b.src=a;b.id=this._callbackId;document.getElementsByTagName("head")[0].appendChild(b)},
initLayer:function(){var a=this.metadata.resourceSets[0].resources[0],b=a.imageUrl.replace("{quadkey}","${quadkey}"),b=b.replace("{culture}",this.culture),b=b.replace(this.protocolRegex,this.protocol);this.url=[];for(var c=0;c<a.imageUrlSubdomains.length;++c)this.url.push(b.replace("{subdomain}",a.imageUrlSubdomains[c]));this.addOptions({maxResolution:Math.min(this.serverResolutions[a.zoomMin],this.maxResolution||Number.POSITIVE_INFINITY),numZoomLevels:Math.min(a.zoomMax+1-a.zoomMin,this.numZoomLevels)},
!0);this.isBaseLayer||this.redraw();this.updateAttribution()},getURL:function(a){if(this.url){var b=this.getXYZ(a);a=b.x;for(var c=b.y,b=b.z,d=[],e=b;0<e;--e){var f="0",g=1<<e-1;0!=(a&g)&&f++;0!=(c&g)&&(f++,f++);d.push(f)}d=d.join("");a=this.selectUrl(""+a+c+b,this.url);return OpenLayers.String.format(a,{quadkey:d})}},updateAttribution:function(){var a=this.metadata;if(a.resourceSets&&this.map&&this.map.center){var b=a.resourceSets[0].resources[0],c=this.map.getExtent().transform(this.map.getProjectionObject(),
new OpenLayers.Projection("EPSG:4326")),d=b.imageryProviders||[],e=OpenLayers.Util.indexOf(this.serverResolutions,this.getServerResolution()),b="",f,g,h,k,l,m,n;g=0;for(h=d.length;g<h;++g)for(f=d[g],k=0,l=f.coverageAreas.length;k<l;++k)n=f.coverageAreas[k],m=OpenLayers.Bounds.fromArray(n.bbox,!0),c.intersectsBounds(m)&&(e<=n.zoomMax&&e>=n.zoomMin)&&(b+=f.attribution+" ");a=a.brandLogoUri.replace(this.protocolRegex,this.protocol);this.attribution=OpenLayers.String.format(this.attributionTemplate,{type:this.type.toLowerCase(),
logo:a,copyrights:b});this.map&&this.map.events.triggerEvent("changelayer",{layer:this,property:"attribution"})}},setMap:function(){OpenLayers.Layer.XYZ.prototype.setMap.apply(this,arguments);this.map.events.register("moveend",this,this.updateAttribution)},clone:function(a){null==a&&(a=new OpenLayers.Layer.Bing(this.options));return a=OpenLayers.Layer.XYZ.prototype.clone.apply(this,[a])},destroy:function(){this.map&&this.map.events.unregister("moveend",this,this.updateAttribution);OpenLayers.Layer.XYZ.prototype.destroy.apply(this,
arguments)},CLASS_NAME:"OpenLayers.Layer.Bing"});OpenLayers.Layer.Bing.processMetadata=function(a){this.metadata=a;this.initLayer();a=document.getElementById(this._callbackId);a.parentNode.removeChild(a);window[this._callbackId]=void 0;delete this._callbackId};OpenLayers.StyleMap=OpenLayers.Class({styles:null,extendDefault:!0,initialize:function(a,b){this.styles={"default":new OpenLayers.Style(OpenLayers.Feature.Vector.style["default"]),select:new OpenLayers.Style(OpenLayers.Feature.Vector.style.select),temporary:new OpenLayers.Style(OpenLayers.Feature.Vector.style.temporary),"delete":new OpenLayers.Style(OpenLayers.Feature.Vector.style["delete"])};if(a instanceof OpenLayers.Style)this.styles["default"]=a,this.styles.select=a,this.styles.temporary=a,this.styles["delete"]=
a;else if("object"==typeof a)for(var c in a)if(a[c]instanceof OpenLayers.Style)this.styles[c]=a[c];else if("object"==typeof a[c])this.styles[c]=new OpenLayers.Style(a[c]);else{this.styles["default"]=new OpenLayers.Style(a);this.styles.select=new OpenLayers.Style(a);this.styles.temporary=new OpenLayers.Style(a);this.styles["delete"]=new OpenLayers.Style(a);break}OpenLayers.Util.extend(this,b)},destroy:function(){for(var a in this.styles)this.styles[a].destroy();this.styles=null},createSymbolizer:function(a,
b){a||(a=new OpenLayers.Feature.Vector);this.styles[b]||(b="default");a.renderIntent=b;var c={};this.extendDefault&&"default"!=b&&(c=this.styles["default"].createSymbolizer(a));return OpenLayers.Util.extend(c,this.styles[b].createSymbolizer(a))},addUniqueValueRules:function(a,b,c,d){var e=[],f;for(f in c)e.push(new OpenLayers.Rule({symbolizer:c[f],context:d,filter:new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.EQUAL_TO,property:b,value:f})}));this.styles[a].addRules(e)},CLASS_NAME:"OpenLayers.StyleMap"});OpenLayers.Layer.Vector=OpenLayers.Class(OpenLayers.Layer,{isBaseLayer:!1,isFixed:!1,features:null,filter:null,selectedFeatures:null,unrenderedFeatures:null,reportError:!0,style:null,styleMap:null,strategies:null,protocol:null,renderers:["SVG","VML","Canvas"],renderer:null,rendererOptions:null,geometryType:null,drawn:!1,ratio:1,initialize:function(a,b){OpenLayers.Layer.prototype.initialize.apply(this,arguments);this.renderer&&this.renderer.supported()||this.assignRenderer();this.renderer&&this.renderer.supported()||
(this.renderer=null,this.displayError());this.styleMap||(this.styleMap=new OpenLayers.StyleMap);this.features=[];this.selectedFeatures=[];this.unrenderedFeatures={};if(this.strategies)for(var c=0,d=this.strategies.length;c<d;c++)this.strategies[c].setLayer(this)},destroy:function(){if(this.strategies){var a,b,c;b=0;for(c=this.strategies.length;b<c;b++)a=this.strategies[b],a.autoDestroy&&a.destroy();this.strategies=null}this.protocol&&(this.protocol.autoDestroy&&this.protocol.destroy(),this.protocol=
null);this.destroyFeatures();this.unrenderedFeatures=this.selectedFeatures=this.features=null;this.renderer&&this.renderer.destroy();this.drawn=this.geometryType=this.renderer=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments)},clone:function(a){null==a&&(a=new OpenLayers.Layer.Vector(this.name,this.getOptions()));a=OpenLayers.Layer.prototype.clone.apply(this,[a]);for(var b=this.features,c=b.length,d=Array(c),e=0;e<c;++e)d[e]=b[e].clone();a.features=d;return a},refresh:function(a){this.calculateInRange()&&
this.visibility&&this.events.triggerEvent("refresh",a)},assignRenderer:function(){for(var a=0,b=this.renderers.length;a<b;a++){var c=this.renderers[a];if((c="function"==typeof c?c:OpenLayers.Renderer[c])&&c.prototype.supported()){this.renderer=new c(this.div,this.rendererOptions);break}}},displayError:function(){this.reportError&&OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported",{renderers:this.renderers.join("\n")}))},setMap:function(a){OpenLayers.Layer.prototype.setMap.apply(this,
arguments);if(this.renderer){this.renderer.map=this.map;var b=this.map.getSize();b.w*=this.ratio;b.h*=this.ratio;this.renderer.setSize(b)}else this.map.removeLayer(this)},afterAdd:function(){if(this.strategies){var a,b,c;b=0;for(c=this.strategies.length;b<c;b++)a=this.strategies[b],a.autoActivate&&a.activate()}},removeMap:function(a){this.drawn=!1;if(this.strategies){var b,c;b=0;for(c=this.strategies.length;b<c;b++)a=this.strategies[b],a.autoActivate&&a.deactivate()}},onMapResize:function(){OpenLayers.Layer.prototype.onMapResize.apply(this,
arguments);var a=this.map.getSize();a.w*=this.ratio;a.h*=this.ratio;this.renderer.setSize(a)},moveTo:function(a,b,c){OpenLayers.Layer.prototype.moveTo.apply(this,arguments);var d=!0;if(!c){this.renderer.root.style.visibility="hidden";var d=this.map.getSize(),e=d.w,d=d.h,e=e/2*this.ratio-e/2,d=d/2*this.ratio-d/2,e=e+this.map.layerContainerOriginPx.x,e=-Math.round(e),d=d+this.map.layerContainerOriginPx.y,d=-Math.round(d);this.div.style.left=e+"px";this.div.style.top=d+"px";e=this.map.getExtent().scale(this.ratio);
d=this.renderer.setExtent(e,b);this.renderer.root.style.visibility="visible";!0===OpenLayers.IS_GECKO&&(this.div.scrollLeft=this.div.scrollLeft);if(!b&&d)for(var f in this.unrenderedFeatures)e=this.unrenderedFeatures[f],this.drawFeature(e)}if(!this.drawn||b||!d)for(this.drawn=!0,f=0,d=this.features.length;f<d;f++)this.renderer.locked=f!==d-1,e=this.features[f],this.drawFeature(e)},display:function(a){OpenLayers.Layer.prototype.display.apply(this,arguments);var b=this.div.style.display;b!=this.renderer.root.style.display&&
(this.renderer.root.style.display=b)},addFeatures:function(a,b){OpenLayers.Util.isArray(a)||(a=[a]);var c=!b||!b.silent;if(c){var d={features:a};if(!1===this.events.triggerEvent("beforefeaturesadded",d))return;a=d.features}for(var d=[],e=0,f=a.length;e<f;e++){this.renderer.locked=e!=a.length-1?!0:!1;var g=a[e];if(this.geometryType&&!(g.geometry instanceof this.geometryType))throw new TypeError("addFeatures: component should be an "+this.geometryType.prototype.CLASS_NAME);g.layer=this;!g.style&&this.style&&
(g.style=OpenLayers.Util.extend({},this.style));if(c){if(!1===this.events.triggerEvent("beforefeatureadded",{feature:g}))continue;this.preFeatureInsert(g)}d.push(g);this.features.push(g);this.drawFeature(g);c&&(this.events.triggerEvent("featureadded",{feature:g}),this.onFeatureInsert(g))}c&&this.events.triggerEvent("featuresadded",{features:d})},removeFeatures:function(a,b){if(a&&0!==a.length){if(a===this.features)return this.removeAllFeatures(b);OpenLayers.Util.isArray(a)||(a=[a]);a===this.selectedFeatures&&
(a=a.slice());var c=!b||!b.silent;c&&this.events.triggerEvent("beforefeaturesremoved",{features:a});for(var d=a.length-1;0<=d;d--){this.renderer.locked=0!=d&&a[d-1].geometry?!0:!1;var e=a[d];delete this.unrenderedFeatures[e.id];c&&this.events.triggerEvent("beforefeatureremoved",{feature:e});this.features=OpenLayers.Util.removeItem(this.features,e);e.layer=null;e.geometry&&this.renderer.eraseFeatures(e);-1!=OpenLayers.Util.indexOf(this.selectedFeatures,e)&&OpenLayers.Util.removeItem(this.selectedFeatures,
e);c&&this.events.triggerEvent("featureremoved",{feature:e})}c&&this.events.triggerEvent("featuresremoved",{features:a})}},removeAllFeatures:function(a){a=!a||!a.silent;var b=this.features;a&&this.events.triggerEvent("beforefeaturesremoved",{features:b});for(var c,d=b.length-1;0<=d;d--)c=b[d],a&&this.events.triggerEvent("beforefeatureremoved",{feature:c}),c.layer=null,a&&this.events.triggerEvent("featureremoved",{feature:c});this.renderer.clear();this.features=[];this.unrenderedFeatures={};this.selectedFeatures=
[];a&&this.events.triggerEvent("featuresremoved",{features:b})},destroyFeatures:function(a,b){void 0==a&&(a=this.features);if(a){this.removeFeatures(a,b);for(var c=a.length-1;0<=c;c--)a[c].destroy()}},drawFeature:function(a,b){if(this.drawn){if("object"!=typeof b){b||a.state!==OpenLayers.State.DELETE||(b="delete");var c=b||a.renderIntent;(b=a.style||this.style)||(b=this.styleMap.createSymbolizer(a,c))}c=this.renderer.drawFeature(a,b);!1===c||null===c?this.unrenderedFeatures[a.id]=a:delete this.unrenderedFeatures[a.id]}},
eraseFeatures:function(a){this.renderer.eraseFeatures(a)},getFeatureFromEvent:function(a){if(!this.renderer)throw Error("getFeatureFromEvent called on layer with no renderer. This usually means you destroyed a layer, but not some handler which is associated with it.");var b=null;(a=this.renderer.getFeatureIdFromEvent(a))&&(b="string"===typeof a?this.getFeatureById(a):a);return b},getFeatureBy:function(a,b){for(var c=null,d=0,e=this.features.length;d<e;++d)if(this.features[d][a]==b){c=this.features[d];
break}return c},getFeatureById:function(a){return this.getFeatureBy("id",a)},getFeatureByFid:function(a){return this.getFeatureBy("fid",a)},getFeaturesByAttribute:function(a,b){var c,d,e=this.features.length,f=[];for(c=0;c<e;c++)(d=this.features[c])&&d.attributes&&d.attributes[a]===b&&f.push(d);return f},onFeatureInsert:function(a){},preFeatureInsert:function(a){},getDataExtent:function(){var a=null,b=this.features;if(b&&0<b.length)for(var c=null,d=0,e=b.length;d<e;d++)if(c=b[d].geometry)null===a&&
(a=new OpenLayers.Bounds),a.extend(c.getBounds());return a},CLASS_NAME:"OpenLayers.Layer.Vector"});OpenLayers.Layer.PointGrid=OpenLayers.Class(OpenLayers.Layer.Vector,{dx:null,dy:null,ratio:1.5,maxFeatures:250,rotation:0,origin:null,gridBounds:null,initialize:function(a){a=a||{};OpenLayers.Layer.Vector.prototype.initialize.apply(this,[a.name,a])},setMap:function(a){OpenLayers.Layer.Vector.prototype.setMap.apply(this,arguments);a.events.register("moveend",this,this.onMoveEnd)},removeMap:function(a){a.events.unregister("moveend",this,this.onMoveEnd);OpenLayers.Layer.Vector.prototype.removeMap.apply(this,
arguments)},setRatio:function(a){this.ratio=a;this.updateGrid(!0)},setMaxFeatures:function(a){this.maxFeatures=a;this.updateGrid(!0)},setSpacing:function(a,b){this.dx=a;this.dy=b||a;this.updateGrid(!0)},setOrigin:function(a){this.origin=a;this.updateGrid(!0)},getOrigin:function(){this.origin||(this.origin=this.map.getExtent().getCenterLonLat());return this.origin},setRotation:function(a){this.rotation=a;this.updateGrid(!0)},onMoveEnd:function(){this.updateGrid()},getViewBounds:function(){var a=this.map.getExtent();
if(this.rotation){var b=this.getOrigin(),b=new OpenLayers.Geometry.Point(b.lon,b.lat),a=a.toGeometry();a.rotate(-this.rotation,b);a=a.getBounds()}return a},updateGrid:function(a){if(a||this.invalidBounds()){var b=this.getViewBounds(),c=this.getOrigin();a=new OpenLayers.Geometry.Point(c.lon,c.lat);var d=b.getWidth(),e=b.getHeight(),f=d/e,g=Math.sqrt(this.dx*this.dy*this.maxFeatures/f),d=Math.min(d*this.ratio,g*f),e=Math.min(e*this.ratio,g),b=b.getCenterLonLat();this.gridBounds=new OpenLayers.Bounds(b.lon-
d/2,b.lat-e/2,b.lon+d/2,b.lat+e/2);for(var b=Math.floor(e/this.dy),d=Math.floor(d/this.dx),e=c.lon+this.dx*Math.ceil((this.gridBounds.left-c.lon)/this.dx),c=c.lat+this.dy*Math.ceil((this.gridBounds.bottom-c.lat)/this.dy),g=Array(b*d),h,k=0;k<d;++k)for(var f=e+k*this.dx,l=0;l<b;++l)h=c+l*this.dy,h=new OpenLayers.Geometry.Point(f,h),this.rotation&&h.rotate(this.rotation,a),g[k*b+l]=new OpenLayers.Feature.Vector(h);this.destroyFeatures(this.features,{silent:!0});this.addFeatures(g,{silent:!0})}},invalidBounds:function(){return!this.gridBounds||
!this.gridBounds.containsBounds(this.getViewBounds())},CLASS_NAME:"OpenLayers.Layer.PointGrid"});OpenLayers.Handler.MouseWheel=OpenLayers.Class(OpenLayers.Handler,{wheelListener:null,interval:0,maxDelta:Number.POSITIVE_INFINITY,delta:0,cumulative:!0,initialize:function(a,b,c){OpenLayers.Handler.prototype.initialize.apply(this,arguments);this.wheelListener=OpenLayers.Function.bindAsEventListener(this.onWheelEvent,this)},destroy:function(){OpenLayers.Handler.prototype.destroy.apply(this,arguments);this.wheelListener=null},onWheelEvent:function(a){if(this.map&&this.checkModifiers(a)){for(var b=
!1,c=!1,d=!1,e=OpenLayers.Event.element(a);null!=e&&!d&&!b;){if(!b)try{var f,b=(f=e.currentStyle?e.currentStyle.overflow:document.defaultView.getComputedStyle(e,null).getPropertyValue("overflow"))&&"auto"==f||"scroll"==f}catch(g){}if(!c&&(c=OpenLayers.Element.hasClass(e,"olScrollable"),!c))for(var d=0,h=this.map.layers.length;d<h;d++){var k=this.map.layers[d];if(e==k.div||e==k.pane){c=!0;break}}d=e==this.map.div;e=e.parentNode}if(!b&&d){if(c)if(b=0,a.wheelDelta?(b=a.wheelDelta,0===b%160&&(b*=0.75),
b/=120):a.detail&&(b=-(a.detail/Math.abs(a.detail))),this.delta+=b,window.clearTimeout(this._timeoutId),this.interval&&Math.abs(this.delta)<this.maxDelta){var l=OpenLayers.Util.extend({},a);this._timeoutId=window.setTimeout(OpenLayers.Function.bind(function(){this.wheelZoom(l)},this),this.interval)}else this.wheelZoom(a);OpenLayers.Event.stop(a)}}},wheelZoom:function(a){var b=this.delta;this.delta=0;b&&(a.xy=this.map.events.getMousePosition(a),0>b?this.callback("down",[a,this.cumulative?Math.max(-this.maxDelta,
b):-1]):this.callback("up",[a,this.cumulative?Math.min(this.maxDelta,b):1]))},activate:function(a){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){var b=this.wheelListener;OpenLayers.Event.observe(window,"DOMMouseScroll",b);OpenLayers.Event.observe(window,"mousewheel",b);OpenLayers.Event.observe(document,"mousewheel",b);return!0}return!1},deactivate:function(a){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){var b=this.wheelListener;OpenLayers.Event.stopObserving(window,
"DOMMouseScroll",b);OpenLayers.Event.stopObserving(window,"mousewheel",b);OpenLayers.Event.stopObserving(document,"mousewheel",b);return!0}return!1},CLASS_NAME:"OpenLayers.Handler.MouseWheel"});OpenLayers.Symbolizer=OpenLayers.Class({zIndex:0,initialize:function(a){OpenLayers.Util.extend(this,a)},clone:function(){return new (eval(this.CLASS_NAME))(OpenLayers.Util.extend({},this))},CLASS_NAME:"OpenLayers.Symbolizer"});OpenLayers.Symbolizer.Raster=OpenLayers.Class(OpenLayers.Symbolizer,{initialize:function(a){OpenLayers.Symbolizer.prototype.initialize.apply(this,arguments)},CLASS_NAME:"OpenLayers.Symbolizer.Raster"});OpenLayers.Rule=OpenLayers.Class({id:null,name:null,title:null,description:null,context:null,filter:null,elseFilter:!1,symbolizer:null,symbolizers:null,minScaleDenominator:null,maxScaleDenominator:null,initialize:function(a){this.symbolizer={};OpenLayers.Util.extend(this,a);this.symbolizers&&delete this.symbolizer;this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},destroy:function(){for(var a in this.symbolizer)this.symbolizer[a]=null;this.symbolizer=null;delete this.symbolizers},evaluate:function(a){var b=
this.getContext(a),c=!0;if(this.minScaleDenominator||this.maxScaleDenominator)var d=a.layer.map.getScale();this.minScaleDenominator&&(c=d>=OpenLayers.Style.createLiteral(this.minScaleDenominator,b));c&&this.maxScaleDenominator&&(c=d<OpenLayers.Style.createLiteral(this.maxScaleDenominator,b));c&&this.filter&&(c="OpenLayers.Filter.FeatureId"==this.filter.CLASS_NAME?this.filter.evaluate(a):this.filter.evaluate(b));return c},getContext:function(a){var b=this.context;b||(b=a.attributes||a.data);"function"==
typeof this.context&&(b=this.context(a));return b},clone:function(){var a=OpenLayers.Util.extend({},this);if(this.symbolizers){var b=this.symbolizers.length;a.symbolizers=Array(b);for(var c=0;c<b;++c)a.symbolizers[c]=this.symbolizers[c].clone()}else{a.symbolizer={};for(var d in this.symbolizer)b=this.symbolizer[d],c=typeof b,"object"===c?a.symbolizer[d]=OpenLayers.Util.extend({},b):"string"===c&&(a.symbolizer[d]=b)}a.filter=this.filter&&this.filter.clone();a.context=this.context&&OpenLayers.Util.extend({},
this.context);return new OpenLayers.Rule(a)},CLASS_NAME:"OpenLayers.Rule"});OpenLayers.Format.SLD=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{profile:null,defaultVersion:"1.0.0",stringifyOutput:!0,namedLayersAsArray:!1,CLASS_NAME:"OpenLayers.Format.SLD"});OpenLayers.Symbolizer.Polygon=OpenLayers.Class(OpenLayers.Symbolizer,{initialize:function(a){OpenLayers.Symbolizer.prototype.initialize.apply(this,arguments)},CLASS_NAME:"OpenLayers.Symbolizer.Polygon"});OpenLayers.Format.GML.v2=OpenLayers.Class(OpenLayers.Format.GML.Base,{schemaLocation:"http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",initialize:function(a){OpenLayers.Format.GML.Base.prototype.initialize.apply(this,[a])},readers:{gml:OpenLayers.Util.applyDefaults({outerBoundaryIs:function(a,b){var c={};this.readChildNodes(a,c);b.outer=c.components[0]},innerBoundaryIs:function(a,b){var c={};this.readChildNodes(a,c);b.inner.push(c.components[0])},Box:function(a,b){var c=
{};this.readChildNodes(a,c);b.components||(b.components=[]);var d=c.points[0],c=c.points[1];b.components.push(new OpenLayers.Bounds(d.x,d.y,c.x,c.y))}},OpenLayers.Format.GML.Base.prototype.readers.gml),feature:OpenLayers.Format.GML.Base.prototype.readers.feature,wfs:OpenLayers.Format.GML.Base.prototype.readers.wfs},write:function(a){var b;b=OpenLayers.Util.isArray(a)?"wfs:FeatureCollection":"gml:featureMember";a=this.writeNode(b,a);this.setAttributeNS(a,this.namespaces.xsi,"xsi:schemaLocation",this.schemaLocation);
return OpenLayers.Format.XML.prototype.write.apply(this,[a])},writers:{gml:OpenLayers.Util.applyDefaults({Point:function(a){var b=this.createElementNSPlus("gml:Point");this.writeNode("coordinates",[a],b);return b},coordinates:function(a){for(var b=a.length,c=Array(b),d,e=0;e<b;++e)d=a[e],c[e]=this.xy?d.x+","+d.y:d.y+","+d.x,void 0!=d.z&&(c[e]+=","+d.z);return this.createElementNSPlus("gml:coordinates",{attributes:{decimal:".",cs:",",ts:" "},value:1==b?c[0]:c.join(" ")})},LineString:function(a){var b=
this.createElementNSPlus("gml:LineString");this.writeNode("coordinates",a.components,b);return b},Polygon:function(a){var b=this.createElementNSPlus("gml:Polygon");this.writeNode("outerBoundaryIs",a.components[0],b);for(var c=1;c<a.components.length;++c)this.writeNode("innerBoundaryIs",a.components[c],b);return b},outerBoundaryIs:function(a){var b=this.createElementNSPlus("gml:outerBoundaryIs");this.writeNode("LinearRing",a,b);return b},innerBoundaryIs:function(a){var b=this.createElementNSPlus("gml:innerBoundaryIs");
this.writeNode("LinearRing",a,b);return b},LinearRing:function(a){var b=this.createElementNSPlus("gml:LinearRing");this.writeNode("coordinates",a.components,b);return b},Box:function(a){var b=this.createElementNSPlus("gml:Box");this.writeNode("coordinates",[{x:a.left,y:a.bottom},{x:a.right,y:a.top}],b);this.srsName&&b.setAttribute("srsName",this.srsName);return b}},OpenLayers.Format.GML.Base.prototype.writers.gml),feature:OpenLayers.Format.GML.Base.prototype.writers.feature,wfs:OpenLayers.Format.GML.Base.prototype.writers.wfs},
CLASS_NAME:"OpenLayers.Format.GML.v2"});OpenLayers.Format.Filter.v1_0_0=OpenLayers.Class(OpenLayers.Format.GML.v2,OpenLayers.Format.Filter.v1,{VERSION:"1.0.0",schemaLocation:"http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",initialize:function(a){OpenLayers.Format.GML.v2.prototype.initialize.apply(this,[a])},readers:{ogc:OpenLayers.Util.applyDefaults({PropertyIsEqualTo:function(a,b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.EQUAL_TO});this.readChildNodes(a,c);b.filters.push(c)},PropertyIsNotEqualTo:function(a,
b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.NOT_EQUAL_TO});this.readChildNodes(a,c);b.filters.push(c)},PropertyIsLike:function(a,b){var c=new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.LIKE});this.readChildNodes(a,c);var d=a.getAttribute("wildCard"),e=a.getAttribute("singleChar"),f=a.getAttribute("escape");c.value2regex(d,e,f);b.filters.push(c)}},OpenLayers.Format.Filter.v1.prototype.readers.ogc),gml:OpenLayers.Format.GML.v2.prototype.readers.gml,
feature:OpenLayers.Format.GML.v2.prototype.readers.feature},writers:{ogc:OpenLayers.Util.applyDefaults({PropertyIsEqualTo:function(a){var b=this.createElementNSPlus("ogc:PropertyIsEqualTo");this.writeNode("PropertyName",a,b);this.writeOgcExpression(a.value,b);return b},PropertyIsNotEqualTo:function(a){var b=this.createElementNSPlus("ogc:PropertyIsNotEqualTo");this.writeNode("PropertyName",a,b);this.writeOgcExpression(a.value,b);return b},PropertyIsLike:function(a){var b=this.createElementNSPlus("ogc:PropertyIsLike",
{attributes:{wildCard:"*",singleChar:".",escape:"!"}});this.writeNode("PropertyName",a,b);this.writeNode("Literal",a.regex2value(),b);return b},BBOX:function(a){var b=this.createElementNSPlus("ogc:BBOX");a.property&&this.writeNode("PropertyName",a,b);var c=this.writeNode("gml:Box",a.value,b);a.projection&&c.setAttribute("srsName",a.projection);return b}},OpenLayers.Format.Filter.v1.prototype.writers.ogc),gml:OpenLayers.Format.GML.v2.prototype.writers.gml,feature:OpenLayers.Format.GML.v2.prototype.writers.feature},
writeSpatial:function(a,b){var c=this.createElementNSPlus("ogc:"+b);this.writeNode("PropertyName",a,c);if(a.value instanceof OpenLayers.Filter.Function)this.writeNode("Function",a.value,c);else{var d;d=a.value instanceof OpenLayers.Geometry?this.writeNode("feature:_geometry",a.value).firstChild:this.writeNode("gml:Box",a.value);a.projection&&d.setAttribute("srsName",a.projection);c.appendChild(d)}return c},CLASS_NAME:"OpenLayers.Format.Filter.v1_0_0"});OpenLayers.Format.WFST.v1_0_0=OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0,OpenLayers.Format.WFST.v1,{version:"1.0.0",srsNameInQuery:!1,schemaLocations:{wfs:"http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd"},initialize:function(a){OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this,[a]);OpenLayers.Format.WFST.v1.prototype.initialize.apply(this,[a])},readNode:function(a,b,c){return OpenLayers.Format.GML.v2.prototype.readNode.apply(this,arguments)},readers:{wfs:OpenLayers.Util.applyDefaults({WFS_TransactionResponse:function(a,
b){b.insertIds=[];b.success=!1;this.readChildNodes(a,b)},InsertResult:function(a,b){var c={fids:[]};this.readChildNodes(a,c);b.insertIds=b.insertIds.concat(c.fids)},TransactionResult:function(a,b){this.readChildNodes(a,b)},Status:function(a,b){this.readChildNodes(a,b)},SUCCESS:function(a,b){b.success=!0}},OpenLayers.Format.WFST.v1.prototype.readers.wfs),gml:OpenLayers.Format.GML.v2.prototype.readers.gml,feature:OpenLayers.Format.GML.v2.prototype.readers.feature,ogc:OpenLayers.Format.Filter.v1_0_0.prototype.readers.ogc},
writers:{wfs:OpenLayers.Util.applyDefaults({Query:function(a){a=OpenLayers.Util.extend({featureNS:this.featureNS,featurePrefix:this.featurePrefix,featureType:this.featureType,srsName:this.srsName,srsNameInQuery:this.srsNameInQuery},a);var b=a.featurePrefix,c=this.createElementNSPlus("wfs:Query",{attributes:{typeName:(b?b+":":"")+a.featureType}});a.srsNameInQuery&&a.srsName&&c.setAttribute("srsName",a.srsName);a.featureNS&&c.setAttribute("xmlns:"+b,a.featureNS);if(a.propertyNames)for(var b=0,d=a.propertyNames.length;b<
d;b++)this.writeNode("ogc:PropertyName",{property:a.propertyNames[b]},c);a.filter&&(this.setFilterProperty(a.filter),this.writeNode("ogc:Filter",a.filter,c));return c}},OpenLayers.Format.WFST.v1.prototype.writers.wfs),gml:OpenLayers.Format.GML.v2.prototype.writers.gml,feature:OpenLayers.Format.GML.v2.prototype.writers.feature,ogc:OpenLayers.Format.Filter.v1_0_0.prototype.writers.ogc},CLASS_NAME:"OpenLayers.Format.WFST.v1_0_0"});OpenLayers.ElementsIndexer=OpenLayers.Class({maxZIndex:null,order:null,indices:null,compare:null,initialize:function(a){this.compare=a?OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER:OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;this.clear()},insert:function(a){this.exists(a)&&this.remove(a);var b=a.id;this.determineZIndex(a);for(var c=-1,d=this.order.length,e;1<d-c;)e=parseInt((c+d)/2),0<this.compare(this,a,OpenLayers.Util.getElement(this.order[e]))?c=e:d=e;this.order.splice(d,
0,b);this.indices[b]=this.getZIndex(a);return this.getNextElement(d)},remove:function(a){a=a.id;var b=OpenLayers.Util.indexOf(this.order,a);0<=b&&(this.order.splice(b,1),delete this.indices[a],this.maxZIndex=0<this.order.length?this.indices[this.order[this.order.length-1]]:0)},clear:function(){this.order=[];this.indices={};this.maxZIndex=0},exists:function(a){return null!=this.indices[a.id]},getZIndex:function(a){return a._style.graphicZIndex},determineZIndex:function(a){var b=a._style.graphicZIndex;
null==b?(b=this.maxZIndex,a._style.graphicZIndex=b):b>this.maxZIndex&&(this.maxZIndex=b)},getNextElement:function(a){a+=1;if(a<this.order.length){var b=OpenLayers.Util.getElement(this.order[a]);void 0==b&&(b=this.getNextElement(a));return b}return null},CLASS_NAME:"OpenLayers.ElementsIndexer"});
OpenLayers.ElementsIndexer.IndexingMethods={Z_ORDER:function(a,b,c){b=a.getZIndex(b);var d=0;c&&(a=a.getZIndex(c),d=b-a);return d},Z_ORDER_DRAWING_ORDER:function(a,b,c){a=OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(a,b,c);c&&0==a&&(a=1);return a},Z_ORDER_Y_ORDER:function(a,b,c){a=OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(a,b,c);c&&0===a&&(b=c._boundsBottom-b._boundsBottom,a=0===b?1:b);return a}};
OpenLayers.Renderer.Elements=OpenLayers.Class(OpenLayers.Renderer,{rendererRoot:null,root:null,vectorRoot:null,textRoot:null,xmlns:null,xOffset:0,indexer:null,BACKGROUND_ID_SUFFIX:"_background",LABEL_ID_SUFFIX:"_label",LABEL_OUTLINE_SUFFIX:"_outline",initialize:function(a,b){OpenLayers.Renderer.prototype.initialize.apply(this,arguments);this.rendererRoot=this.createRenderRoot();this.root=this.createRoot("_root");this.vectorRoot=this.createRoot("_vroot");this.textRoot=this.createRoot("_troot");this.root.appendChild(this.vectorRoot);
this.root.appendChild(this.textRoot);this.rendererRoot.appendChild(this.root);this.container.appendChild(this.rendererRoot);b&&(b.zIndexing||b.yOrdering)&&(this.indexer=new OpenLayers.ElementsIndexer(b.yOrdering))},destroy:function(){this.clear();this.xmlns=this.root=this.rendererRoot=null;OpenLayers.Renderer.prototype.destroy.apply(this,arguments)},clear:function(){var a,b=this.vectorRoot;if(b)for(;a=b.firstChild;)b.removeChild(a);if(b=this.textRoot)for(;a=b.firstChild;)b.removeChild(a);this.indexer&&
this.indexer.clear()},setExtent:function(a,b){var c=OpenLayers.Renderer.prototype.setExtent.apply(this,arguments),d=this.getResolution();if(this.map.baseLayer&&this.map.baseLayer.wrapDateLine){var e,f=a.getWidth()/this.map.getExtent().getWidth();a=a.scale(1/f);f=this.map.getMaxExtent();f.right>a.left&&f.right<a.right?e=!0:f.left>a.left&&f.left<a.right&&(e=!1);if(e!==this.rightOfDateLine||b)c=!1,this.xOffset=!0===e?f.getWidth()/d:0;this.rightOfDateLine=e}return c},getNodeType:function(a,b){},drawGeometry:function(a,
b,c){var d=a.CLASS_NAME,e=!0;if("OpenLayers.Geometry.Collection"==d||"OpenLayers.Geometry.MultiPoint"==d||"OpenLayers.Geometry.MultiLineString"==d||"OpenLayers.Geometry.MultiPolygon"==d){for(var d=0,f=a.components.length;d<f;d++)e=this.drawGeometry(a.components[d],b,c)&&e;return e}d=e=!1;"none"!=b.display&&(b.backgroundGraphic?this.redrawBackgroundNode(a.id,a,b,c):d=!0,e=this.redrawNode(a.id,a,b,c));!1==e&&(b=document.getElementById(a.id))&&(b._style.backgroundGraphic&&(d=!0),b.parentNode.removeChild(b));
d&&(b=document.getElementById(a.id+this.BACKGROUND_ID_SUFFIX))&&b.parentNode.removeChild(b);return e},redrawNode:function(a,b,c,d){c=this.applyDefaultSymbolizer(c);a=this.nodeFactory(a,this.getNodeType(b,c));a._featureId=d;a._boundsBottom=b.getBounds().bottom;a._geometryClass=b.CLASS_NAME;a._style=c;b=this.drawGeometryNode(a,b,c);if(!1===b)return!1;a=b.node;this.indexer?(c=this.indexer.insert(a))?this.vectorRoot.insertBefore(a,c):this.vectorRoot.appendChild(a):a.parentNode!==this.vectorRoot&&this.vectorRoot.appendChild(a);
this.postDraw(a);return b.complete},redrawBackgroundNode:function(a,b,c,d){c=OpenLayers.Util.extend({},c);c.externalGraphic=c.backgroundGraphic;c.graphicXOffset=c.backgroundXOffset;c.graphicYOffset=c.backgroundYOffset;c.graphicZIndex=c.backgroundGraphicZIndex;c.graphicWidth=c.backgroundWidth||c.graphicWidth;c.graphicHeight=c.backgroundHeight||c.graphicHeight;c.backgroundGraphic=null;c.backgroundXOffset=null;c.backgroundYOffset=null;c.backgroundGraphicZIndex=null;return this.redrawNode(a+this.BACKGROUND_ID_SUFFIX,
b,c,null)},drawGeometryNode:function(a,b,c){c=c||a._style;var d={isFilled:void 0===c.fill?!0:c.fill,isStroked:void 0===c.stroke?!!c.strokeWidth:c.stroke},e;switch(b.CLASS_NAME){case "OpenLayers.Geometry.Point":!1===c.graphic&&(d.isFilled=!1,d.isStroked=!1);e=this.drawPoint(a,b);break;case "OpenLayers.Geometry.LineString":d.isFilled=!1;e=this.drawLineString(a,b);break;case "OpenLayers.Geometry.LinearRing":e=this.drawLinearRing(a,b);break;case "OpenLayers.Geometry.Polygon":e=this.drawPolygon(a,b);break;
case "OpenLayers.Geometry.Rectangle":e=this.drawRectangle(a,b)}a._options=d;return!1!=e?{node:this.setStyle(a,c,d,b),complete:e}:!1},postDraw:function(a){},drawPoint:function(a,b){},drawLineString:function(a,b){},drawLinearRing:function(a,b){},drawPolygon:function(a,b){},drawRectangle:function(a,b){},drawCircle:function(a,b){},removeText:function(a){var b=document.getElementById(a+this.LABEL_ID_SUFFIX);b&&this.textRoot.removeChild(b);(a=document.getElementById(a+this.LABEL_OUTLINE_SUFFIX))&&this.textRoot.removeChild(a)},
getFeatureIdFromEvent:function(a){var b=a.target,c=b&&b.correspondingUseElement;return(c?c:b||a.srcElement)._featureId},eraseGeometry:function(a,b){if("OpenLayers.Geometry.MultiPoint"==a.CLASS_NAME||"OpenLayers.Geometry.MultiLineString"==a.CLASS_NAME||"OpenLayers.Geometry.MultiPolygon"==a.CLASS_NAME||"OpenLayers.Geometry.Collection"==a.CLASS_NAME)for(var c=0,d=a.components.length;c<d;c++)this.eraseGeometry(a.components[c],b);else(c=OpenLayers.Util.getElement(a.id))&&c.parentNode&&(c.geometry&&(c.geometry.destroy(),
c.geometry=null),c.parentNode.removeChild(c),this.indexer&&this.indexer.remove(c),c._style.backgroundGraphic&&(c=OpenLayers.Util.getElement(a.id+this.BACKGROUND_ID_SUFFIX))&&c.parentNode&&c.parentNode.removeChild(c))},nodeFactory:function(a,b){var c=OpenLayers.Util.getElement(a);c?this.nodeTypeCompare(c,b)||(c.parentNode.removeChild(c),c=this.nodeFactory(a,b)):c=this.createNode(b,a);return c},nodeTypeCompare:function(a,b){},createNode:function(a,b){},moveRoot:function(a){var b=this.root;a.root.parentNode==
this.rendererRoot&&(b=a.root);b.parentNode.removeChild(b);a.rendererRoot.appendChild(b)},getRenderLayerId:function(){return this.root.parentNode.parentNode.id},isComplexSymbol:function(a){return"circle"!=a&&!!a},CLASS_NAME:"OpenLayers.Renderer.Elements"});OpenLayers.Control.ArgParser=OpenLayers.Class(OpenLayers.Control,{center:null,zoom:null,layers:null,displayProjection:null,getParameters:function(a){a=a||window.location.href;var b=OpenLayers.Util.getParameters(a),c=a.indexOf("#");0<c&&(a="?"+a.substring(c+1,a.length),OpenLayers.Util.extend(b,OpenLayers.Util.getParameters(a)));return b},setMap:function(a){OpenLayers.Control.prototype.setMap.apply(this,arguments);for(var b=0,c=this.map.controls.length;b<c;b++){var d=this.map.controls[b];if(d!=this&&
"OpenLayers.Control.ArgParser"==d.CLASS_NAME){d.displayProjection!=this.displayProjection&&(this.displayProjection=d.displayProjection);break}}b==this.map.controls.length&&(b=this.getParameters(),b.layers&&(this.layers=b.layers,this.map.events.register("addlayer",this,this.configureLayers),this.configureLayers()),b.lat&&b.lon&&(this.center=new OpenLayers.LonLat(parseFloat(b.lon),parseFloat(b.lat)),b.zoom&&(this.zoom=parseFloat(b.zoom)),this.map.events.register("changebaselayer",this,this.setCenter),
this.setCenter()))},setCenter:function(){this.map.baseLayer&&(this.map.events.unregister("changebaselayer",this,this.setCenter),this.displayProjection&&this.center.transform(this.displayProjection,this.map.getProjectionObject()),this.map.setCenter(this.center,this.zoom))},configureLayers:function(){if(this.layers.length==this.map.layers.length){this.map.events.unregister("addlayer",this,this.configureLayers);for(var a=0,b=this.layers.length;a<b;a++){var c=this.map.layers[a],d=this.layers.charAt(a);
"B"==d?this.map.setBaseLayer(c):"T"!=d&&"F"!=d||c.setVisibility("T"==d)}}},CLASS_NAME:"OpenLayers.Control.ArgParser"});OpenLayers.Control.Permalink=OpenLayers.Class(OpenLayers.Control,{argParserClass:OpenLayers.Control.ArgParser,element:null,anchor:!1,base:"",displayProjection:null,initialize:function(a,b,c){null===a||"object"!=typeof a||OpenLayers.Util.isElement(a)?(OpenLayers.Control.prototype.initialize.apply(this,[c]),this.element=OpenLayers.Util.getElement(a),this.base=b||document.location.href):(this.base=document.location.href,OpenLayers.Control.prototype.initialize.apply(this,[a]),null!=this.element&&(this.element=
OpenLayers.Util.getElement(this.element)))},destroy:function(){this.element&&this.element.parentNode==this.div&&(this.div.removeChild(this.element),this.element=null);this.map&&this.map.events.unregister("moveend",this,this.updateLink);OpenLayers.Control.prototype.destroy.apply(this,arguments)},setMap:function(a){OpenLayers.Control.prototype.setMap.apply(this,arguments);for(var b=0,c=this.map.controls.length;b<c;b++){var d=this.map.controls[b];if(d.CLASS_NAME==this.argParserClass.CLASS_NAME){d.displayProjection!=
this.displayProjection&&(this.displayProjection=d.displayProjection);break}}b==this.map.controls.length&&this.map.addControl(new this.argParserClass({displayProjection:this.displayProjection}))},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);this.element||this.anchor||(this.element=document.createElement("a"),this.element.innerHTML=OpenLayers.i18n("Permalink"),this.element.href="",this.div.appendChild(this.element));this.map.events.on({moveend:this.updateLink,changelayer:this.updateLink,
changebaselayer:this.updateLink,scope:this});this.updateLink();return this.div},updateLink:function(){var a=this.anchor?"#":"?",b=this.base,c=null;-1!=b.indexOf("#")&&!1==this.anchor&&(c=b.substring(b.indexOf("#"),b.length));-1!=b.indexOf(a)&&(b=b.substring(0,b.indexOf(a)));b=b.split("#")[0]+a+OpenLayers.Util.getParameterString(this.createParams());c&&(b+=c);this.anchor&&!this.element?window.location.href=b:this.element.href=b},createParams:function(a,b,c){a=a||this.map.getCenter();var d=OpenLayers.Util.getParameters(this.base);
if(a)for(d.zoom=b||this.map.getZoom(),b=a.lat,a=a.lon,this.displayProjection&&(b=OpenLayers.Projection.transform({x:a,y:b},this.map.getProjectionObject(),this.displayProjection),a=b.x,b=b.y),d.lat=Math.round(1E5*b)/1E5,d.lon=Math.round(1E5*a)/1E5,c=c||this.map.layers,d.layers="",a=0,b=c.length;a<b;a++){var e=c[a];d.layers=e.isBaseLayer?d.layers+(e==this.map.baseLayer?"B":"0"):d.layers+(e.getVisibility()?"T":"F")}return d},CLASS_NAME:"OpenLayers.Control.Permalink"});OpenLayers.Layer.TMS=OpenLayers.Class(OpenLayers.Layer.Grid,{serviceVersion:"1.0.0",layername:null,type:null,isBaseLayer:!0,tileOrigin:null,serverResolutions:null,zoomOffset:0,initialize:function(a,b,c){var d=[];d.push(a,b,{},c);OpenLayers.Layer.Grid.prototype.initialize.apply(this,d)},clone:function(a){null==a&&(a=new OpenLayers.Layer.TMS(this.name,this.url,this.getOptions()));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,[a])},getURL:function(a){a=this.adjustBounds(a);var b=this.getServerResolution(),
c=Math.round((a.left-this.tileOrigin.lon)/(b*this.tileSize.w));a=Math.round((a.bottom-this.tileOrigin.lat)/(b*this.tileSize.h));b=this.getServerZoom();c=this.serviceVersion+"/"+this.layername+"/"+b+"/"+c+"/"+a+"."+this.type;a=this.url;OpenLayers.Util.isArray(a)&&(a=this.selectUrl(c,a));return a+c},setMap:function(a){OpenLayers.Layer.Grid.prototype.setMap.apply(this,arguments);this.tileOrigin||(this.tileOrigin=new OpenLayers.LonLat(this.map.maxExtent.left,this.map.maxExtent.bottom))},CLASS_NAME:"OpenLayers.Layer.TMS"});OpenLayers.Format.WCSCapabilities=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.1.0",CLASS_NAME:"OpenLayers.Format.WCSCapabilities"});OpenLayers.Format.WCSCapabilities.v1=OpenLayers.Class(OpenLayers.Format.XML,{regExes:{trimSpace:/^\s*|\s*$/g,splitSpace:/\s+/},defaultPrefix:"wcs",read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={};this.readNode(a,b);return b},CLASS_NAME:"OpenLayers.Format.WCSCapabilities.v1"});OpenLayers.Format.WCSCapabilities.v1_0_0=OpenLayers.Class(OpenLayers.Format.WCSCapabilities.v1,{namespaces:{wcs:"http://www.opengis.net/wcs",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance",ows:"http://www.opengis.net/ows"},errorProperty:"service",readers:{wcs:{WCS_Capabilities:function(a,b){this.readChildNodes(a,b)},Service:function(a,b){b.service={};this.readChildNodes(a,b.service)},name:function(a,b){b.name=this.getChildValue(a)},label:function(a,b){b.label=
this.getChildValue(a)},keywords:function(a,b){b.keywords=[];this.readChildNodes(a,b.keywords)},keyword:function(a,b){b.push(this.getChildValue(a))},responsibleParty:function(a,b){b.responsibleParty={};this.readChildNodes(a,b.responsibleParty)},individualName:function(a,b){b.individualName=this.getChildValue(a)},organisationName:function(a,b){b.organisationName=this.getChildValue(a)},positionName:function(a,b){b.positionName=this.getChildValue(a)},contactInfo:function(a,b){b.contactInfo={};this.readChildNodes(a,
b.contactInfo)},phone:function(a,b){b.phone={};this.readChildNodes(a,b.phone)},voice:function(a,b){b.voice=this.getChildValue(a)},facsimile:function(a,b){b.facsimile=this.getChildValue(a)},address:function(a,b){b.address={};this.readChildNodes(a,b.address)},deliveryPoint:function(a,b){b.deliveryPoint=this.getChildValue(a)},city:function(a,b){b.city=this.getChildValue(a)},postalCode:function(a,b){b.postalCode=this.getChildValue(a)},country:function(a,b){b.country=this.getChildValue(a)},electronicMailAddress:function(a,
b){b.electronicMailAddress=this.getChildValue(a)},fees:function(a,b){b.fees=this.getChildValue(a)},accessConstraints:function(a,b){b.accessConstraints=this.getChildValue(a)},ContentMetadata:function(a,b){b.contentMetadata=[];this.readChildNodes(a,b.contentMetadata)},CoverageOfferingBrief:function(a,b){var c={};this.readChildNodes(a,c);b.push(c)},name:function(a,b){b.name=this.getChildValue(a)},label:function(a,b){b.label=this.getChildValue(a)},lonLatEnvelope:function(a,b){var c=this.getElementsByTagNameNS(a,
"http://www.opengis.net/gml","pos");if(2==c.length){var d={},e={};OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply(this,[c[0],d]);OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply(this,[c[1],e]);b.lonLatEnvelope={};b.lonLatEnvelope.srsName=a.getAttribute("srsName");b.lonLatEnvelope.min=d.points[0];b.lonLatEnvelope.max=e.points[0]}}}},CLASS_NAME:"OpenLayers.Format.WCSCapabilities.v1_0_0"});OpenLayers.Strategy.Fixed=OpenLayers.Class(OpenLayers.Strategy,{preload:!1,activate:function(){var a=OpenLayers.Strategy.prototype.activate.apply(this,arguments);if(a)if(this.layer.events.on({refresh:this.load,scope:this}),!0==this.layer.visibility||this.preload)this.load();else this.layer.events.on({visibilitychanged:this.load,scope:this});return a},deactivate:function(){var a=OpenLayers.Strategy.prototype.deactivate.call(this);a&&this.layer.events.un({refresh:this.load,visibilitychanged:this.load,
scope:this});return a},load:function(a){var b=this.layer;b.events.triggerEvent("loadstart",{filter:b.filter});b.protocol.read(OpenLayers.Util.applyDefaults({callback:this.merge,filter:b.filter,scope:this},a));b.events.un({visibilitychanged:this.load,scope:this})},merge:function(a){var b=this.layer;b.destroyFeatures();var c=a.features;if(c&&0<c.length){var d=b.projection,e=b.map.getProjectionObject();if(!e.equals(d))for(var f,g=0,h=c.length;g<h;++g)(f=c[g].geometry)&&f.transform(d,e);b.addFeatures(c)}b.events.triggerEvent("loadend",
{response:a})},CLASS_NAME:"OpenLayers.Strategy.Fixed"});OpenLayers.Control.Zoom=OpenLayers.Class(OpenLayers.Control,{zoomInText:"+",zoomInId:"olZoomInLink",zoomOutText:"\u2212",zoomOutId:"olZoomOutLink",draw:function(){var a=OpenLayers.Control.prototype.draw.apply(this),b=this.getOrCreateLinks(a),c=b.zoomIn,b=b.zoomOut,d=this.map.events;b.parentNode!==a&&(d=this.events,d.attachToElement(b.parentNode));d.register("buttonclick",this,this.onZoomClick);this.zoomInLink=c;this.zoomOutLink=b;return a},getOrCreateLinks:function(a){var b=document.getElementById(this.zoomInId),
c=document.getElementById(this.zoomOutId);b||(b=document.createElement("a"),b.href="#zoomIn",b.appendChild(document.createTextNode(this.zoomInText)),b.className="olControlZoomIn",a.appendChild(b));OpenLayers.Element.addClass(b,"olButton");c||(c=document.createElement("a"),c.href="#zoomOut",c.appendChild(document.createTextNode(this.zoomOutText)),c.className="olControlZoomOut",a.appendChild(c));OpenLayers.Element.addClass(c,"olButton");return{zoomIn:b,zoomOut:c}},onZoomClick:function(a){a=a.buttonElement;
a===this.zoomInLink?this.map.zoomIn():a===this.zoomOutLink&&this.map.zoomOut()},destroy:function(){this.map&&this.map.events.unregister("buttonclick",this,this.onZoomClick);delete this.zoomInLink;delete this.zoomOutLink;OpenLayers.Control.prototype.destroy.apply(this)},CLASS_NAME:"OpenLayers.Control.Zoom"});OpenLayers.Layer.PointTrack=OpenLayers.Class(OpenLayers.Layer.Vector,{dataFrom:null,styleFrom:null,addNodes:function(a,b){if(2>a.length)throw Error("At least two point features have to be added to create a line from");for(var c=Array(a.length-1),d,e,f,g=0,h=a.length;g<h;g++){d=a[g];f=d.geometry;if(!f)f=d.lonlat,f=new OpenLayers.Geometry.Point(f.lon,f.lat);else if("OpenLayers.Geometry.Point"!=f.CLASS_NAME)throw new TypeError("Only features with point geometries are supported.");if(0<g){d=null!=this.dataFrom?
a[g+this.dataFrom].data||a[g+this.dataFrom].attributes:null;var k=null!=this.styleFrom?a[g+this.styleFrom].style:null;e=new OpenLayers.Geometry.LineString([e,f]);c[g-1]=new OpenLayers.Feature.Vector(e,d,k)}e=f}this.addFeatures(c,b)},CLASS_NAME:"OpenLayers.Layer.PointTrack"});OpenLayers.Layer.PointTrack.SOURCE_NODE=-1;OpenLayers.Layer.PointTrack.TARGET_NODE=0;OpenLayers.Layer.PointTrack.dataFrom={SOURCE_NODE:-1,TARGET_NODE:0};OpenLayers.Protocol.WFS=function(a){a=OpenLayers.Util.applyDefaults(a,OpenLayers.Protocol.WFS.DEFAULTS);var b=OpenLayers.Protocol.WFS["v"+a.version.replace(/\./g,"_")];if(!b)throw"Unsupported WFS version: "+a.version;return new b(a)};
OpenLayers.Protocol.WFS.fromWMSLayer=function(a,b){var c,d;c=a.params.LAYERS;c=(OpenLayers.Util.isArray(c)?c[0]:c).split(":");1<c.length&&(d=c[0]);c=c.pop();d={url:a.url,featureType:c,featurePrefix:d,srsName:a.projection&&a.projection.getCode()||a.map&&a.map.getProjectionObject().getCode(),version:"1.1.0"};return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(b,d))};OpenLayers.Protocol.WFS.DEFAULTS={version:"1.0.0"};OpenLayers.Layer.Markers=OpenLayers.Class(OpenLayers.Layer,{isBaseLayer:!1,markers:null,drawn:!1,initialize:function(a,b){OpenLayers.Layer.prototype.initialize.apply(this,arguments);this.markers=[]},destroy:function(){this.clearMarkers();this.markers=null;OpenLayers.Layer.prototype.destroy.apply(this,arguments)},setOpacity:function(a){if(a!=this.opacity){this.opacity=a;a=0;for(var b=this.markers.length;a<b;a++)this.markers[a].setOpacity(this.opacity)}},moveTo:function(a,b,c){OpenLayers.Layer.prototype.moveTo.apply(this,
arguments);if(b||!this.drawn){for(var d=0,e=this.markers.length;d<e;d++)this.drawMarker(this.markers[d]);this.drawn=!0}},addMarker:function(a){this.markers.push(a);1>this.opacity&&a.setOpacity(this.opacity);this.map&&this.map.getExtent()&&(a.map=this.map,this.drawMarker(a))},removeMarker:function(a){this.markers&&this.markers.length&&(OpenLayers.Util.removeItem(this.markers,a),a.erase())},clearMarkers:function(){if(null!=this.markers)for(;0<this.markers.length;)this.removeMarker(this.markers[0])},
drawMarker:function(a){var b=this.map.getLayerPxFromLonLat(a.lonlat);null==b?a.display(!1):a.isDrawn()?a.icon&&a.icon.moveTo(b):(a=a.draw(b),this.div.appendChild(a))},getDataExtent:function(){var a=null;if(this.markers&&0<this.markers.length)for(var a=new OpenLayers.Bounds,b=0,c=this.markers.length;b<c;b++)a.extend(this.markers[b].lonlat);return a},CLASS_NAME:"OpenLayers.Layer.Markers"});OpenLayers.Control.Pan=OpenLayers.Class(OpenLayers.Control.Button,{slideFactor:50,slideRatio:null,direction:null,initialize:function(a,b){this.direction=a;this.CLASS_NAME+=this.direction;OpenLayers.Control.prototype.initialize.apply(this,[b])},trigger:function(){if(this.map){var a=OpenLayers.Function.bind(function(a){return this.slideRatio?this.map.getSize()[a]*this.slideRatio:this.slideFactor},this);switch(this.direction){case OpenLayers.Control.Pan.NORTH:this.map.pan(0,-a("h"));break;case OpenLayers.Control.Pan.SOUTH:this.map.pan(0,
a("h"));break;case OpenLayers.Control.Pan.WEST:this.map.pan(-a("w"),0);break;case OpenLayers.Control.Pan.EAST:this.map.pan(a("w"),0)}}},CLASS_NAME:"OpenLayers.Control.Pan"});OpenLayers.Control.Pan.NORTH="North";OpenLayers.Control.Pan.SOUTH="South";OpenLayers.Control.Pan.EAST="East";OpenLayers.Control.Pan.WEST="West";OpenLayers.Format.CSWGetDomain=function(a){a=OpenLayers.Util.applyDefaults(a,OpenLayers.Format.CSWGetDomain.DEFAULTS);var b=OpenLayers.Format.CSWGetDomain["v"+a.version.replace(/\./g,"_")];if(!b)throw"Unsupported CSWGetDomain version: "+a.version;return new b(a)};OpenLayers.Format.CSWGetDomain.DEFAULTS={version:"2.0.2"};OpenLayers.Format.CSWGetDomain.v2_0_2=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance",csw:"http://www.opengis.net/cat/csw/2.0.2"},defaultPrefix:"csw",version:"2.0.2",schemaLocation:"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd",PropertyName:null,ParameterName:null,read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==
a.nodeType&&(a=a.documentElement);var b={};this.readNode(a,b);return b},readers:{csw:{GetDomainResponse:function(a,b){this.readChildNodes(a,b)},DomainValues:function(a,b){OpenLayers.Util.isArray(b.DomainValues)||(b.DomainValues=[]);for(var c=a.attributes,d={},e=0,f=c.length;e<f;++e)d[c[e].name]=c[e].nodeValue;this.readChildNodes(a,d);b.DomainValues.push(d)},PropertyName:function(a,b){b.PropertyName=this.getChildValue(a)},ParameterName:function(a,b){b.ParameterName=this.getChildValue(a)},ListOfValues:function(a,
b){OpenLayers.Util.isArray(b.ListOfValues)||(b.ListOfValues=[]);this.readChildNodes(a,b.ListOfValues)},Value:function(a,b){for(var c=a.attributes,d={},e=0,f=c.length;e<f;++e)d[c[e].name]=c[e].nodeValue;d.value=this.getChildValue(a);b.push({Value:d})},ConceptualScheme:function(a,b){b.ConceptualScheme={};this.readChildNodes(a,b.ConceptualScheme)},Name:function(a,b){b.Name=this.getChildValue(a)},Document:function(a,b){b.Document=this.getChildValue(a)},Authority:function(a,b){b.Authority=this.getChildValue(a)},
RangeOfValues:function(a,b){b.RangeOfValues={};this.readChildNodes(a,b.RangeOfValues)},MinValue:function(a,b){for(var c=a.attributes,d={},e=0,f=c.length;e<f;++e)d[c[e].name]=c[e].nodeValue;d.value=this.getChildValue(a);b.MinValue=d},MaxValue:function(a,b){for(var c=a.attributes,d={},e=0,f=c.length;e<f;++e)d[c[e].name]=c[e].nodeValue;d.value=this.getChildValue(a);b.MaxValue=d}}},write:function(a){a=this.writeNode("csw:GetDomain",a);return OpenLayers.Format.XML.prototype.write.apply(this,[a])},writers:{csw:{GetDomain:function(a){var b=
this.createElementNSPlus("csw:GetDomain",{attributes:{service:"CSW",version:this.version}});a.PropertyName||this.PropertyName?this.writeNode("csw:PropertyName",a.PropertyName||this.PropertyName,b):(a.ParameterName||this.ParameterName)&&this.writeNode("csw:ParameterName",a.ParameterName||this.ParameterName,b);this.readChildNodes(b,a);return b},PropertyName:function(a){return this.createElementNSPlus("csw:PropertyName",{value:a})},ParameterName:function(a){return this.createElementNSPlus("csw:ParameterName",
{value:a})}}},CLASS_NAME:"OpenLayers.Format.CSWGetDomain.v2_0_2"});OpenLayers.Format.ArcXML.Features=OpenLayers.Class(OpenLayers.Format.XML,{read:function(a){return(new OpenLayers.Format.ArcXML).read(a).features.feature}});OpenLayers.Control.Snapping=OpenLayers.Class(OpenLayers.Control,{DEFAULTS:{tolerance:10,node:!0,edge:!0,vertex:!0},greedy:!0,precedence:["node","vertex","edge"],resolution:null,geoToleranceCache:null,layer:null,feature:null,point:null,initialize:function(a){OpenLayers.Control.prototype.initialize.apply(this,[a]);this.options=a||{};this.options.layer&&this.setLayer(this.options.layer);a=OpenLayers.Util.extend({},this.options.defaults);this.defaults=OpenLayers.Util.applyDefaults(a,this.DEFAULTS);this.setTargets(this.options.targets);
0===this.targets.length&&this.layer&&this.addTargetLayer(this.layer);this.geoToleranceCache={}},setLayer:function(a){this.active?(this.deactivate(),this.layer=a,this.activate()):this.layer=a},setTargets:function(a){this.targets=[];if(a&&a.length)for(var b,c=0,d=a.length;c<d;++c)b=a[c],b instanceof OpenLayers.Layer.Vector?this.addTargetLayer(b):this.addTarget(b)},addTargetLayer:function(a){this.addTarget({layer:a})},addTarget:function(a){a=OpenLayers.Util.applyDefaults(a,this.defaults);a.nodeTolerance=
a.nodeTolerance||a.tolerance;a.vertexTolerance=a.vertexTolerance||a.tolerance;a.edgeTolerance=a.edgeTolerance||a.tolerance;this.targets.push(a)},removeTargetLayer:function(a){for(var b,c=this.targets.length-1;0<=c;--c)b=this.targets[c],b.layer===a&&this.removeTarget(b)},removeTarget:function(a){return OpenLayers.Util.removeItem(this.targets,a)},activate:function(){var a=OpenLayers.Control.prototype.activate.call(this);if(a&&this.layer&&this.layer.events)this.layer.events.on({sketchstarted:this.onSketchModified,
sketchmodified:this.onSketchModified,vertexmodified:this.onVertexModified,scope:this});return a},deactivate:function(){var a=OpenLayers.Control.prototype.deactivate.call(this);a&&this.layer&&this.layer.events&&this.layer.events.un({sketchstarted:this.onSketchModified,sketchmodified:this.onSketchModified,vertexmodified:this.onVertexModified,scope:this});this.point=this.feature=null;return a},onSketchModified:function(a){this.feature=a.feature;this.considerSnapping(a.vertex,a.vertex)},onVertexModified:function(a){this.feature=
a.feature;var b=this.layer.map.getLonLatFromViewPortPx(a.pixel);this.considerSnapping(a.vertex,new OpenLayers.Geometry.Point(b.lon,b.lat))},considerSnapping:function(a,b){for(var c={rank:Number.POSITIVE_INFINITY,dist:Number.POSITIVE_INFINITY,x:null,y:null},d=!1,e,f,g=0,h=this.targets.length;g<h;++g)if(f=this.targets[g],e=this.testTarget(f,b))if(this.greedy){c=e;c.target=f;d=!0;break}else if(e.rank<c.rank||e.rank===c.rank&&e.dist<c.dist)c=e,c.target=f,d=!0;d&&(!1!==this.events.triggerEvent("beforesnap",
{point:a,x:c.x,y:c.y,distance:c.dist,layer:c.target.layer,snapType:this.precedence[c.rank]})?(a.x=c.x,a.y=c.y,this.point=a,this.events.triggerEvent("snap",{point:a,snapType:this.precedence[c.rank],layer:c.target.layer,distance:c.dist})):d=!1);this.point&&!d&&(a.x=b.x,a.y=b.y,this.point=null,this.events.triggerEvent("unsnap",{point:a}))},testTarget:function(a,b){var c=this.layer.map.getResolution();if("minResolution"in a&&c<a.minResolution||"maxResolution"in a&&c>=a.maxResolution)return null;for(var c=
{node:this.getGeoTolerance(a.nodeTolerance,c),vertex:this.getGeoTolerance(a.vertexTolerance,c),edge:this.getGeoTolerance(a.edgeTolerance,c)},d=Math.max(c.node,c.vertex,c.edge),e={rank:Number.POSITIVE_INFINITY,dist:Number.POSITIVE_INFINITY},f=!1,g=a.layer.features,h,k,l,m,n,p,q=this.precedence.length,r=new OpenLayers.LonLat(b.x,b.y),s=0,t=g.length;s<t;++s)if(h=g[s],h!==this.feature&&(!h._sketch&&h.state!==OpenLayers.State.DELETE&&(!a.filter||a.filter.evaluate(h)))&&h.atPoint(r,d,d))for(var u=0,v=Math.min(e.rank+
1,q);u<v;++u)if(k=this.precedence[u],a[k])if("edge"===k){if(l=h.geometry.distanceTo(b,{details:!0}),n=l.distance,n<=c[k]&&n<e.dist){e={rank:u,dist:n,x:l.x0,y:l.y0};f=!0;break}}else{l=h.geometry.getVertices("node"===k);p=!1;for(var w=0,x=l.length;w<x;++w)m=l[w],n=m.distanceTo(b),n<=c[k]&&(u<e.rank||u===e.rank&&n<e.dist)&&(e={rank:u,dist:n,x:m.x,y:m.y},p=f=!0);if(p)break}return f?e:null},getGeoTolerance:function(a,b){b!==this.resolution&&(this.resolution=b,this.geoToleranceCache={});var c=this.geoToleranceCache[a];
void 0===c&&(c=a*b,this.geoToleranceCache[a]=c);return c},destroy:function(){this.active&&this.deactivate();delete this.layer;delete this.targets;OpenLayers.Control.prototype.destroy.call(this)},CLASS_NAME:"OpenLayers.Control.Snapping"});OpenLayers.Format.OWSCommon.v1_1_0=OpenLayers.Class(OpenLayers.Format.OWSCommon.v1,{namespaces:{ows:"http://www.opengis.net/ows/1.1",xlink:"http://www.w3.org/1999/xlink"},readers:{ows:OpenLayers.Util.applyDefaults({ExceptionReport:function(a,b){b.exceptionReport={version:a.getAttribute("version"),language:a.getAttribute("xml:lang"),exceptions:[]};this.readChildNodes(a,b.exceptionReport)},AllowedValues:function(a,b){b.allowedValues={};this.readChildNodes(a,b.allowedValues)},AnyValue:function(a,b){b.anyValue=
!0},DataType:function(a,b){b.dataType=this.getChildValue(a)},Range:function(a,b){b.range={};this.readChildNodes(a,b.range)},MinimumValue:function(a,b){b.minValue=this.getChildValue(a)},MaximumValue:function(a,b){b.maxValue=this.getChildValue(a)},Identifier:function(a,b){b.identifier=this.getChildValue(a)},SupportedCRS:function(a,b){b.supportedCRS=this.getChildValue(a)}},OpenLayers.Format.OWSCommon.v1.prototype.readers.ows)},writers:{ows:OpenLayers.Util.applyDefaults({Range:function(a){var b=this.createElementNSPlus("ows:Range",
{attributes:{"ows:rangeClosure":a.closure}});this.writeNode("ows:MinimumValue",a.minValue,b);this.writeNode("ows:MaximumValue",a.maxValue,b);return b},MinimumValue:function(a){return this.createElementNSPlus("ows:MinimumValue",{value:a})},MaximumValue:function(a){return this.createElementNSPlus("ows:MaximumValue",{value:a})},Value:function(a){return this.createElementNSPlus("ows:Value",{value:a})}},OpenLayers.Format.OWSCommon.v1.prototype.writers.ows)},CLASS_NAME:"OpenLayers.Format.OWSCommon.v1_1_0"});OpenLayers.Format.WCSGetCoverage=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{ows:"http://www.opengis.net/ows/1.1",wcs:"http://www.opengis.net/wcs/1.1",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},VERSION:"1.1.2",schemaLocation:"http://www.opengis.net/wcs/1.1 http://schemas.opengis.net/wcs/1.1/wcsGetCoverage.xsd",write:function(a){a=this.writeNode("wcs:GetCoverage",
a);this.setAttributeNS(a,this.namespaces.xsi,"xsi:schemaLocation",this.schemaLocation);return OpenLayers.Format.XML.prototype.write.apply(this,[a])},writers:{wcs:{GetCoverage:function(a){var b=this.createElementNSPlus("wcs:GetCoverage",{attributes:{version:a.version||this.VERSION,service:"WCS"}});this.writeNode("ows:Identifier",a.identifier,b);this.writeNode("wcs:DomainSubset",a.domainSubset,b);this.writeNode("wcs:Output",a.output,b);return b},DomainSubset:function(a){var b=this.createElementNSPlus("wcs:DomainSubset",
{});this.writeNode("ows:BoundingBox",a.boundingBox,b);a.temporalSubset&&this.writeNode("wcs:TemporalSubset",a.temporalSubset,b);return b},TemporalSubset:function(a){for(var b=this.createElementNSPlus("wcs:TemporalSubset",{}),c=0,d=a.timePeriods.length;c<d;++c)this.writeNode("wcs:TimePeriod",a.timePeriods[c],b);return b},TimePeriod:function(a){var b=this.createElementNSPlus("wcs:TimePeriod",{});this.writeNode("wcs:BeginPosition",a.begin,b);this.writeNode("wcs:EndPosition",a.end,b);a.resolution&&this.writeNode("wcs:TimeResolution",
a.resolution,b);return b},BeginPosition:function(a){return this.createElementNSPlus("wcs:BeginPosition",{value:a})},EndPosition:function(a){return this.createElementNSPlus("wcs:EndPosition",{value:a})},TimeResolution:function(a){return this.createElementNSPlus("wcs:TimeResolution",{value:a})},Output:function(a){var b=this.createElementNSPlus("wcs:Output",{attributes:{format:a.format,store:a.store}});a.gridCRS&&this.writeNode("wcs:GridCRS",a.gridCRS,b);return b},GridCRS:function(a){var b=this.createElementNSPlus("wcs:GridCRS",
{});this.writeNode("wcs:GridBaseCRS",a.baseCRS,b);a.type&&this.writeNode("wcs:GridType",a.type,b);a.origin&&this.writeNode("wcs:GridOrigin",a.origin,b);this.writeNode("wcs:GridOffsets",a.offsets,b);a.CS&&this.writeNode("wcs:GridCS",a.CS,b);return b},GridBaseCRS:function(a){return this.createElementNSPlus("wcs:GridBaseCRS",{value:a})},GridOrigin:function(a){return this.createElementNSPlus("wcs:GridOrigin",{value:a})},GridType:function(a){return this.createElementNSPlus("wcs:GridType",{value:a})},GridOffsets:function(a){return this.createElementNSPlus("wcs:GridOffsets",
{value:a})},GridCS:function(a){return this.createElementNSPlus("wcs:GridCS",{value:a})}},ows:OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows},CLASS_NAME:"OpenLayers.Format.WCSGetCoverage"});OpenLayers.Format.KML=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{kml:"http://www.opengis.net/kml/2.2",gx:"http://www.google.com/kml/ext/2.2"},kmlns:"http://earth.google.com/kml/2.0",placemarksDesc:"No description available",foldersName:"OpenLayers export",foldersDesc:"Exported on "+new Date,extractAttributes:!0,kvpAttributes:!1,extractStyles:!1,extractTracks:!1,trackAttributes:null,internalns:null,features:null,styles:null,styleBaseUrl:"",fetched:null,maxDepth:0,initialize:function(a){this.regExes=
{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g,kmlColor:/(\w{2})(\w{2})(\w{2})(\w{2})/,kmlIconPalette:/root:\/\/icons\/palette-(\d+)(\.\w+)/,straightBracket:/\$\[(.*?)\]/g};this.externalProjection=new OpenLayers.Projection("EPSG:4326");OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},read:function(a){this.features=[];this.styles={};this.fetched={};return this.parseData(a,{depth:0,styleBaseUrl:this.styleBaseUrl})},parseData:function(a,b){"string"==typeof a&&
(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));for(var c=["Link","NetworkLink","Style","StyleMap","Placemark"],d=0,e=c.length;d<e;++d){var f=c[d],g=this.getElementsByTagNameNS(a,"*",f);if(0!=g.length)switch(f.toLowerCase()){case "link":case "networklink":this.parseLinks(g,b);break;case "style":this.extractStyles&&this.parseStyles(g,b);break;case "stylemap":this.extractStyles&&this.parseStyleMaps(g,b);break;case "placemark":this.parseFeatures(g,b)}}return this.features},parseLinks:function(a,
b){if(b.depth>=this.maxDepth)return!1;var c=OpenLayers.Util.extend({},b);c.depth++;for(var d=0,e=a.length;d<e;d++){var f=this.parseProperty(a[d],"*","href");f&&!this.fetched[f]&&(this.fetched[f]=!0,(f=this.fetchLink(f))&&this.parseData(f,c))}},fetchLink:function(a){if(a=OpenLayers.Request.GET({url:a,async:!1}))return a.responseText},parseStyles:function(a,b){for(var c=0,d=a.length;c<d;c++){var e=this.parseStyle(a[c]);e&&(this.styles[(b.styleBaseUrl||"")+"#"+e.id]=e)}},parseKmlColor:function(a){var b=
null;a&&(a=a.match(this.regExes.kmlColor))&&(b={color:"#"+a[4]+a[3]+a[2],opacity:parseInt(a[1],16)/255});return b},parseStyle:function(a){for(var b={},c=["LineStyle","PolyStyle","IconStyle","BalloonStyle","LabelStyle"],d,e,f=0,g=c.length;f<g;++f)if(d=c[f],e=this.getElementsByTagNameNS(a,"*",d)[0])switch(d.toLowerCase()){case "linestyle":d=this.parseProperty(e,"*","color");if(d=this.parseKmlColor(d))b.strokeColor=d.color,b.strokeOpacity=d.opacity;(d=this.parseProperty(e,"*","width"))&&(b.strokeWidth=
d);break;case "polystyle":d=this.parseProperty(e,"*","color");if(d=this.parseKmlColor(d))b.fillOpacity=d.opacity,b.fillColor=d.color;"0"==this.parseProperty(e,"*","fill")&&(b.fillColor="none");"0"==this.parseProperty(e,"*","outline")&&(b.strokeWidth="0");break;case "iconstyle":var h=parseFloat(this.parseProperty(e,"*","scale")||1);d=32*h;var k=32*h,l=this.getElementsByTagNameNS(e,"*","Icon")[0];if(l){var m=this.parseProperty(l,"*","href");if(m){var n=this.parseProperty(l,"*","w"),p=this.parseProperty(l,
"*","h");!OpenLayers.String.startsWith(m,"http://maps.google.com/mapfiles/kml")||(n||p)||(p=n=64,h/=2);n=n||p;p=p||n;n&&(d=parseInt(n)*h);p&&(k=parseInt(p)*h);if(p=m.match(this.regExes.kmlIconPalette))n=p[1],p=p[2],m=this.parseProperty(l,"*","x"),l=this.parseProperty(l,"*","y"),m="http://maps.google.com/mapfiles/kml/pal"+n+"/icon"+(8*(l?7-l/32:7)+(m?m/32:0))+p;b.graphicOpacity=1;b.externalGraphic=m}}if(e=this.getElementsByTagNameNS(e,"*","hotSpot")[0])m=parseFloat(e.getAttribute("x")),l=parseFloat(e.getAttribute("y")),
n=e.getAttribute("xunits"),"pixels"==n?b.graphicXOffset=-m*h:"insetPixels"==n?b.graphicXOffset=-d+m*h:"fraction"==n&&(b.graphicXOffset=-d*m),e=e.getAttribute("yunits"),"pixels"==e?b.graphicYOffset=-k+l*h+1:"insetPixels"==e?b.graphicYOffset=-(l*h)+1:"fraction"==e&&(b.graphicYOffset=-k*(1-l)+1);b.graphicWidth=d;b.graphicHeight=k;break;case "balloonstyle":(e=OpenLayers.Util.getXmlNodeValue(e))&&(b.balloonStyle=e.replace(this.regExes.straightBracket,"${$1}"));break;case "labelstyle":if(d=this.parseProperty(e,
"*","color"),d=this.parseKmlColor(d))b.fontColor=d.color,b.fontOpacity=d.opacity}!b.strokeColor&&b.fillColor&&(b.strokeColor=b.fillColor);(a=a.getAttribute("id"))&&b&&(b.id=a);return b},parseStyleMaps:function(a,b){for(var c=0,d=a.length;c<d;c++)for(var e=a[c],f=this.getElementsByTagNameNS(e,"*","Pair"),e=e.getAttribute("id"),g=0,h=f.length;g<h;g++){var k=f[g],l=this.parseProperty(k,"*","key");(k=this.parseProperty(k,"*","styleUrl"))&&"normal"==l&&(this.styles[(b.styleBaseUrl||"")+"#"+e]=this.styles[(b.styleBaseUrl||
"")+k])}},parseFeatures:function(a,b){for(var c=[],d=0,e=a.length;d<e;d++){var f=a[d],g=this.parseFeature.apply(this,[f]);if(g){this.extractStyles&&(g.attributes&&g.attributes.styleUrl)&&(g.style=this.getStyle(g.attributes.styleUrl,b));if(this.extractStyles){var h=this.getElementsByTagNameNS(f,"*","Style")[0];h&&(h=this.parseStyle(h))&&(g.style=OpenLayers.Util.extend(g.style,h))}this.extractTracks?(f=this.getElementsByTagNameNS(f,this.namespaces.gx,"Track"))&&0<f.length&&(g={features:[],feature:g},
this.readNode(f[0],g),0<g.features.length&&c.push.apply(c,g.features)):c.push(g)}else throw"Bad Placemark: "+d;}this.features=this.features.concat(c)},readers:{kml:{when:function(a,b){b.whens.push(OpenLayers.Date.parse(this.getChildValue(a)))},_trackPointAttribute:function(a,b){var c=a.nodeName.split(":").pop();b.attributes[c].push(this.getChildValue(a))}},gx:{Track:function(a,b){var c={whens:[],points:[],angles:[]};if(this.trackAttributes){var d;c.attributes={};for(var e=0,f=this.trackAttributes.length;e<
f;++e)d=this.trackAttributes[e],c.attributes[d]=[],d in this.readers.kml||(this.readers.kml[d]=this.readers.kml._trackPointAttribute)}this.readChildNodes(a,c);if(c.whens.length!==c.points.length)throw Error("gx:Track with unequal number of when ("+c.whens.length+") and gx:coord ("+c.points.length+") elements.");var g=0<c.angles.length;if(g&&c.whens.length!==c.angles.length)throw Error("gx:Track with unequal number of when ("+c.whens.length+") and gx:angles ("+c.angles.length+") elements.");for(var h,
e=0,f=c.whens.length;e<f;++e){h=b.feature.clone();h.fid=b.feature.fid||b.feature.id;d=c.points[e];h.geometry=d;"z"in d&&(h.attributes.altitude=d.z);this.internalProjection&&this.externalProjection&&h.geometry.transform(this.externalProjection,this.internalProjection);if(this.trackAttributes)for(var k=0,l=this.trackAttributes.length;k<l;++k)d=this.trackAttributes[k],h.attributes[d]=c.attributes[d][e];h.attributes.when=c.whens[e];h.attributes.trackId=b.feature.id;g&&(d=c.angles[e],h.attributes.heading=
parseFloat(d[0]),h.attributes.tilt=parseFloat(d[1]),h.attributes.roll=parseFloat(d[2]));b.features.push(h)}},coord:function(a,b){var c=this.getChildValue(a).replace(this.regExes.trimSpace,"").split(/\s+/),d=new OpenLayers.Geometry.Point(c[0],c[1]);2<c.length&&(d.z=parseFloat(c[2]));b.points.push(d)},angles:function(a,b){var c=this.getChildValue(a).replace(this.regExes.trimSpace,"").split(/\s+/);b.angles.push(c)}}},parseFeature:function(a){for(var b=["MultiGeometry","Polygon","LineString","Point"],
c,d,e,f=0,g=b.length;f<g;++f)if(c=b[f],this.internalns=a.namespaceURI?a.namespaceURI:this.kmlns,d=this.getElementsByTagNameNS(a,this.internalns,c),0<d.length){if(b=this.parseGeometry[c.toLowerCase()])e=b.apply(this,[d[0]]),this.internalProjection&&this.externalProjection&&e.transform(this.externalProjection,this.internalProjection);else throw new TypeError("Unsupported geometry type: "+c);break}var h;this.extractAttributes&&(h=this.parseAttributes(a));c=new OpenLayers.Feature.Vector(e,h);a=a.getAttribute("id")||
a.getAttribute("name");null!=a&&(c.fid=a);return c},getStyle:function(a,b){var c=OpenLayers.Util.removeTail(a),d=OpenLayers.Util.extend({},b);d.depth++;d.styleBaseUrl=c;!this.styles[a]&&!OpenLayers.String.startsWith(a,"#")&&d.depth<=this.maxDepth&&!this.fetched[c]&&(c=this.fetchLink(c))&&this.parseData(c,d);return OpenLayers.Util.extend({},this.styles[a])},parseGeometry:{point:function(a){var b=this.getElementsByTagNameNS(a,this.internalns,"coordinates");a=[];if(0<b.length){var c=b[0].firstChild.nodeValue,
c=c.replace(this.regExes.removeSpace,"");a=c.split(",")}b=null;if(1<a.length)2==a.length&&(a[2]=null),b=new OpenLayers.Geometry.Point(a[0],a[1],a[2]);else throw"Bad coordinate string: "+c;return b},linestring:function(a,b){var c=this.getElementsByTagNameNS(a,this.internalns,"coordinates"),d=null;if(0<c.length){for(var c=this.getChildValue(c[0]),c=c.replace(this.regExes.trimSpace,""),c=c.replace(this.regExes.trimComma,","),d=c.split(this.regExes.splitSpace),e=d.length,f=Array(e),g,h,k=0;k<e;++k)if(g=
d[k].split(","),h=g.length,1<h)2==g.length&&(g[2]=null),f[k]=new OpenLayers.Geometry.Point(g[0],g[1],g[2]);else throw"Bad LineString point coordinates: "+d[k];if(e)d=b?new OpenLayers.Geometry.LinearRing(f):new OpenLayers.Geometry.LineString(f);else throw"Bad LineString coordinates: "+c;}return d},polygon:function(a){a=this.getElementsByTagNameNS(a,this.internalns,"LinearRing");var b=a.length,c=Array(b);if(0<b)for(var d=0,e=a.length;d<e;++d)if(b=this.parseGeometry.linestring.apply(this,[a[d],!0]))c[d]=
b;else throw"Bad LinearRing geometry: "+d;return new OpenLayers.Geometry.Polygon(c)},multigeometry:function(a){for(var b,c=[],d=a.childNodes,e=0,f=d.length;e<f;++e)a=d[e],1==a.nodeType&&(b=a.prefix?a.nodeName.split(":")[1]:a.nodeName,(b=this.parseGeometry[b.toLowerCase()])&&c.push(b.apply(this,[a])));return new OpenLayers.Geometry.Collection(c)}},parseAttributes:function(a){var b={},c=a.getElementsByTagName("ExtendedData");c.length&&(b=this.parseExtendedData(c[0]));var d,e,f;a=a.childNodes;for(var c=
0,g=a.length;c<g;++c)if(d=a[c],1==d.nodeType&&(e=d.childNodes,1<=e.length&&3>=e.length)){switch(e.length){case 1:f=e[0];break;case 2:f=e[0];e=e[1];f=3==f.nodeType||4==f.nodeType?f:e;break;default:f=e[1]}if(3==f.nodeType||4==f.nodeType)if(d=d.prefix?d.nodeName.split(":")[1]:d.nodeName,f=OpenLayers.Util.getXmlNodeValue(f))f=f.replace(this.regExes.trimSpace,""),b[d]=f}return b},parseExtendedData:function(a){var b={},c,d,e,f,g=a.getElementsByTagName("Data");c=0;for(d=g.length;c<d;c++){e=g[c];f=e.getAttribute("name");
var h={},k=e.getElementsByTagName("value");k.length&&(h.value=this.getChildValue(k[0]));this.kvpAttributes?b[f]=h.value:(e=e.getElementsByTagName("displayName"),e.length&&(h.displayName=this.getChildValue(e[0])),b[f]=h)}a=a.getElementsByTagName("SimpleData");c=0;for(d=a.length;c<d;c++)h={},e=a[c],f=e.getAttribute("name"),h.value=this.getChildValue(e),this.kvpAttributes?b[f]=h.value:(h.displayName=f,b[f]=h);return b},parseProperty:function(a,b,c){var d;a=this.getElementsByTagNameNS(a,b,c);try{d=OpenLayers.Util.getXmlNodeValue(a[0])}catch(e){d=
null}return d},write:function(a){OpenLayers.Util.isArray(a)||(a=[a]);for(var b=this.createElementNS(this.kmlns,"kml"),c=this.createFolderXML(),d=0,e=a.length;d<e;++d)c.appendChild(this.createPlacemarkXML(a[d]));b.appendChild(c);return OpenLayers.Format.XML.prototype.write.apply(this,[b])},createFolderXML:function(){var a=this.createElementNS(this.kmlns,"Folder");if(this.foldersName){var b=this.createElementNS(this.kmlns,"name"),c=this.createTextNode(this.foldersName);b.appendChild(c);a.appendChild(b)}this.foldersDesc&&
(b=this.createElementNS(this.kmlns,"description"),c=this.createTextNode(this.foldersDesc),b.appendChild(c),a.appendChild(b));return a},createPlacemarkXML:function(a){var b=this.createElementNS(this.kmlns,"name"),c=a.style&&a.style.label?a.style.label:a.id;b.appendChild(this.createTextNode(a.attributes.name||c));var d=this.createElementNS(this.kmlns,"description");d.appendChild(this.createTextNode(a.attributes.description||this.placemarksDesc));c=this.createElementNS(this.kmlns,"Placemark");null!=
a.fid&&c.setAttribute("id",a.fid);c.appendChild(b);c.appendChild(d);b=this.buildGeometryNode(a.geometry);c.appendChild(b);a.attributes&&(a=this.buildExtendedData(a.attributes))&&c.appendChild(a);return c},buildGeometryNode:function(a){var b=a.CLASS_NAME,b=b.substring(b.lastIndexOf(".")+1),b=this.buildGeometry[b.toLowerCase()],c=null;b&&(c=b.apply(this,[a]));return c},buildGeometry:{point:function(a){var b=this.createElementNS(this.kmlns,"Point");b.appendChild(this.buildCoordinatesNode(a));return b},
multipoint:function(a){return this.buildGeometry.collection.apply(this,[a])},linestring:function(a){var b=this.createElementNS(this.kmlns,"LineString");b.appendChild(this.buildCoordinatesNode(a));return b},multilinestring:function(a){return this.buildGeometry.collection.apply(this,[a])},linearring:function(a){var b=this.createElementNS(this.kmlns,"LinearRing");b.appendChild(this.buildCoordinatesNode(a));return b},polygon:function(a){var b=this.createElementNS(this.kmlns,"Polygon");a=a.components;
for(var c,d,e=0,f=a.length;e<f;++e)c=0==e?"outerBoundaryIs":"innerBoundaryIs",c=this.createElementNS(this.kmlns,c),d=this.buildGeometry.linearring.apply(this,[a[e]]),c.appendChild(d),b.appendChild(c);return b},multipolygon:function(a){return this.buildGeometry.collection.apply(this,[a])},collection:function(a){for(var b=this.createElementNS(this.kmlns,"MultiGeometry"),c,d=0,e=a.components.length;d<e;++d)(c=this.buildGeometryNode.apply(this,[a.components[d]]))&&b.appendChild(c);return b}},buildCoordinatesNode:function(a){var b=
this.createElementNS(this.kmlns,"coordinates"),c;if(c=a.components){for(var d=c.length,e=Array(d),f=0;f<d;++f)a=c[f],e[f]=this.buildCoordinates(a);c=e.join(" ")}else c=this.buildCoordinates(a);c=this.createTextNode(c);b.appendChild(c);return b},buildCoordinates:function(a){this.internalProjection&&this.externalProjection&&(a=a.clone(),a.transform(this.internalProjection,this.externalProjection));return a.x+","+a.y},buildExtendedData:function(a){var b=this.createElementNS(this.kmlns,"ExtendedData"),
c;for(c in a)if(a[c]&&"name"!=c&&"description"!=c&&"styleUrl"!=c){var d=this.createElementNS(this.kmlns,"Data");d.setAttribute("name",c);var e=this.createElementNS(this.kmlns,"value");if("object"==typeof a[c]){if(a[c].value&&e.appendChild(this.createTextNode(a[c].value)),a[c].displayName){var f=this.createElementNS(this.kmlns,"displayName");f.appendChild(this.getXMLDoc().createCDATASection(a[c].displayName));d.appendChild(f)}}else e.appendChild(this.createTextNode(a[c]));d.appendChild(e);b.appendChild(d)}return this.isSimpleContent(b)?
null:b},CLASS_NAME:"OpenLayers.Format.KML"});OpenLayers.Format.WMSCapabilities=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.1.1",profile:null,CLASS_NAME:"OpenLayers.Format.WMSCapabilities"});OpenLayers.Format.WMSCapabilities.v1=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{wms:"http://www.opengis.net/wms",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},defaultPrefix:"wms",read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));var b=a;a&&9==a.nodeType&&(a=a.documentElement);var c={};this.readNode(a,c);void 0===c.service&&(a=new OpenLayers.Format.OGCExceptionReport,c.error=a.read(b));return c},readers:{wms:{Service:function(a,
b){b.service={};this.readChildNodes(a,b.service)},Name:function(a,b){b.name=this.getChildValue(a)},Title:function(a,b){b.title=this.getChildValue(a)},Abstract:function(a,b){b["abstract"]=this.getChildValue(a)},BoundingBox:function(a,b){var c={};c.bbox=[parseFloat(a.getAttribute("minx")),parseFloat(a.getAttribute("miny")),parseFloat(a.getAttribute("maxx")),parseFloat(a.getAttribute("maxy"))];var d={x:parseFloat(a.getAttribute("resx")),y:parseFloat(a.getAttribute("resy"))};isNaN(d.x)&&isNaN(d.y)||(c.res=
d);return c},OnlineResource:function(a,b){b.href=this.getAttributeNS(a,this.namespaces.xlink,"href")},ContactInformation:function(a,b){b.contactInformation={};this.readChildNodes(a,b.contactInformation)},ContactPersonPrimary:function(a,b){b.personPrimary={};this.readChildNodes(a,b.personPrimary)},ContactPerson:function(a,b){b.person=this.getChildValue(a)},ContactOrganization:function(a,b){b.organization=this.getChildValue(a)},ContactPosition:function(a,b){b.position=this.getChildValue(a)},ContactAddress:function(a,
b){b.contactAddress={};this.readChildNodes(a,b.contactAddress)},AddressType:function(a,b){b.type=this.getChildValue(a)},Address:function(a,b){b.address=this.getChildValue(a)},City:function(a,b){b.city=this.getChildValue(a)},StateOrProvince:function(a,b){b.stateOrProvince=this.getChildValue(a)},PostCode:function(a,b){b.postcode=this.getChildValue(a)},Country:function(a,b){b.country=this.getChildValue(a)},ContactVoiceTelephone:function(a,b){b.phone=this.getChildValue(a)},ContactFacsimileTelephone:function(a,
b){b.fax=this.getChildValue(a)},ContactElectronicMailAddress:function(a,b){b.email=this.getChildValue(a)},Fees:function(a,b){var c=this.getChildValue(a);c&&"none"!=c.toLowerCase()&&(b.fees=c)},AccessConstraints:function(a,b){var c=this.getChildValue(a);c&&"none"!=c.toLowerCase()&&(b.accessConstraints=c)},Capability:function(a,b){b.capability={nestedLayers:[],layers:[]};this.readChildNodes(a,b.capability)},Request:function(a,b){b.request={};this.readChildNodes(a,b.request)},GetCapabilities:function(a,
b){b.getcapabilities={formats:[]};this.readChildNodes(a,b.getcapabilities)},Format:function(a,b){OpenLayers.Util.isArray(b.formats)?b.formats.push(this.getChildValue(a)):b.format=this.getChildValue(a)},DCPType:function(a,b){this.readChildNodes(a,b)},HTTP:function(a,b){this.readChildNodes(a,b)},Get:function(a,b){b.get={};this.readChildNodes(a,b.get);b.href||(b.href=b.get.href)},Post:function(a,b){b.post={};this.readChildNodes(a,b.post);b.href||(b.href=b.get.href)},GetMap:function(a,b){b.getmap={formats:[]};
this.readChildNodes(a,b.getmap)},GetFeatureInfo:function(a,b){b.getfeatureinfo={formats:[]};this.readChildNodes(a,b.getfeatureinfo)},Exception:function(a,b){b.exception={formats:[]};this.readChildNodes(a,b.exception)},Layer:function(a,b){var c,d;b.capability?(d=b.capability,c=b):d=b;var e=a.getAttributeNode("queryable"),f=e&&e.specified?a.getAttribute("queryable"):null,g=(e=a.getAttributeNode("cascaded"))&&e.specified?a.getAttribute("cascaded"):null,e=(e=a.getAttributeNode("opaque"))&&e.specified?
a.getAttribute("opaque"):null,h=a.getAttribute("noSubsets"),k=a.getAttribute("fixedWidth"),l=a.getAttribute("fixedHeight"),m=c||{},n=OpenLayers.Util.extend;c={nestedLayers:[],styles:c?[].concat(c.styles):[],srs:c?n({},m.srs):{},metadataURLs:[],bbox:c?n({},m.bbox):{},llbbox:m.llbbox,dimensions:c?n({},m.dimensions):{},authorityURLs:c?n({},m.authorityURLs):{},identifiers:{},keywords:[],queryable:f&&""!==f?"1"===f||"true"===f:m.queryable||!1,cascaded:null!==g?parseInt(g):m.cascaded||0,opaque:e?"1"===
e||"true"===e:m.opaque||!1,noSubsets:null!==h?"1"===h||"true"===h:m.noSubsets||!1,fixedWidth:null!=k?parseInt(k):m.fixedWidth||0,fixedHeight:null!=l?parseInt(l):m.fixedHeight||0,minScale:m.minScale,maxScale:m.maxScale,attribution:m.attribution};b.nestedLayers.push(c);c.capability=d;this.readChildNodes(a,c);delete c.capability;c.name&&(f=c.name.split(":"),g=d.request,e=g.getfeatureinfo,0<f.length&&(c.prefix=f[0]),d.layers.push(c),void 0===c.formats&&(c.formats=g.getmap.formats),void 0===c.infoFormats&&
e&&(c.infoFormats=e.formats))},Attribution:function(a,b){b.attribution={};this.readChildNodes(a,b.attribution)},LogoURL:function(a,b){b.logo={width:a.getAttribute("width"),height:a.getAttribute("height")};this.readChildNodes(a,b.logo)},Style:function(a,b){var c={};b.styles.push(c);this.readChildNodes(a,c)},LegendURL:function(a,b){var c={width:a.getAttribute("width"),height:a.getAttribute("height")};b.legend=c;this.readChildNodes(a,c)},MetadataURL:function(a,b){var c={type:a.getAttribute("type")};
b.metadataURLs.push(c);this.readChildNodes(a,c)},DataURL:function(a,b){b.dataURL={};this.readChildNodes(a,b.dataURL)},FeatureListURL:function(a,b){b.featureListURL={};this.readChildNodes(a,b.featureListURL)},AuthorityURL:function(a,b){var c=a.getAttribute("name"),d={};this.readChildNodes(a,d);b.authorityURLs[c]=d.href},Identifier:function(a,b){var c=a.getAttribute("authority");b.identifiers[c]=this.getChildValue(a)},KeywordList:function(a,b){this.readChildNodes(a,b)},SRS:function(a,b){b.srs[this.getChildValue(a)]=
!0}}},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1"});OpenLayers.Format.WMSCapabilities.v1_1=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1,{readers:{wms:OpenLayers.Util.applyDefaults({WMT_MS_Capabilities:function(a,b){this.readChildNodes(a,b)},Keyword:function(a,b){b.keywords&&b.keywords.push(this.getChildValue(a))},DescribeLayer:function(a,b){b.describelayer={formats:[]};this.readChildNodes(a,b.describelayer)},GetLegendGraphic:function(a,b){b.getlegendgraphic={formats:[]};this.readChildNodes(a,b.getlegendgraphic)},GetStyles:function(a,b){b.getstyles=
{formats:[]};this.readChildNodes(a,b.getstyles)},PutStyles:function(a,b){b.putstyles={formats:[]};this.readChildNodes(a,b.putstyles)},UserDefinedSymbolization:function(a,b){var c={supportSLD:1==parseInt(a.getAttribute("SupportSLD")),userLayer:1==parseInt(a.getAttribute("UserLayer")),userStyle:1==parseInt(a.getAttribute("UserStyle")),remoteWFS:1==parseInt(a.getAttribute("RemoteWFS"))};b.userSymbols=c},LatLonBoundingBox:function(a,b){b.llbbox=[parseFloat(a.getAttribute("minx")),parseFloat(a.getAttribute("miny")),
parseFloat(a.getAttribute("maxx")),parseFloat(a.getAttribute("maxy"))]},BoundingBox:function(a,b){var c=OpenLayers.Format.WMSCapabilities.v1.prototype.readers.wms.BoundingBox.apply(this,[a,b]);c.srs=a.getAttribute("SRS");b.bbox[c.srs]=c},ScaleHint:function(a,b){var c=a.getAttribute("min"),d=a.getAttribute("max"),e=Math.pow(2,0.5),f=OpenLayers.INCHES_PER_UNIT.m;0!=c&&(b.maxScale=parseFloat((c/e*f*OpenLayers.DOTS_PER_INCH).toPrecision(13)));d!=Number.POSITIVE_INFINITY&&(b.minScale=parseFloat((d/e*f*
OpenLayers.DOTS_PER_INCH).toPrecision(13)))},Dimension:function(a,b){var c={name:a.getAttribute("name").toLowerCase(),units:a.getAttribute("units"),unitsymbol:a.getAttribute("unitSymbol")};b.dimensions[c.name]=c},Extent:function(a,b){var c=a.getAttribute("name").toLowerCase();if(c in b.dimensions){c=b.dimensions[c];c.nearestVal="1"===a.getAttribute("nearestValue");c.multipleVal="1"===a.getAttribute("multipleValues");c.current="1"===a.getAttribute("current");c["default"]=a.getAttribute("default")||
"";var d=this.getChildValue(a);c.values=d.split(",")}}},OpenLayers.Format.WMSCapabilities.v1.prototype.readers.wms)},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_1"});OpenLayers.Format.WMSCapabilities.v1_1_0=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1,{version:"1.1.0",readers:{wms:OpenLayers.Util.applyDefaults({SRS:function(a,b){for(var c=this.getChildValue(a).split(/ +/),d=0,e=c.length;d<e;d++)b.srs[c[d]]=!0}},OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers.wms)},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_1_0"});OpenLayers.Protocol.WFS.v1=OpenLayers.Class(OpenLayers.Protocol,{version:null,srsName:"EPSG:4326",featureType:null,featureNS:null,geometryName:"the_geom",schema:null,featurePrefix:"feature",formatOptions:null,readFormat:null,readOptions:null,initialize:function(a){OpenLayers.Protocol.prototype.initialize.apply(this,[a]);a.format||(this.format=OpenLayers.Format.WFST(OpenLayers.Util.extend({version:this.version,featureType:this.featureType,featureNS:this.featureNS,featurePrefix:this.featurePrefix,geometryName:this.geometryName,
srsName:this.srsName,schema:this.schema},this.formatOptions)));!a.geometryName&&1<parseFloat(this.format.version)&&this.setGeometryName(null)},destroy:function(){this.options&&!this.options.format&&this.format.destroy();this.format=null;OpenLayers.Protocol.prototype.destroy.apply(this)},read:function(a){OpenLayers.Protocol.prototype.read.apply(this,arguments);a=OpenLayers.Util.extend({},a);OpenLayers.Util.applyDefaults(a,this.options||{});var b=new OpenLayers.Protocol.Response({requestType:"read"}),
c=OpenLayers.Format.XML.prototype.write.apply(this.format,[this.format.writeNode("wfs:GetFeature",a)]);b.priv=OpenLayers.Request.POST({url:a.url,callback:this.createCallback(this.handleRead,b,a),params:a.params,headers:a.headers,data:c});return b},setFeatureType:function(a){this.featureType=a;this.format.featureType=a},setGeometryName:function(a){this.geometryName=a;this.format.geometryName=a},handleRead:function(a,b){b=OpenLayers.Util.extend({},b);OpenLayers.Util.applyDefaults(b,this.options);if(b.callback){var c=
a.priv;200<=c.status&&300>c.status?(c=this.parseResponse(c,b.readOptions))&&!1!==c.success?(b.readOptions&&"object"==b.readOptions.output?OpenLayers.Util.extend(a,c):a.features=c,a.code=OpenLayers.Protocol.Response.SUCCESS):(a.code=OpenLayers.Protocol.Response.FAILURE,a.error=c):a.code=OpenLayers.Protocol.Response.FAILURE;b.callback.call(b.scope,a)}},parseResponse:function(a,b){var c=a.responseXML;c&&c.documentElement||(c=a.responseText);if(!c||0>=c.length)return null;c=null!==this.readFormat?this.readFormat.read(c):
this.format.read(c,b);if(!this.featureNS){var d=this.readFormat||this.format;this.featureNS=d.featureNS;d.autoConfig=!1;this.geometryName||this.setGeometryName(d.geometryName)}return c},commit:function(a,b){b=OpenLayers.Util.extend({},b);OpenLayers.Util.applyDefaults(b,this.options);var c=new OpenLayers.Protocol.Response({requestType:"commit",reqFeatures:a});c.priv=OpenLayers.Request.POST({url:b.url,headers:b.headers,data:this.format.write(a,b),callback:this.createCallback(this.handleCommit,c,b)});
return c},handleCommit:function(a,b){if(b.callback){var c=a.priv,d=c.responseXML;d&&d.documentElement||(d=c.responseText);c=this.format.read(d)||{};a.insertIds=c.insertIds||[];c.success?a.code=OpenLayers.Protocol.Response.SUCCESS:(a.code=OpenLayers.Protocol.Response.FAILURE,a.error=c);b.callback.call(b.scope,a)}},filterDelete:function(a,b){b=OpenLayers.Util.extend({},b);OpenLayers.Util.applyDefaults(b,this.options);new OpenLayers.Protocol.Response({requestType:"commit"});var c=this.format.createElementNSPlus("wfs:Transaction",
{attributes:{service:"WFS",version:this.version}}),d=this.format.createElementNSPlus("wfs:Delete",{attributes:{typeName:(b.featureNS?this.featurePrefix+":":"")+b.featureType}});b.featureNS&&d.setAttribute("xmlns:"+this.featurePrefix,b.featureNS);var e=this.format.writeNode("ogc:Filter",a);d.appendChild(e);c.appendChild(d);c=OpenLayers.Format.XML.prototype.write.apply(this.format,[c]);return OpenLayers.Request.POST({url:this.url,callback:b.callback||function(){},data:c})},abort:function(a){a&&a.priv.abort()},
CLASS_NAME:"OpenLayers.Protocol.WFS.v1"});OpenLayers.Handler.Feature=OpenLayers.Class(OpenLayers.Handler,{EVENTMAP:{click:{"in":"click",out:"clickout"},mousemove:{"in":"over",out:"out"},dblclick:{"in":"dblclick",out:null},mousedown:{"in":null,out:null},mouseup:{"in":null,out:null},touchstart:{"in":"click",out:"clickout"}},feature:null,lastFeature:null,down:null,up:null,clickTolerance:4,geometryTypes:null,stopClick:!0,stopDown:!0,stopUp:!1,initialize:function(a,b,c,d){OpenLayers.Handler.prototype.initialize.apply(this,[a,c,d]);this.layer=
b},touchstart:function(a){this.startTouch();return OpenLayers.Event.isMultiTouch(a)?!0:this.mousedown(a)},touchmove:function(a){OpenLayers.Event.preventDefault(a)},mousedown:function(a){if(OpenLayers.Event.isLeftClick(a)||OpenLayers.Event.isSingleTouch(a))this.down=a.xy;return this.handle(a)?!this.stopDown:!0},mouseup:function(a){this.up=a.xy;return this.handle(a)?!this.stopUp:!0},click:function(a){return this.handle(a)?!this.stopClick:!0},mousemove:function(a){if(!this.callbacks.over&&!this.callbacks.out)return!0;
this.handle(a);return!0},dblclick:function(a){return!this.handle(a)},geometryTypeMatches:function(a){return null==this.geometryTypes||-1<OpenLayers.Util.indexOf(this.geometryTypes,a.geometry.CLASS_NAME)},handle:function(a){this.feature&&!this.feature.layer&&(this.feature=null);var b=a.type,c=!1,d=!!this.feature,e="click"==b||"dblclick"==b||"touchstart"==b;(this.feature=this.layer.getFeatureFromEvent(a))&&!this.feature.layer&&(this.feature=null);this.lastFeature&&!this.lastFeature.layer&&(this.lastFeature=
null);this.feature?("touchstart"===b&&OpenLayers.Event.preventDefault(a),a=this.feature!=this.lastFeature,this.geometryTypeMatches(this.feature)?(d&&a?(this.lastFeature&&this.triggerCallback(b,"out",[this.lastFeature]),this.triggerCallback(b,"in",[this.feature])):d&&!e||this.triggerCallback(b,"in",[this.feature]),this.lastFeature=this.feature,c=!0):(this.lastFeature&&(d&&a||e)&&this.triggerCallback(b,"out",[this.lastFeature]),this.feature=null)):this.lastFeature&&(d||e)&&this.triggerCallback(b,"out",
[this.lastFeature]);return c},triggerCallback:function(a,b,c){if(b=this.EVENTMAP[a][b])"click"==a&&this.up&&this.down?(Math.sqrt(Math.pow(this.up.x-this.down.x,2)+Math.pow(this.up.y-this.down.y,2))<=this.clickTolerance&&this.callback(b,c),this.up=this.down=null):this.callback(b,c)},activate:function(){var a=!1;OpenLayers.Handler.prototype.activate.apply(this,arguments)&&(this.moveLayerToTop(),this.map.events.on({removelayer:this.handleMapEvents,changelayer:this.handleMapEvents,scope:this}),a=!0);
return a},deactivate:function(){var a=!1;OpenLayers.Handler.prototype.deactivate.apply(this,arguments)&&(this.moveLayerBack(),this.up=this.down=this.lastFeature=this.feature=null,this.map.events.un({removelayer:this.handleMapEvents,changelayer:this.handleMapEvents,scope:this}),a=!0);return a},handleMapEvents:function(a){"removelayer"!=a.type&&"order"!=a.property||this.moveLayerToTop()},moveLayerToTop:function(){var a=Math.max(this.map.Z_INDEX_BASE.Feature-1,this.layer.getZIndex())+1;this.layer.setZIndex(a)},
moveLayerBack:function(){var a=this.layer.getZIndex()-1;a>=this.map.Z_INDEX_BASE.Feature?this.layer.setZIndex(a):this.map.setLayerZIndex(this.layer,this.map.getLayerIndex(this.layer))},CLASS_NAME:"OpenLayers.Handler.Feature"});OpenLayers.Layer.Vector.RootContainer=OpenLayers.Class(OpenLayers.Layer.Vector,{displayInLayerSwitcher:!1,layers:null,display:function(){},getFeatureFromEvent:function(a){for(var b=this.layers,c,d=0;d<b.length;d++)if(c=b[d].getFeatureFromEvent(a))return c},setMap:function(a){OpenLayers.Layer.Vector.prototype.setMap.apply(this,arguments);this.collectRoots();a.events.register("changelayer",this,this.handleChangeLayer)},removeMap:function(a){a.events.unregister("changelayer",this,this.handleChangeLayer);
this.resetRoots();OpenLayers.Layer.Vector.prototype.removeMap.apply(this,arguments)},collectRoots:function(){for(var a,b=0;b<this.map.layers.length;++b)a=this.map.layers[b],-1!=OpenLayers.Util.indexOf(this.layers,a)&&a.renderer.moveRoot(this.renderer)},resetRoots:function(){for(var a,b=0;b<this.layers.length;++b)a=this.layers[b],this.renderer&&a.renderer.getRenderLayerId()==this.id&&this.renderer.moveRoot(a.renderer)},handleChangeLayer:function(a){var b=a.layer;"order"==a.property&&-1!=OpenLayers.Util.indexOf(this.layers,
b)&&(this.resetRoots(),this.collectRoots())},CLASS_NAME:"OpenLayers.Layer.Vector.RootContainer"});OpenLayers.Control.SelectFeature=OpenLayers.Class(OpenLayers.Control,{multipleKey:null,toggleKey:null,multiple:!1,clickout:!0,toggle:!1,hover:!1,highlightOnly:!1,box:!1,onBeforeSelect:function(){},onSelect:function(){},onUnselect:function(){},scope:null,geometryTypes:null,layer:null,layers:null,callbacks:null,selectStyle:null,renderIntent:"select",handlers:null,initialize:function(a,b){OpenLayers.Control.prototype.initialize.apply(this,[b]);null===this.scope&&(this.scope=this);this.initLayer(a);var c=
{click:this.clickFeature,clickout:this.clickoutFeature};this.hover&&(c.over=this.overFeature,c.out=this.outFeature);this.callbacks=OpenLayers.Util.extend(c,this.callbacks);this.handlers={feature:new OpenLayers.Handler.Feature(this,this.layer,this.callbacks,{geometryTypes:this.geometryTypes})};this.box&&(this.handlers.box=new OpenLayers.Handler.Box(this,{done:this.selectBox},{boxDivClassName:"olHandlerBoxSelectFeature"}))},initLayer:function(a){OpenLayers.Util.isArray(a)?(this.layers=a,this.layer=
new OpenLayers.Layer.Vector.RootContainer(this.id+"_container",{layers:a})):this.layer=a},destroy:function(){this.active&&this.layers&&this.map.removeLayer(this.layer);OpenLayers.Control.prototype.destroy.apply(this,arguments);this.layers&&this.layer.destroy()},activate:function(){this.active||(this.layers&&this.map.addLayer(this.layer),this.handlers.feature.activate(),this.box&&this.handlers.box&&this.handlers.box.activate());return OpenLayers.Control.prototype.activate.apply(this,arguments)},deactivate:function(){this.active&&
(this.handlers.feature.deactivate(),this.handlers.box&&this.handlers.box.deactivate(),this.layers&&this.map.removeLayer(this.layer));return OpenLayers.Control.prototype.deactivate.apply(this,arguments)},unselectAll:function(a){var b=this.layers||[this.layer],c,d,e,f;for(e=0;e<b.length;++e)if(c=b[e],f=0,null!=c.selectedFeatures)for(;c.selectedFeatures.length>f;)d=c.selectedFeatures[f],a&&a.except==d?++f:this.unselect(d)},clickFeature:function(a){this.hover||(-1<OpenLayers.Util.indexOf(a.layer.selectedFeatures,
a)?this.toggleSelect()?this.unselect(a):this.multipleSelect()||this.unselectAll({except:a}):(this.multipleSelect()||this.unselectAll({except:a}),this.select(a)))},multipleSelect:function(){return this.multiple||this.handlers.feature.evt&&this.handlers.feature.evt[this.multipleKey]},toggleSelect:function(){return this.toggle||this.handlers.feature.evt&&this.handlers.feature.evt[this.toggleKey]},clickoutFeature:function(a){!this.hover&&this.clickout&&this.unselectAll()},overFeature:function(a){var b=
a.layer;this.hover&&(this.highlightOnly?this.highlight(a):-1==OpenLayers.Util.indexOf(b.selectedFeatures,a)&&this.select(a))},outFeature:function(a){if(this.hover)if(this.highlightOnly){if(a._lastHighlighter==this.id)if(a._prevHighlighter&&a._prevHighlighter!=this.id){delete a._lastHighlighter;var b=this.map.getControl(a._prevHighlighter);b&&b.highlight(a)}else this.unhighlight(a)}else this.unselect(a)},highlight:function(a){var b=a.layer;!1!==this.events.triggerEvent("beforefeaturehighlighted",{feature:a})&&
(a._prevHighlighter=a._lastHighlighter,a._lastHighlighter=this.id,b.drawFeature(a,this.selectStyle||this.renderIntent),this.events.triggerEvent("featurehighlighted",{feature:a}))},unhighlight:function(a){var b=a.layer;void 0==a._prevHighlighter?delete a._lastHighlighter:(a._prevHighlighter!=this.id&&(a._lastHighlighter=a._prevHighlighter),delete a._prevHighlighter);b.drawFeature(a,a.style||a.layer.style||"default");this.events.triggerEvent("featureunhighlighted",{feature:a})},select:function(a){var b=
this.onBeforeSelect.call(this.scope,a),c=a.layer;!1!==b&&(b=c.events.triggerEvent("beforefeatureselected",{feature:a}),!1!==b&&(c.selectedFeatures.push(a),this.highlight(a),this.handlers.feature.lastFeature||(this.handlers.feature.lastFeature=c.selectedFeatures[0]),c.events.triggerEvent("featureselected",{feature:a}),this.onSelect.call(this.scope,a)))},unselect:function(a){var b=a.layer;this.unhighlight(a);OpenLayers.Util.removeItem(b.selectedFeatures,a);b.events.triggerEvent("featureunselected",
{feature:a});this.onUnselect.call(this.scope,a)},selectBox:function(a){if(a instanceof OpenLayers.Bounds){var b=this.map.getLonLatFromPixel({x:a.left,y:a.bottom});a=this.map.getLonLatFromPixel({x:a.right,y:a.top});b=new OpenLayers.Bounds(b.lon,b.lat,a.lon,a.lat);this.multipleSelect()||this.unselectAll();a=this.multiple;this.multiple=!0;var c=this.layers||[this.layer];this.events.triggerEvent("boxselectionstart",{layers:c});for(var d,e=0;e<c.length;++e){d=c[e];for(var f=0,g=d.features.length;f<g;++f){var h=
d.features[f];h.getVisibility()&&(null==this.geometryTypes||-1<OpenLayers.Util.indexOf(this.geometryTypes,h.geometry.CLASS_NAME))&&b.toGeometry().intersects(h.geometry)&&-1==OpenLayers.Util.indexOf(d.selectedFeatures,h)&&this.select(h)}}this.multiple=a;this.events.triggerEvent("boxselectionend",{layers:c})}},setMap:function(a){this.handlers.feature.setMap(a);this.box&&this.handlers.box.setMap(a);OpenLayers.Control.prototype.setMap.apply(this,arguments)},setLayer:function(a){var b=this.active;this.unselectAll();
this.deactivate();this.layers&&(this.layer.destroy(),this.layers=null);this.initLayer(a);this.handlers.feature.layer=this.layer;b&&this.activate()},CLASS_NAME:"OpenLayers.Control.SelectFeature"});OpenLayers.Handler.Point=OpenLayers.Class(OpenLayers.Handler,{point:null,layer:null,multi:!1,citeCompliant:!1,mouseDown:!1,stoppedDown:null,lastDown:null,lastUp:null,persist:!1,stopDown:!1,stopUp:!1,layerOptions:null,pixelTolerance:5,lastTouchPx:null,initialize:function(a,b,c){c&&c.layerOptions&&c.layerOptions.styleMap||(this.style=OpenLayers.Util.extend(OpenLayers.Feature.Vector.style["default"],{}));OpenLayers.Handler.prototype.initialize.apply(this,arguments)},activate:function(){if(!OpenLayers.Handler.prototype.activate.apply(this,
arguments))return!1;var a=OpenLayers.Util.extend({displayInLayerSwitcher:!1,calculateInRange:OpenLayers.Function.True,wrapDateLine:this.citeCompliant},this.layerOptions);this.layer=new OpenLayers.Layer.Vector(this.CLASS_NAME,a);this.map.addLayer(this.layer);return!0},createFeature:function(a){a=this.layer.getLonLatFromViewPortPx(a);a=new OpenLayers.Geometry.Point(a.lon,a.lat);this.point=new OpenLayers.Feature.Vector(a);this.callback("create",[this.point.geometry,this.point]);this.point.geometry.clearBounds();
this.layer.addFeatures([this.point],{silent:!0})},deactivate:function(){if(!OpenLayers.Handler.prototype.deactivate.apply(this,arguments))return!1;this.cancel();null!=this.layer.map&&(this.destroyFeature(!0),this.layer.destroy(!1));this.layer=null;return!0},destroyFeature:function(a){!this.layer||!a&&this.persist||this.layer.destroyFeatures();this.point=null},destroyPersistedFeature:function(){var a=this.layer;a&&1<a.features.length&&this.layer.features[0].destroy()},finalize:function(a){this.mouseDown=
!1;this.lastTouchPx=this.lastUp=this.lastDown=null;this.callback(a?"cancel":"done",[this.geometryClone()]);this.destroyFeature(a)},cancel:function(){this.finalize(!0)},click:function(a){OpenLayers.Event.stop(a);return!1},dblclick:function(a){OpenLayers.Event.stop(a);return!1},modifyFeature:function(a){this.point||this.createFeature(a);a=this.layer.getLonLatFromViewPortPx(a);this.point.geometry.x=a.lon;this.point.geometry.y=a.lat;this.callback("modify",[this.point.geometry,this.point,!1]);this.point.geometry.clearBounds();
this.drawFeature()},drawFeature:function(){this.layer.drawFeature(this.point,this.style)},getGeometry:function(){var a=this.point&&this.point.geometry;a&&this.multi&&(a=new OpenLayers.Geometry.MultiPoint([a]));return a},geometryClone:function(){var a=this.getGeometry();return a&&a.clone()},mousedown:function(a){return this.down(a)},touchstart:function(a){this.startTouch();this.lastTouchPx=a.xy;return this.down(a)},mousemove:function(a){return this.move(a)},touchmove:function(a){this.lastTouchPx=a.xy;
return this.move(a)},mouseup:function(a){return this.up(a)},touchend:function(a){a.xy=this.lastTouchPx;return this.up(a)},down:function(a){this.mouseDown=!0;this.lastDown=a.xy;this.touch||this.modifyFeature(a.xy);this.stoppedDown=this.stopDown;return!this.stopDown},move:function(a){this.touch||this.mouseDown&&!this.stoppedDown||this.modifyFeature(a.xy);return!0},up:function(a){this.mouseDown=!1;this.stoppedDown=this.stopDown;if(!this.checkModifiers(a)||this.lastUp&&this.lastUp.equals(a.xy)||!this.lastDown||
!this.passesTolerance(this.lastDown,a.xy,this.pixelTolerance))return!0;this.touch&&this.modifyFeature(a.xy);this.persist&&this.destroyPersistedFeature();this.lastUp=a.xy;this.finalize();return!this.stopUp},mouseout:function(a){OpenLayers.Util.mouseLeft(a,this.map.viewPortDiv)&&(this.stoppedDown=this.stopDown,this.mouseDown=!1)},passesTolerance:function(a,b,c){var d=!0;null!=c&&a&&b&&a.distanceTo(b)>c&&(d=!1);return d},CLASS_NAME:"OpenLayers.Handler.Point"});OpenLayers.Handler.Path=OpenLayers.Class(OpenLayers.Handler.Point,{line:null,maxVertices:null,doubleTouchTolerance:20,freehand:!1,freehandToggle:"shiftKey",timerId:null,redoStack:null,createFeature:function(a){a=this.layer.getLonLatFromViewPortPx(a);a=new OpenLayers.Geometry.Point(a.lon,a.lat);this.point=new OpenLayers.Feature.Vector(a);this.line=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([this.point.geometry]));this.callback("create",[this.point.geometry,this.getSketch()]);
this.point.geometry.clearBounds();this.layer.addFeatures([this.line,this.point],{silent:!0})},destroyFeature:function(a){OpenLayers.Handler.Point.prototype.destroyFeature.call(this,a);this.line=null},destroyPersistedFeature:function(){var a=this.layer;a&&2<a.features.length&&this.layer.features[0].destroy()},removePoint:function(){this.point&&this.layer.removeFeatures([this.point])},addPoint:function(a){this.layer.removeFeatures([this.point]);a=this.layer.getLonLatFromViewPortPx(a);this.point=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(a.lon,
a.lat));this.line.geometry.addComponent(this.point.geometry,this.line.geometry.components.length);this.layer.addFeatures([this.point]);this.callback("point",[this.point.geometry,this.getGeometry()]);this.callback("modify",[this.point.geometry,this.getSketch()]);this.drawFeature();delete this.redoStack},insertXY:function(a,b){this.line.geometry.addComponent(new OpenLayers.Geometry.Point(a,b),this.getCurrentPointIndex());this.drawFeature();delete this.redoStack},insertDeltaXY:function(a,b){var c=this.getCurrentPointIndex()-
1,c=this.line.geometry.components[c];!c||(isNaN(c.x)||isNaN(c.y))||this.insertXY(c.x+a,c.y+b)},insertDirectionLength:function(a,b){a*=Math.PI/180;var c=b*Math.cos(a),d=b*Math.sin(a);this.insertDeltaXY(c,d)},insertDeflectionLength:function(a,b){var c=this.getCurrentPointIndex()-1;if(0<c){var d=this.line.geometry.components[c],c=this.line.geometry.components[c-1],d=Math.atan2(d.y-c.y,d.x-c.x);this.insertDirectionLength(180*d/Math.PI+a,b)}},getCurrentPointIndex:function(){return this.line.geometry.components.length-
1},undo:function(){var a=this.line.geometry,b=a.components,c=this.getCurrentPointIndex()-1,d=b[c],e=a.removeComponent(d);e&&(this.touch&&0<c&&(b=a.components,a=b[c-1],c=this.getCurrentPointIndex(),b=b[c],b.x=a.x,b.y=a.y),this.redoStack||(this.redoStack=[]),this.redoStack.push(d),this.drawFeature());return e},redo:function(){var a=this.redoStack&&this.redoStack.pop();a&&(this.line.geometry.addComponent(a,this.getCurrentPointIndex()),this.drawFeature());return!!a},freehandMode:function(a){return this.freehandToggle&&
a[this.freehandToggle]?!this.freehand:this.freehand},modifyFeature:function(a,b){this.line||this.createFeature(a);var c=this.layer.getLonLatFromViewPortPx(a);this.point.geometry.x=c.lon;this.point.geometry.y=c.lat;this.callback("modify",[this.point.geometry,this.getSketch(),b]);this.point.geometry.clearBounds();this.drawFeature()},drawFeature:function(){this.layer.drawFeature(this.line,this.style);this.layer.drawFeature(this.point,this.style)},getSketch:function(){return this.line},getGeometry:function(){var a=
this.line&&this.line.geometry;a&&this.multi&&(a=new OpenLayers.Geometry.MultiLineString([a]));return a},touchstart:function(a){if(this.timerId&&this.passesTolerance(this.lastTouchPx,a.xy,this.doubleTouchTolerance))return this.finishGeometry(),window.clearTimeout(this.timerId),this.timerId=null,!1;this.timerId&&(window.clearTimeout(this.timerId),this.timerId=null);this.timerId=window.setTimeout(OpenLayers.Function.bind(function(){this.timerId=null},this),300);return OpenLayers.Handler.Point.prototype.touchstart.call(this,
a)},down:function(a){var b=this.stopDown;this.freehandMode(a)&&(b=!0,this.touch&&(this.modifyFeature(a.xy,!!this.lastUp),OpenLayers.Event.stop(a)));this.touch||this.lastDown&&this.passesTolerance(this.lastDown,a.xy,this.pixelTolerance)||this.modifyFeature(a.xy,!!this.lastUp);this.mouseDown=!0;this.lastDown=a.xy;this.stoppedDown=b;return!b},move:function(a){if(this.stoppedDown&&this.freehandMode(a))return this.persist&&this.destroyPersistedFeature(),this.maxVertices&&this.line&&this.line.geometry.components.length===
this.maxVertices?(this.removePoint(),this.finalize()):this.addPoint(a.xy),!1;this.touch||this.mouseDown&&!this.stoppedDown||this.modifyFeature(a.xy,!!this.lastUp);return!0},up:function(a){!this.mouseDown||this.lastUp&&this.lastUp.equals(a.xy)||(this.stoppedDown&&this.freehandMode(a)?(this.persist&&this.destroyPersistedFeature(),this.removePoint(),this.finalize()):this.passesTolerance(this.lastDown,a.xy,this.pixelTolerance)&&(this.touch&&this.modifyFeature(a.xy),null==this.lastUp&&this.persist&&this.destroyPersistedFeature(),
this.addPoint(a.xy),this.lastUp=a.xy,this.line.geometry.components.length===this.maxVertices+1&&this.finishGeometry()));this.stoppedDown=this.stopDown;this.mouseDown=!1;return!this.stopUp},finishGeometry:function(){this.line.geometry.removeComponent(this.line.geometry.components[this.line.geometry.components.length-1]);this.removePoint();this.finalize()},dblclick:function(a){this.freehandMode(a)||this.finishGeometry();return!1},CLASS_NAME:"OpenLayers.Handler.Path"});OpenLayers.Spherical=OpenLayers.Spherical||{};OpenLayers.Spherical.DEFAULT_RADIUS=6378137;OpenLayers.Spherical.computeDistanceBetween=function(a,b,c){c=c||OpenLayers.Spherical.DEFAULT_RADIUS;var d=Math.sin(Math.PI*(b.lon-a.lon)/360),e=Math.sin(Math.PI*(b.lat-a.lat)/360);a=e*e+d*d*Math.cos(Math.PI*a.lat/180)*Math.cos(Math.PI*b.lat/180);return 2*c*Math.atan2(Math.sqrt(a),Math.sqrt(1-a))};
OpenLayers.Spherical.computeHeading=function(a,b){var c=Math.sin(Math.PI*(a.lon-b.lon)/180)*Math.cos(Math.PI*b.lat/180),d=Math.cos(Math.PI*a.lat/180)*Math.sin(Math.PI*b.lat/180)-Math.sin(Math.PI*a.lat/180)*Math.cos(Math.PI*b.lat/180)*Math.cos(Math.PI*(a.lon-b.lon)/180);return 180*Math.atan2(c,d)/Math.PI};OpenLayers.Control.CacheWrite=OpenLayers.Class(OpenLayers.Control,{layers:null,imageFormat:"image/png",quotaRegEx:/quota/i,setMap:function(a){OpenLayers.Control.prototype.setMap.apply(this,arguments);var b,c=this.layers||a.layers;for(b=c.length-1;0<=b;--b)this.addLayer({layer:c[b]});if(!this.layers)a.events.on({addlayer:this.addLayer,removeLayer:this.removeLayer,scope:this})},addLayer:function(a){a.layer.events.on({tileloadstart:this.makeSameOrigin,tileloaded:this.onTileLoaded,scope:this})},removeLayer:function(a){a.layer.events.un({tileloadstart:this.makeSameOrigin,
tileloaded:this.onTileLoaded,scope:this})},makeSameOrigin:function(a){if(this.active&&(a=a.tile,a instanceof OpenLayers.Tile.Image&&!a.crossOriginKeyword&&"data:"!==a.url.substr(0,5))){var b=OpenLayers.Request.makeSameOrigin(a.url,OpenLayers.ProxyHost);OpenLayers.Control.CacheWrite.urlMap[b]=a.url;a.url=b}},onTileLoaded:function(a){this.active&&(!a.aborted&&a.tile instanceof OpenLayers.Tile.Image&&"data:"!==a.tile.url.substr(0,5))&&(this.cache({tile:a.tile}),delete OpenLayers.Control.CacheWrite.urlMap[a.tile.url])},
cache:function(a){if(window.localStorage){a=a.tile;try{var b=a.getCanvasContext();b&&window.localStorage.setItem("olCache_"+(OpenLayers.Control.CacheWrite.urlMap[a.url]||a.url),b.canvas.toDataURL(this.imageFormat))}catch(c){(b=c.name||c.message)&&this.quotaRegEx.test(b)?this.events.triggerEvent("cachefull",{tile:a}):OpenLayers.Console.error(c.toString())}}},destroy:function(){if(this.layers||this.map){var a,b=this.layers||this.map.layers;for(a=b.length-1;0<=a;--a)this.removeLayer({layer:b[a]})}this.map&&
this.map.events.un({addlayer:this.addLayer,removeLayer:this.removeLayer,scope:this});OpenLayers.Control.prototype.destroy.apply(this,arguments)},CLASS_NAME:"OpenLayers.Control.CacheWrite"});OpenLayers.Control.CacheWrite.clearCache=function(){if(window.localStorage){var a,b;for(a=window.localStorage.length-1;0<=a;--a)b=window.localStorage.key(a),"olCache_"===b.substr(0,8)&&window.localStorage.removeItem(b)}};OpenLayers.Control.CacheWrite.urlMap={};OpenLayers.Format.Context=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{layerOptions:null,layerParams:null,read:function(a,b){var c=OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this,arguments);if(b&&b.map)if(this.context=c,b.map instanceof OpenLayers.Map)c=this.mergeContextToMap(c,b.map);else{var d=b.map;if(OpenLayers.Util.isElement(d)||"string"==typeof d)d={div:d};c=this.contextToMap(c,d)}return c},getLayerFromContext:function(a){var b,c,d={queryable:a.queryable,visibility:a.visibility,
maxExtent:a.maxExtent,metadata:OpenLayers.Util.applyDefaults(a.metadata,{styles:a.styles,formats:a.formats,"abstract":a["abstract"],dataURL:a.dataURL}),numZoomLevels:a.numZoomLevels,units:a.units,isBaseLayer:a.isBaseLayer,opacity:a.opacity,displayInLayerSwitcher:a.displayInLayerSwitcher,singleTile:a.singleTile,tileSize:a.tileSize?new OpenLayers.Size(a.tileSize.width,a.tileSize.height):void 0,minScale:a.minScale||a.maxScaleDenominator,maxScale:a.maxScale||a.minScaleDenominator,srs:a.srs,dimensions:a.dimensions,
metadataURL:a.metadataURL};this.layerOptions&&OpenLayers.Util.applyDefaults(d,this.layerOptions);var e={layers:a.name,transparent:a.transparent,version:a.version};if(a.formats&&0<a.formats.length)for(e.format=a.formats[0].value,b=0,c=a.formats.length;b<c;b++){var f=a.formats[b];if(!0==f.current){e.format=f.value;break}}if(a.styles&&0<a.styles.length)for(b=0,c=a.styles.length;b<c;b++)if(f=a.styles[b],!0==f.current){f.href?e.sld=f.href:f.body?e.sld_body=f.body:e.styles=f.name;break}this.layerParams&&
OpenLayers.Util.applyDefaults(e,this.layerParams);b=null;c=a.service;c==OpenLayers.Format.Context.serviceTypes.WFS?(d.strategies=[new OpenLayers.Strategy.BBOX],d.protocol=new OpenLayers.Protocol.WFS({url:a.url,featurePrefix:a.name.split(":")[0],featureType:a.name.split(":").pop()}),b=new OpenLayers.Layer.Vector(a.title||a.name,d)):c==OpenLayers.Format.Context.serviceTypes.KML?(d.strategies=[new OpenLayers.Strategy.Fixed],d.protocol=new OpenLayers.Protocol.HTTP({url:a.url,format:new OpenLayers.Format.KML}),
b=new OpenLayers.Layer.Vector(a.title||a.name,d)):c==OpenLayers.Format.Context.serviceTypes.GML?(d.strategies=[new OpenLayers.Strategy.Fixed],d.protocol=new OpenLayers.Protocol.HTTP({url:a.url,format:new OpenLayers.Format.GML}),b=new OpenLayers.Layer.Vector(a.title||a.name,d)):a.features?(b=new OpenLayers.Layer.Vector(a.title||a.name,d),b.addFeatures(a.features)):!0!==a.categoryLayer&&(b=new OpenLayers.Layer.WMS(a.title||a.name,a.url,e,d));return b},getLayersFromContext:function(a){for(var b=[],c=
0,d=a.length;c<d;c++){var e=this.getLayerFromContext(a[c]);null!==e&&b.push(e)}return b},contextToMap:function(a,b){b=OpenLayers.Util.applyDefaults({maxExtent:a.maxExtent,projection:a.projection,units:a.units},b);b.maxExtent&&(b.maxResolution=b.maxExtent.getWidth()/OpenLayers.Map.TILE_WIDTH);b.metadata={contactInformation:a.contactInformation,"abstract":a["abstract"],keywords:a.keywords,logo:a.logo,descriptionURL:a.descriptionURL};var c=new OpenLayers.Map(b);c.addLayers(this.getLayersFromContext(a.layersContext));
c.setCenter(a.bounds.getCenterLonLat(),c.getZoomForExtent(a.bounds,!0));return c},mergeContextToMap:function(a,b){b.addLayers(this.getLayersFromContext(a.layersContext));return b},write:function(a,b){a=this.toContext(a);return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this,arguments)},CLASS_NAME:"OpenLayers.Format.Context"});
OpenLayers.Format.Context.serviceTypes={WMS:"urn:ogc:serviceType:WMS",WFS:"urn:ogc:serviceType:WFS",WCS:"urn:ogc:serviceType:WCS",GML:"urn:ogc:serviceType:GML",SLD:"urn:ogc:serviceType:SLD",FES:"urn:ogc:serviceType:FES",KML:"urn:ogc:serviceType:KML"};OpenLayers.Format.WMC=OpenLayers.Class(OpenLayers.Format.Context,{defaultVersion:"1.1.0",layerToContext:function(a){var b=this.getParser(),c={queryable:a.queryable,visibility:a.visibility,name:a.params.LAYERS,title:a.name,"abstract":a.metadata["abstract"],dataURL:a.metadata.dataURL,metadataURL:a.metadataURL,server:{version:a.params.VERSION,url:a.url},maxExtent:a.maxExtent,transparent:a.params.TRANSPARENT,numZoomLevels:a.numZoomLevels,units:a.units,isBaseLayer:a.isBaseLayer,opacity:1==a.opacity?void 0:
a.opacity,displayInLayerSwitcher:a.displayInLayerSwitcher,singleTile:a.singleTile,tileSize:a.singleTile||!a.tileSize?void 0:{width:a.tileSize.w,height:a.tileSize.h},minScale:a.options.resolutions||a.options.scales||a.options.maxResolution||a.options.minScale?a.minScale:void 0,maxScale:a.options.resolutions||a.options.scales||a.options.minResolution||a.options.maxScale?a.maxScale:void 0,formats:[],styles:[],srs:a.srs,dimensions:a.dimensions};a.metadata.servertitle&&(c.server.title=a.metadata.servertitle);
if(a.metadata.formats&&0<a.metadata.formats.length)for(var d=0,e=a.metadata.formats.length;d<e;d++){var f=a.metadata.formats[d];c.formats.push({value:f.value,current:f.value==a.params.FORMAT})}else c.formats.push({value:a.params.FORMAT,current:!0});if(a.metadata.styles&&0<a.metadata.styles.length)for(d=0,e=a.metadata.styles.length;d<e;d++)b=a.metadata.styles[d],b.current=b.href==a.params.SLD||b.body==a.params.SLD_BODY||b.name==a.params.STYLES?!0:!1,c.styles.push(b);else c.styles.push({href:a.params.SLD,
body:a.params.SLD_BODY,name:a.params.STYLES||b.defaultStyleName,title:b.defaultStyleTitle,current:!0});return c},toContext:function(a){var b={},c=a.layers;if("OpenLayers.Map"==a.CLASS_NAME){var d=a.metadata||{};b.size=a.getSize();b.bounds=a.getExtent();b.projection=a.projection;b.title=a.title;b.keywords=d.keywords;b["abstract"]=d["abstract"];b.logo=d.logo;b.descriptionURL=d.descriptionURL;b.contactInformation=d.contactInformation;b.maxExtent=a.maxExtent}else OpenLayers.Util.applyDefaults(b,a),void 0!=
b.layers&&delete b.layers;void 0==b.layersContext&&(b.layersContext=[]);if(void 0!=c&&OpenLayers.Util.isArray(c))for(a=0,d=c.length;a<d;a++){var e=c[a];e instanceof OpenLayers.Layer.WMS&&b.layersContext.push(this.layerToContext(e))}return b},CLASS_NAME:"OpenLayers.Format.WMC"});OpenLayers.Format.WMC.v1=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{ol:"http://openlayers.org/context",wmc:"http://www.opengis.net/context",sld:"http://www.opengis.net/sld",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},schemaLocation:"",getNamespacePrefix:function(a){var b=null;if(null==a)b=this.namespaces[this.defaultPrefix];else for(b in this.namespaces)if(this.namespaces[b]==a)break;return b},defaultPrefix:"wmc",rootPrefix:null,defaultStyleName:"",
defaultStyleTitle:"Default",initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a=a.documentElement;this.rootPrefix=a.prefix;var b={version:a.getAttribute("version")};this.runChildNodes(b,a);return b},runChildNodes:function(a,b){for(var c=b.childNodes,d,e,f,g=0,h=c.length;g<h;++g)d=c[g],1==d.nodeType&&(e=this.getNamespacePrefix(d.namespaceURI),f=d.nodeName.split(":").pop(),
(e=this["read_"+e+"_"+f])&&e.apply(this,[a,d]))},read_wmc_General:function(a,b){this.runChildNodes(a,b)},read_wmc_BoundingBox:function(a,b){a.projection=b.getAttribute("SRS");a.bounds=new OpenLayers.Bounds(b.getAttribute("minx"),b.getAttribute("miny"),b.getAttribute("maxx"),b.getAttribute("maxy"))},read_wmc_LayerList:function(a,b){a.layersContext=[];this.runChildNodes(a,b)},read_wmc_Layer:function(a,b){var c={visibility:"1"!=b.getAttribute("hidden"),queryable:"1"==b.getAttribute("queryable"),formats:[],
styles:[],metadata:{}};this.runChildNodes(c,b);a.layersContext.push(c)},read_wmc_Extension:function(a,b){this.runChildNodes(a,b)},read_ol_units:function(a,b){a.units=this.getChildValue(b)},read_ol_maxExtent:function(a,b){var c=new OpenLayers.Bounds(b.getAttribute("minx"),b.getAttribute("miny"),b.getAttribute("maxx"),b.getAttribute("maxy"));a.maxExtent=c},read_ol_transparent:function(a,b){a.transparent=this.getChildValue(b)},read_ol_numZoomLevels:function(a,b){a.numZoomLevels=parseInt(this.getChildValue(b))},
read_ol_opacity:function(a,b){a.opacity=parseFloat(this.getChildValue(b))},read_ol_singleTile:function(a,b){a.singleTile="true"==this.getChildValue(b)},read_ol_tileSize:function(a,b){var c={width:b.getAttribute("width"),height:b.getAttribute("height")};a.tileSize=c},read_ol_isBaseLayer:function(a,b){a.isBaseLayer="true"==this.getChildValue(b)},read_ol_displayInLayerSwitcher:function(a,b){a.displayInLayerSwitcher="true"==this.getChildValue(b)},read_wmc_Server:function(a,b){a.version=b.getAttribute("version");
a.url=this.getOnlineResource_href(b);a.metadata.servertitle=b.getAttribute("title")},read_wmc_FormatList:function(a,b){this.runChildNodes(a,b)},read_wmc_Format:function(a,b){var c={value:this.getChildValue(b)};"1"==b.getAttribute("current")&&(c.current=!0);a.formats.push(c)},read_wmc_StyleList:function(a,b){this.runChildNodes(a,b)},read_wmc_Style:function(a,b){var c={};this.runChildNodes(c,b);"1"==b.getAttribute("current")&&(c.current=!0);a.styles.push(c)},read_wmc_SLD:function(a,b){this.runChildNodes(a,
b)},read_sld_StyledLayerDescriptor:function(a,b){var c=OpenLayers.Format.XML.prototype.write.apply(this,[b]);a.body=c},read_sld_FeatureTypeStyle:function(a,b){var c=OpenLayers.Format.XML.prototype.write.apply(this,[b]);a.body=c},read_wmc_OnlineResource:function(a,b){a.href=this.getAttributeNS(b,this.namespaces.xlink,"href")},read_wmc_Name:function(a,b){var c=this.getChildValue(b);c&&(a.name=c)},read_wmc_Title:function(a,b){var c=this.getChildValue(b);c&&(a.title=c)},read_wmc_MetadataURL:function(a,
b){a.metadataURL=this.getOnlineResource_href(b)},read_wmc_KeywordList:function(a,b){a.keywords=[];this.runChildNodes(a.keywords,b)},read_wmc_Keyword:function(a,b){a.push(this.getChildValue(b))},read_wmc_Abstract:function(a,b){var c=this.getChildValue(b);c&&(a["abstract"]=c)},read_wmc_LogoURL:function(a,b){a.logo={width:b.getAttribute("width"),height:b.getAttribute("height"),format:b.getAttribute("format"),href:this.getOnlineResource_href(b)}},read_wmc_DescriptionURL:function(a,b){a.descriptionURL=
this.getOnlineResource_href(b)},read_wmc_ContactInformation:function(a,b){var c={};this.runChildNodes(c,b);a.contactInformation=c},read_wmc_ContactPersonPrimary:function(a,b){var c={};this.runChildNodes(c,b);a.personPrimary=c},read_wmc_ContactPerson:function(a,b){var c=this.getChildValue(b);c&&(a.person=c)},read_wmc_ContactOrganization:function(a,b){var c=this.getChildValue(b);c&&(a.organization=c)},read_wmc_ContactPosition:function(a,b){var c=this.getChildValue(b);c&&(a.position=c)},read_wmc_ContactAddress:function(a,
b){var c={};this.runChildNodes(c,b);a.contactAddress=c},read_wmc_AddressType:function(a,b){var c=this.getChildValue(b);c&&(a.type=c)},read_wmc_Address:function(a,b){var c=this.getChildValue(b);c&&(a.address=c)},read_wmc_City:function(a,b){var c=this.getChildValue(b);c&&(a.city=c)},read_wmc_StateOrProvince:function(a,b){var c=this.getChildValue(b);c&&(a.stateOrProvince=c)},read_wmc_PostCode:function(a,b){var c=this.getChildValue(b);c&&(a.postcode=c)},read_wmc_Country:function(a,b){var c=this.getChildValue(b);
c&&(a.country=c)},read_wmc_ContactVoiceTelephone:function(a,b){var c=this.getChildValue(b);c&&(a.phone=c)},read_wmc_ContactFacsimileTelephone:function(a,b){var c=this.getChildValue(b);c&&(a.fax=c)},read_wmc_ContactElectronicMailAddress:function(a,b){var c=this.getChildValue(b);c&&(a.email=c)},read_wmc_DataURL:function(a,b){a.dataURL=this.getOnlineResource_href(b)},read_wmc_LegendURL:function(a,b){var c={width:b.getAttribute("width"),height:b.getAttribute("height"),format:b.getAttribute("format"),
href:this.getOnlineResource_href(b)};a.legend=c},read_wmc_DimensionList:function(a,b){a.dimensions={};this.runChildNodes(a.dimensions,b)},read_wmc_Dimension:function(a,b){var c={name:b.getAttribute("name").toLowerCase(),units:b.getAttribute("units")||"",unitSymbol:b.getAttribute("unitSymbol")||"",userValue:b.getAttribute("userValue")||"",nearestValue:"1"===b.getAttribute("nearestValue"),multipleValues:"1"===b.getAttribute("multipleValues"),current:"1"===b.getAttribute("current"),"default":b.getAttribute("default")||
""},d=this.getChildValue(b);c.values=d.split(",");a[c.name]=c},write:function(a,b){var c=this.createElementDefaultNS("ViewContext");this.setAttributes(c,{version:this.VERSION,id:b&&"string"==typeof b.id?b.id:OpenLayers.Util.createUniqueID("OpenLayers_Context_")});this.setAttributeNS(c,this.namespaces.xsi,"xsi:schemaLocation",this.schemaLocation);c.appendChild(this.write_wmc_General(a));c.appendChild(this.write_wmc_LayerList(a));return OpenLayers.Format.XML.prototype.write.apply(this,[c])},createElementDefaultNS:function(a,
b,c){a=this.createElementNS(this.namespaces[this.defaultPrefix],a);b&&a.appendChild(this.createTextNode(b));c&&this.setAttributes(a,c);return a},setAttributes:function(a,b){var c,d;for(d in b)c=b[d].toString(),c.match(/[A-Z]/)?this.setAttributeNS(a,null,d,c):a.setAttribute(d,c)},write_wmc_General:function(a){var b=this.createElementDefaultNS("General");a.size&&b.appendChild(this.createElementDefaultNS("Window",null,{width:a.size.w,height:a.size.h}));var c=a.bounds;b.appendChild(this.createElementDefaultNS("BoundingBox",
null,{minx:c.left.toPrecision(18),miny:c.bottom.toPrecision(18),maxx:c.right.toPrecision(18),maxy:c.top.toPrecision(18),SRS:a.projection}));b.appendChild(this.createElementDefaultNS("Title",a.title));a.keywords&&b.appendChild(this.write_wmc_KeywordList(a.keywords));a["abstract"]&&b.appendChild(this.createElementDefaultNS("Abstract",a["abstract"]));a.logo&&b.appendChild(this.write_wmc_URLType("LogoURL",a.logo.href,a.logo));a.descriptionURL&&b.appendChild(this.write_wmc_URLType("DescriptionURL",a.descriptionURL));
a.contactInformation&&b.appendChild(this.write_wmc_ContactInformation(a.contactInformation));b.appendChild(this.write_ol_MapExtension(a));return b},write_wmc_KeywordList:function(a){for(var b=this.createElementDefaultNS("KeywordList"),c=0,d=a.length;c<d;c++)b.appendChild(this.createElementDefaultNS("Keyword",a[c]));return b},write_wmc_ContactInformation:function(a){var b=this.createElementDefaultNS("ContactInformation");a.personPrimary&&b.appendChild(this.write_wmc_ContactPersonPrimary(a.personPrimary));
a.position&&b.appendChild(this.createElementDefaultNS("ContactPosition",a.position));a.contactAddress&&b.appendChild(this.write_wmc_ContactAddress(a.contactAddress));a.phone&&b.appendChild(this.createElementDefaultNS("ContactVoiceTelephone",a.phone));a.fax&&b.appendChild(this.createElementDefaultNS("ContactFacsimileTelephone",a.fax));a.email&&b.appendChild(this.createElementDefaultNS("ContactElectronicMailAddress",a.email));return b},write_wmc_ContactPersonPrimary:function(a){var b=this.createElementDefaultNS("ContactPersonPrimary");
a.person&&b.appendChild(this.createElementDefaultNS("ContactPerson",a.person));a.organization&&b.appendChild(this.createElementDefaultNS("ContactOrganization",a.organization));return b},write_wmc_ContactAddress:function(a){var b=this.createElementDefaultNS("ContactAddress");a.type&&b.appendChild(this.createElementDefaultNS("AddressType",a.type));a.address&&b.appendChild(this.createElementDefaultNS("Address",a.address));a.city&&b.appendChild(this.createElementDefaultNS("City",a.city));a.stateOrProvince&&
b.appendChild(this.createElementDefaultNS("StateOrProvince",a.stateOrProvince));a.postcode&&b.appendChild(this.createElementDefaultNS("PostCode",a.postcode));a.country&&b.appendChild(this.createElementDefaultNS("Country",a.country));return b},write_ol_MapExtension:function(a){var b=this.createElementDefaultNS("Extension");if(a=a.maxExtent){var c=this.createElementNS(this.namespaces.ol,"ol:maxExtent");this.setAttributes(c,{minx:a.left.toPrecision(18),miny:a.bottom.toPrecision(18),maxx:a.right.toPrecision(18),
maxy:a.top.toPrecision(18)});b.appendChild(c)}return b},write_wmc_LayerList:function(a){for(var b=this.createElementDefaultNS("LayerList"),c=0,d=a.layersContext.length;c<d;++c)b.appendChild(this.write_wmc_Layer(a.layersContext[c]));return b},write_wmc_Layer:function(a){var b=this.createElementDefaultNS("Layer",null,{queryable:a.queryable?"1":"0",hidden:a.visibility?"0":"1"});b.appendChild(this.write_wmc_Server(a));b.appendChild(this.createElementDefaultNS("Name",a.name));b.appendChild(this.createElementDefaultNS("Title",
a.title));a["abstract"]&&b.appendChild(this.createElementDefaultNS("Abstract",a["abstract"]));a.dataURL&&b.appendChild(this.write_wmc_URLType("DataURL",a.dataURL));a.metadataURL&&b.appendChild(this.write_wmc_URLType("MetadataURL",a.metadataURL));return b},write_wmc_LayerExtension:function(a){var b=this.createElementDefaultNS("Extension"),c=a.maxExtent,d=this.createElementNS(this.namespaces.ol,"ol:maxExtent");this.setAttributes(d,{minx:c.left.toPrecision(18),miny:c.bottom.toPrecision(18),maxx:c.right.toPrecision(18),
maxy:c.top.toPrecision(18)});b.appendChild(d);a.tileSize&&!a.singleTile&&(c=this.createElementNS(this.namespaces.ol,"ol:tileSize"),this.setAttributes(c,a.tileSize),b.appendChild(c));for(var c="transparent numZoomLevels units isBaseLayer opacity displayInLayerSwitcher singleTile".split(" "),e=0,f=c.length;e<f;++e)(d=this.createOLPropertyNode(a,c[e]))&&b.appendChild(d);return b},createOLPropertyNode:function(a,b){var c=null;null!=a[b]&&(c=this.createElementNS(this.namespaces.ol,"ol:"+b),c.appendChild(this.createTextNode(a[b].toString())));
return c},write_wmc_Server:function(a){a=a.server;var b=this.createElementDefaultNS("Server"),c={service:"OGC:WMS",version:a.version};a.title&&(c.title=a.title);this.setAttributes(b,c);b.appendChild(this.write_wmc_OnlineResource(a.url));return b},write_wmc_URLType:function(a,b,c){a=this.createElementDefaultNS(a);a.appendChild(this.write_wmc_OnlineResource(b));if(c){b=["width","height","format"];for(var d=0;d<b.length;d++)b[d]in c&&a.setAttribute(b[d],c[b[d]])}return a},write_wmc_DimensionList:function(a){var b=
this.createElementDefaultNS("DimensionList"),c;for(c in a.dimensions){var d={},e=a.dimensions[c],f;for(f in e)d[f]="boolean"==typeof e[f]?Number(e[f]):e[f];e="";d.values&&(e=d.values.join(","),delete d.values);b.appendChild(this.createElementDefaultNS("Dimension",e,d))}return b},write_wmc_FormatList:function(a){for(var b=this.createElementDefaultNS("FormatList"),c=0,d=a.formats.length;c<d;c++){var e=a.formats[c];b.appendChild(this.createElementDefaultNS("Format",e.value,e.current&&!0==e.current?{current:"1"}:
null))}return b},write_wmc_StyleList:function(a){var b=this.createElementDefaultNS("StyleList");if((a=a.styles)&&OpenLayers.Util.isArray(a))for(var c,d=0,e=a.length;d<e;d++){var f=a[d],g=this.createElementDefaultNS("Style",null,f.current&&!0==f.current?{current:"1"}:null);f.href?(c=this.createElementDefaultNS("SLD"),f.name&&c.appendChild(this.createElementDefaultNS("Name",f.name)),f.title&&c.appendChild(this.createElementDefaultNS("Title",f.title)),f.legend&&c.appendChild(this.write_wmc_URLType("LegendURL",
f.legend.href,f.legend)),f=this.write_wmc_OnlineResource(f.href),c.appendChild(f),g.appendChild(c)):f.body?(c=this.createElementDefaultNS("SLD"),f.name&&c.appendChild(this.createElementDefaultNS("Name",f.name)),f.title&&c.appendChild(this.createElementDefaultNS("Title",f.title)),f.legend&&c.appendChild(this.write_wmc_URLType("LegendURL",f.legend.href,f.legend)),f=OpenLayers.Format.XML.prototype.read.apply(this,[f.body]).documentElement,c.ownerDocument&&c.ownerDocument.importNode&&(f=c.ownerDocument.importNode(f,
!0)),c.appendChild(f),g.appendChild(c)):(g.appendChild(this.createElementDefaultNS("Name",f.name)),g.appendChild(this.createElementDefaultNS("Title",f.title)),f["abstract"]&&g.appendChild(this.createElementDefaultNS("Abstract",f["abstract"])),f.legend&&g.appendChild(this.write_wmc_URLType("LegendURL",f.legend.href,f.legend)));b.appendChild(g)}return b},write_wmc_OnlineResource:function(a){var b=this.createElementDefaultNS("OnlineResource");this.setAttributeNS(b,this.namespaces.xlink,"xlink:type",
"simple");this.setAttributeNS(b,this.namespaces.xlink,"xlink:href",a);return b},getOnlineResource_href:function(a){var b={};a=a.getElementsByTagName("OnlineResource");0<a.length&&this.read_wmc_OnlineResource(b,a[0]);return b.href},CLASS_NAME:"OpenLayers.Format.WMC.v1"});OpenLayers.Control.PanPanel=OpenLayers.Class(OpenLayers.Control.Panel,{slideFactor:50,slideRatio:null,initialize:function(a){OpenLayers.Control.Panel.prototype.initialize.apply(this,[a]);a={slideFactor:this.slideFactor,slideRatio:this.slideRatio};this.addControls([new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH,a),new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH,a),new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST,a),new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST,a)])},
CLASS_NAME:"OpenLayers.Control.PanPanel"});OpenLayers.Control.Attribution=OpenLayers.Class(OpenLayers.Control,{separator:", ",template:"${layers}",destroy:function(){this.map.events.un({removelayer:this.updateAttribution,addlayer:this.updateAttribution,changelayer:this.updateAttribution,changebaselayer:this.updateAttribution,scope:this});OpenLayers.Control.prototype.destroy.apply(this,arguments)},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);this.map.events.on({changebaselayer:this.updateAttribution,changelayer:this.updateAttribution,
addlayer:this.updateAttribution,removelayer:this.updateAttribution,scope:this});this.updateAttribution();return this.div},updateAttribution:function(){var a=[];if(this.map&&this.map.layers){for(var b=0,c=this.map.layers.length;b<c;b++){var d=this.map.layers[b];d.attribution&&d.getVisibility()&&-1===OpenLayers.Util.indexOf(a,d.attribution)&&a.push(d.attribution)}this.div.innerHTML=OpenLayers.String.format(this.template,{layers:a.join(this.separator)})}},CLASS_NAME:"OpenLayers.Control.Attribution"});OpenLayers.Kinetic=OpenLayers.Class({threshold:0,deceleration:0.0035,nbPoints:100,delay:200,points:void 0,timerId:void 0,initialize:function(a){OpenLayers.Util.extend(this,a)},begin:function(){OpenLayers.Animation.stop(this.timerId);this.timerId=void 0;this.points=[]},update:function(a){this.points.unshift({xy:a,tick:(new Date).getTime()});this.points.length>this.nbPoints&&this.points.pop()},end:function(a){for(var b,c=(new Date).getTime(),d=0,e=this.points.length,f;d<e;d++){f=this.points[d];if(c-
f.tick>this.delay)break;b=f}if(b&&(d=(new Date).getTime()-b.tick,c=Math.sqrt(Math.pow(a.x-b.xy.x,2)+Math.pow(a.y-b.xy.y,2)),d=c/d,!(0==d||d<this.threshold)))return c=Math.asin((a.y-b.xy.y)/c),b.xy.x<=a.x&&(c=Math.PI-c),{speed:d,theta:c}},move:function(a,b){var c=a.speed,d=Math.cos(a.theta),e=-Math.sin(a.theta),f=(new Date).getTime(),g=0,h=0;this.timerId=OpenLayers.Animation.start(OpenLayers.Function.bind(function(){if(null!=this.timerId){var a=(new Date).getTime()-f,l=-this.deceleration*Math.pow(a,
2)/2+c*a,m=l*d,l=l*e,n,p;n=!1;0>=-this.deceleration*a+c&&(OpenLayers.Animation.stop(this.timerId),this.timerId=null,n=!0);a=m-g;p=l-h;g=m;h=l;b(a,p,n)}},this))},CLASS_NAME:"OpenLayers.Kinetic"});OpenLayers.Format.WPSExecute=OpenLayers.Class(OpenLayers.Format.XML,OpenLayers.Format.Filter.v1_1_0,{namespaces:{ows:"http://www.opengis.net/ows/1.1",gml:"http://www.opengis.net/gml",wps:"http://www.opengis.net/wps/1.0.0",wfs:"http://www.opengis.net/wfs",ogc:"http://www.opengis.net/ogc",wcs:"http://www.opengis.net/wcs",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},VERSION:"1.0.0",
schemaLocation:"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd",schemaLocationAttr:function(a){},write:function(a){var b;window.ActiveXObject?this.xmldom=b=new ActiveXObject("Microsoft.XMLDOM"):b=document.implementation.createDocument("","",null);a=this.writeNode("wps:Execute",a,b);this.setAttributeNS(a,this.namespaces.xsi,"xsi:schemaLocation",this.schemaLocation);return OpenLayers.Format.XML.prototype.write.apply(this,[a])},read:function(a){"string"==typeof a&&(a=
OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={};this.readNode(a,b);return b},writers:{wps:{Execute:function(a){var b=this.createElementNSPlus("wps:Execute",{attributes:{version:this.VERSION,service:"WPS"}});this.writeNode("ows:Identifier",a.identifier,b);this.writeNode("wps:DataInputs",a.dataInputs,b);this.writeNode("wps:ResponseForm",a.responseForm,b);return b},ResponseForm:function(a){var b=this.createElementNSPlus("wps:ResponseForm",{});a.rawDataOutput&&
this.writeNode("wps:RawDataOutput",a.rawDataOutput,b);a.responseDocument&&this.writeNode("wps:ResponseDocument",a.responseDocument,b);return b},ResponseDocument:function(a){var b=this.createElementNSPlus("wps:ResponseDocument",{attributes:{storeExecuteResponse:a.storeExecuteResponse,lineage:a.lineage,status:a.status}});if(a.outputs)for(var c=0,d=a.outputs.length;c<d;c++)this.writeNode("wps:Output",a.outputs[c],b);return b},Output:function(a){var b=this.createElementNSPlus("wps:Output",{attributes:{asReference:a.asReference,
mimeType:a.mimeType,encoding:a.encoding,schema:a.schema}});this.writeNode("ows:Identifier",a.identifier,b);this.writeNode("ows:Title",a.title,b);this.writeNode("ows:Abstract",a["abstract"],b);return b},RawDataOutput:function(a){var b=this.createElementNSPlus("wps:RawDataOutput",{attributes:{mimeType:a.mimeType,encoding:a.encoding,schema:a.schema}});this.writeNode("ows:Identifier",a.identifier,b);return b},DataInputs:function(a){for(var b=this.createElementNSPlus("wps:DataInputs",{}),c=0,d=a.length;c<
d;++c)this.writeNode("wps:Input",a[c],b);return b},Input:function(a){var b=this.createElementNSPlus("wps:Input",{});this.writeNode("ows:Identifier",a.identifier,b);a.title&&this.writeNode("ows:Title",a.title,b);a.data&&this.writeNode("wps:Data",a.data,b);a.reference&&this.writeNode("wps:Reference",a.reference,b);a.boundingBoxData&&this.writeNode("wps:BoundingBoxData",a.boundingBoxData,b);return b},Data:function(a){var b=this.createElementNSPlus("wps:Data",{});a.literalData?this.writeNode("wps:LiteralData",
a.literalData,b):a.complexData?this.writeNode("wps:ComplexData",a.complexData,b):a.boundingBoxData&&this.writeNode("ows:BoundingBox",a.boundingBoxData,b);return b},LiteralData:function(a){return this.createElementNSPlus("wps:LiteralData",{attributes:{uom:a.uom},value:a.value})},ComplexData:function(a){var b=this.createElementNSPlus("wps:ComplexData",{attributes:{mimeType:a.mimeType,encoding:a.encoding,schema:a.schema}}),c=a.value;"string"===typeof c?b.appendChild(this.getXMLDoc().createCDATASection(a.value)):
b.appendChild(c);return b},Reference:function(a){var b=this.createElementNSPlus("wps:Reference",{attributes:{mimeType:a.mimeType,"xlink:href":a.href,method:a.method,encoding:a.encoding,schema:a.schema}});a.body&&this.writeNode("wps:Body",a.body,b);return b},BoundingBoxData:function(a,b){this.writers.ows.BoundingBox.apply(this,[a,b,"wps:BoundingBoxData"])},Body:function(a){var b=this.createElementNSPlus("wps:Body",{});a.wcs?this.writeNode("wcs:GetCoverage",a.wcs,b):a.wfs?(this.featureType=a.wfs.featureType,
this.version=a.wfs.version,this.writeNode("wfs:GetFeature",a.wfs,b)):this.writeNode("wps:Execute",a,b);return b}},wcs:OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs,wfs:OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs,ogc:OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc,ows:OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows},readers:{wps:{ExecuteResponse:function(a,b){b.executeResponse={lang:a.getAttribute("lang"),statusLocation:a.getAttribute("statusLocation"),serviceInstance:a.getAttribute("serviceInstance"),
service:a.getAttribute("service")};this.readChildNodes(a,b.executeResponse)},Process:function(a,b){b.process={};this.readChildNodes(a,b.process)},Status:function(a,b){b.status={creationTime:a.getAttribute("creationTime")};this.readChildNodes(a,b.status)},ProcessSucceeded:function(a,b){b.processSucceeded=!0},ProcessOutputs:function(a,b){b.processOutputs=[];this.readChildNodes(a,b.processOutputs)},Output:function(a,b){var c={};this.readChildNodes(a,c);b.push(c)},Reference:function(a,b){b.reference=
{href:a.getAttribute("href"),mimeType:a.getAttribute("mimeType"),encoding:a.getAttribute("encoding"),schema:a.getAttribute("schema")}},Data:function(a,b){b.data={};this.readChildNodes(a,b)},LiteralData:function(a,b){b.literalData={dataType:a.getAttribute("dataType"),uom:a.getAttribute("uom"),value:this.getChildValue(a)}},ComplexData:function(a,b){b.complexData={mimeType:a.getAttribute("mimeType"),schema:a.getAttribute("schema"),encoding:a.getAttribute("encoding"),value:""};if(this.isSimpleContent(a)){var c;
for(c=a.firstChild;c;c=c.nextSibling)switch(c.nodeType){case 3:case 4:b.complexData.value+=c.nodeValue}}else for(c=a.firstChild;c;c=c.nextSibling)1==c.nodeType&&(b.complexData.value=c)},BoundingBox:function(a,b){b.boundingBoxData={dimensions:a.getAttribute("dimensions"),crs:a.getAttribute("crs")};this.readChildNodes(a,b.boundingBoxData)}},ows:OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers.ows},CLASS_NAME:"OpenLayers.Format.WPSExecute"});OpenLayers.Layer.GeoRSS=OpenLayers.Class(OpenLayers.Layer.Markers,{location:null,features:null,formatOptions:null,selectedFeature:null,icon:null,popupSize:null,useFeedTitle:!0,initialize:function(a,b,c){OpenLayers.Layer.Markers.prototype.initialize.apply(this,[a,c]);this.location=b;this.features=[]},destroy:function(){OpenLayers.Layer.Markers.prototype.destroy.apply(this,arguments);this.clearFeatures();this.features=null},loadRSS:function(){this.loaded||(this.events.triggerEvent("loadstart"),OpenLayers.Request.GET({url:this.location,
success:this.parseData,scope:this}),this.loaded=!0)},moveTo:function(a,b,c){OpenLayers.Layer.Markers.prototype.moveTo.apply(this,arguments);this.visibility&&!this.loaded&&this.loadRSS()},parseData:function(a){var b=a.responseXML;b&&b.documentElement||(b=OpenLayers.Format.XML.prototype.read(a.responseText));if(this.useFeedTitle){a=null;try{a=b.getElementsByTagNameNS("*","title")[0].firstChild.nodeValue}catch(c){a=b.getElementsByTagName("title")[0].firstChild.nodeValue}a&&this.setName(a)}a={};OpenLayers.Util.extend(a,
this.formatOptions);this.map&&!this.projection.equals(this.map.getProjectionObject())&&(a.externalProjection=this.projection,a.internalProjection=this.map.getProjectionObject());b=(new OpenLayers.Format.GeoRSS(a)).read(b);a=0;for(var d=b.length;a<d;a++){var e={},f=b[a];if(f.geometry){var g=f.attributes.title?f.attributes.title:"Untitled",h=f.attributes.description?f.attributes.description:"No description.",k=f.attributes.link?f.attributes.link:"",f=f.geometry.getBounds().getCenterLonLat();e.icon=
null==this.icon?OpenLayers.Marker.defaultIcon():this.icon.clone();e.popupSize=this.popupSize?this.popupSize.clone():new OpenLayers.Size(250,120);if(g||h){e.title=g;e.description=h;var l='<div class="olLayerGeoRSSClose">[x]</div>',l=l+'<div class="olLayerGeoRSSTitle">';k&&(l+='<a class="link" href="'+k+'" target="_blank">');l+=g;k&&(l+="</a>");l+="</div>";l+='<div style="" class="olLayerGeoRSSDescription">';l+=h;l+="</div>";e.popupContentHTML=l}f=new OpenLayers.Feature(this,f,e);this.features.push(f);
e=f.createMarker();e.events.register("click",f,this.markerClick);this.addMarker(e)}}this.events.triggerEvent("loadend")},markerClick:function(a){var b=this==this.layer.selectedFeature;this.layer.selectedFeature=b?null:this;for(var c=0,d=this.layer.map.popups.length;c<d;c++)this.layer.map.removePopup(this.layer.map.popups[c]);b||(b=this.createPopup(),OpenLayers.Event.observe(b.div,"click",OpenLayers.Function.bind(function(){for(var a=0,b=this.layer.map.popups.length;a<b;a++)this.layer.map.removePopup(this.layer.map.popups[a])},
this)),this.layer.map.addPopup(b));OpenLayers.Event.stop(a)},clearFeatures:function(){if(null!=this.features)for(;0<this.features.length;){var a=this.features[0];OpenLayers.Util.removeItem(this.features,a);a.destroy()}},CLASS_NAME:"OpenLayers.Layer.GeoRSS"});OpenLayers.Symbolizer.Point=OpenLayers.Class(OpenLayers.Symbolizer,{initialize:function(a){OpenLayers.Symbolizer.prototype.initialize.apply(this,arguments)},CLASS_NAME:"OpenLayers.Symbolizer.Point"});OpenLayers.Symbolizer.Line=OpenLayers.Class(OpenLayers.Symbolizer,{initialize:function(a){OpenLayers.Symbolizer.prototype.initialize.apply(this,arguments)},CLASS_NAME:"OpenLayers.Symbolizer.Line"});OpenLayers.Symbolizer.Text=OpenLayers.Class(OpenLayers.Symbolizer,{initialize:function(a){OpenLayers.Symbolizer.prototype.initialize.apply(this,arguments)},CLASS_NAME:"OpenLayers.Symbolizer.Text"});OpenLayers.Format.SLD.v1=OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0,{namespaces:{sld:"http://www.opengis.net/sld",ogc:"http://www.opengis.net/ogc",gml:"http://www.opengis.net/gml",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},defaultPrefix:"sld",schemaLocation:null,multipleSymbolizers:!1,featureTypeCounter:null,defaultSymbolizer:{fillColor:"#808080",fillOpacity:1,strokeColor:"#000000",strokeOpacity:1,strokeWidth:1,strokeDashstyle:"solid",pointRadius:3,
graphicName:"square"},read:function(a,b){b=OpenLayers.Util.applyDefaults(b,this.options);var c={namedLayers:!0===b.namedLayersAsArray?[]:{}};this.readChildNodes(a,c);return c},readers:OpenLayers.Util.applyDefaults({sld:{StyledLayerDescriptor:function(a,b){b.version=a.getAttribute("version");this.readChildNodes(a,b)},Name:function(a,b){b.name=this.getChildValue(a)},Title:function(a,b){b.title=this.getChildValue(a)},Abstract:function(a,b){b.description=this.getChildValue(a)},NamedLayer:function(a,b){var c=
{userStyles:[],namedStyles:[]};this.readChildNodes(a,c);for(var d=0,e=c.userStyles.length;d<e;++d)c.userStyles[d].layerName=c.name;OpenLayers.Util.isArray(b.namedLayers)?b.namedLayers.push(c):b.namedLayers[c.name]=c},NamedStyle:function(a,b){b.namedStyles.push(this.getChildName(a.firstChild))},UserStyle:function(a,b){var c={defaultsPerSymbolizer:!0,rules:[]};this.featureTypeCounter=-1;this.readChildNodes(a,c);this.multipleSymbolizers?(delete c.defaultsPerSymbolizer,c=new OpenLayers.Style2(c)):c=new OpenLayers.Style(this.defaultSymbolizer,
c);b.userStyles.push(c)},IsDefault:function(a,b){"1"==this.getChildValue(a)&&(b.isDefault=!0)},FeatureTypeStyle:function(a,b){++this.featureTypeCounter;var c={rules:this.multipleSymbolizers?b.rules:[]};this.readChildNodes(a,c);this.multipleSymbolizers||(b.rules=c.rules)},Rule:function(a,b){var c;this.multipleSymbolizers&&(c={symbolizers:[]});c=new OpenLayers.Rule(c);this.readChildNodes(a,c);b.rules.push(c)},ElseFilter:function(a,b){b.elseFilter=!0},MinScaleDenominator:function(a,b){b.minScaleDenominator=
parseFloat(this.getChildValue(a))},MaxScaleDenominator:function(a,b){b.maxScaleDenominator=parseFloat(this.getChildValue(a))},TextSymbolizer:function(a,b){var c={};this.readChildNodes(a,c);this.multipleSymbolizers?(c.zIndex=this.featureTypeCounter,b.symbolizers.push(new OpenLayers.Symbolizer.Text(c))):b.symbolizer.Text=OpenLayers.Util.applyDefaults(c,b.symbolizer.Text)},LabelPlacement:function(a,b){this.readChildNodes(a,b)},PointPlacement:function(a,b){var c={};this.readChildNodes(a,c);c.labelRotation=
c.rotation;delete c.rotation;var d,e=b.labelAnchorPointX,f=b.labelAnchorPointY;e<=1/3?d="l":e>1/3&&e<2/3?d="c":e>=2/3&&(d="r");f<=1/3?d+="b":f>1/3&&f<2/3?d+="m":f>=2/3&&(d+="t");c.labelAlign=d;OpenLayers.Util.applyDefaults(b,c)},AnchorPoint:function(a,b){this.readChildNodes(a,b)},AnchorPointX:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.labelAnchorPointX=c)},AnchorPointY:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.labelAnchorPointY=c)},Displacement:function(a,
b){this.readChildNodes(a,b)},DisplacementX:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.labelXOffset=c)},DisplacementY:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.labelYOffset=c)},LinePlacement:function(a,b){this.readChildNodes(a,b)},PerpendicularOffset:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.labelPerpendicularOffset=c)},Label:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.label=c)},Font:function(a,b){this.readChildNodes(a,
b)},Halo:function(a,b){var c={};this.readChildNodes(a,c);b.haloRadius=c.haloRadius;b.haloColor=c.fillColor;b.haloOpacity=c.fillOpacity},Radius:function(a,b){var c=this.readers.ogc._expression.call(this,a);null!=c&&(b.haloRadius=c)},RasterSymbolizer:function(a,b){var c={};this.readChildNodes(a,c);this.multipleSymbolizers?(c.zIndex=this.featureTypeCounter,b.symbolizers.push(new OpenLayers.Symbolizer.Raster(c))):b.symbolizer.Raster=OpenLayers.Util.applyDefaults(c,b.symbolizer.Raster)},Geometry:function(a,
b){b.geometry={};this.readChildNodes(a,b.geometry)},ColorMap:function(a,b){b.colorMap=[];this.readChildNodes(a,b.colorMap)},ColorMapEntry:function(a,b){var c=a.getAttribute("quantity"),d=a.getAttribute("opacity");b.push({color:a.getAttribute("color"),quantity:null!==c?parseFloat(c):void 0,label:a.getAttribute("label")||void 0,opacity:null!==d?parseFloat(d):void 0})},LineSymbolizer:function(a,b){var c={};this.readChildNodes(a,c);this.multipleSymbolizers?(c.zIndex=this.featureTypeCounter,b.symbolizers.push(new OpenLayers.Symbolizer.Line(c))):
b.symbolizer.Line=OpenLayers.Util.applyDefaults(c,b.symbolizer.Line)},PolygonSymbolizer:function(a,b){var c={fill:!1,stroke:!1};this.multipleSymbolizers||(c=b.symbolizer.Polygon||c);this.readChildNodes(a,c);this.multipleSymbolizers?(c.zIndex=this.featureTypeCounter,b.symbolizers.push(new OpenLayers.Symbolizer.Polygon(c))):b.symbolizer.Polygon=c},PointSymbolizer:function(a,b){var c={fill:!1,stroke:!1,graphic:!1};this.multipleSymbolizers||(c=b.symbolizer.Point||c);this.readChildNodes(a,c);this.multipleSymbolizers?
(c.zIndex=this.featureTypeCounter,b.symbolizers.push(new OpenLayers.Symbolizer.Point(c))):b.symbolizer.Point=c},Stroke:function(a,b){b.stroke=!0;this.readChildNodes(a,b)},Fill:function(a,b){b.fill=!0;this.readChildNodes(a,b)},CssParameter:function(a,b){var c=a.getAttribute("name"),d=this.cssMap[c];b.label&&("fill"===c?d="fontColor":"fill-opacity"===c&&(d="fontOpacity"));d&&(c=this.readers.ogc._expression.call(this,a))&&(b[d]=c)},Graphic:function(a,b){b.graphic=!0;var c={};this.readChildNodes(a,c);
for(var d="stroke strokeColor strokeWidth strokeOpacity strokeLinecap fill fillColor fillOpacity graphicName rotation graphicFormat".split(" "),e,f,g=0,h=d.length;g<h;++g)e=d[g],f=c[e],void 0!=f&&(b[e]=f);void 0!=c.opacity&&(b.graphicOpacity=c.opacity);void 0!=c.size&&(isNaN(c.size/2)?b.graphicWidth=c.size:b.pointRadius=c.size/2);void 0!=c.href&&(b.externalGraphic=c.href);void 0!=c.rotation&&(b.rotation=c.rotation)},ExternalGraphic:function(a,b){this.readChildNodes(a,b)},Mark:function(a,b){this.readChildNodes(a,
b)},WellKnownName:function(a,b){b.graphicName=this.getChildValue(a)},Opacity:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.opacity=c)},Size:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.size=c)},Rotation:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.rotation=c)},OnlineResource:function(a,b){b.href=this.getAttributeNS(a,this.namespaces.xlink,"href")},Format:function(a,b){b.graphicFormat=this.getChildValue(a)}}},OpenLayers.Format.Filter.v1_0_0.prototype.readers),
cssMap:{stroke:"strokeColor","stroke-opacity":"strokeOpacity","stroke-width":"strokeWidth","stroke-linecap":"strokeLinecap","stroke-dasharray":"strokeDashstyle",fill:"fillColor","fill-opacity":"fillOpacity","font-family":"fontFamily","font-size":"fontSize","font-weight":"fontWeight","font-style":"fontStyle"},getCssProperty:function(a){var b=null,c;for(c in this.cssMap)if(this.cssMap[c]==a){b=c;break}return b},getGraphicFormat:function(a){var b,c;for(c in this.graphicFormats)if(this.graphicFormats[c].test(a)){b=
c;break}return b||this.defaultGraphicFormat},defaultGraphicFormat:"image/png",graphicFormats:{"image/jpeg":/\.jpe?g$/i,"image/gif":/\.gif$/i,"image/png":/\.png$/i},write:function(a){return this.writers.sld.StyledLayerDescriptor.apply(this,[a])},writers:OpenLayers.Util.applyDefaults({sld:{_OGCExpression:function(a,b){var c=this.createElementNSPlus(a),d="string"==typeof b?b.split("${"):[b];c.appendChild(this.createTextNode(d[0]));for(var e,f,g=1,h=d.length;g<h;g++)e=d[g],f=e.indexOf("}"),0<f?(this.writeNode("ogc:PropertyName",
{property:e.substring(0,f)},c),c.appendChild(this.createTextNode(e.substring(++f)))):c.appendChild(this.createTextNode("${"+e));return c},StyledLayerDescriptor:function(a){var b=this.createElementNSPlus("sld:StyledLayerDescriptor",{attributes:{version:this.VERSION,"xsi:schemaLocation":this.schemaLocation}});b.setAttribute("xmlns:ogc",this.namespaces.ogc);b.setAttribute("xmlns:gml",this.namespaces.gml);a.name&&this.writeNode("Name",a.name,b);a.title&&this.writeNode("Title",a.title,b);a.description&&
this.writeNode("Abstract",a.description,b);if(OpenLayers.Util.isArray(a.namedLayers))for(var c=0,d=a.namedLayers.length;c<d;++c)this.writeNode("NamedLayer",a.namedLayers[c],b);else for(c in a.namedLayers)this.writeNode("NamedLayer",a.namedLayers[c],b);return b},Name:function(a){return this.createElementNSPlus("sld:Name",{value:a})},Title:function(a){return this.createElementNSPlus("sld:Title",{value:a})},Abstract:function(a){return this.createElementNSPlus("sld:Abstract",{value:a})},NamedLayer:function(a){var b=
this.createElementNSPlus("sld:NamedLayer");this.writeNode("Name",a.name,b);if(a.namedStyles)for(var c=0,d=a.namedStyles.length;c<d;++c)this.writeNode("NamedStyle",a.namedStyles[c],b);if(a.userStyles)for(c=0,d=a.userStyles.length;c<d;++c)this.writeNode("UserStyle",a.userStyles[c],b);return b},NamedStyle:function(a){var b=this.createElementNSPlus("sld:NamedStyle");this.writeNode("Name",a,b);return b},UserStyle:function(a){var b=this.createElementNSPlus("sld:UserStyle");a.name&&this.writeNode("Name",
a.name,b);a.title&&this.writeNode("Title",a.title,b);a.description&&this.writeNode("Abstract",a.description,b);a.isDefault&&this.writeNode("IsDefault",a.isDefault,b);if(this.multipleSymbolizers&&a.rules){for(var c={0:[]},d=[0],e,f,g,h,k,l=0,m=a.rules.length;l<m;++l)if(e=a.rules[l],e.symbolizers){f={};for(var n=0,p=e.symbolizers.length;n<p;++n)g=e.symbolizers[n],h=g.zIndex,h in f||(k=e.clone(),k.symbolizers=[],f[h]=k),f[h].symbolizers.push(g.clone());for(h in f)h in c||(d.push(h),c[h]=[]),c[h].push(f[h])}else c[0].push(e.clone());
d.sort();l=0;for(m=d.length;l<m;++l)e=c[d[l]],0<e.length&&(k=a.clone(),k.rules=c[d[l]],this.writeNode("FeatureTypeStyle",k,b))}else this.writeNode("FeatureTypeStyle",a,b);return b},IsDefault:function(a){return this.createElementNSPlus("sld:IsDefault",{value:a?"1":"0"})},FeatureTypeStyle:function(a){for(var b=this.createElementNSPlus("sld:FeatureTypeStyle"),c=0,d=a.rules.length;c<d;++c)this.writeNode("Rule",a.rules[c],b);return b},Rule:function(a){var b=this.createElementNSPlus("sld:Rule");a.name&&
this.writeNode("Name",a.name,b);a.title&&this.writeNode("Title",a.title,b);a.description&&this.writeNode("Abstract",a.description,b);a.elseFilter?this.writeNode("ElseFilter",null,b):a.filter&&this.writeNode("ogc:Filter",a.filter,b);void 0!=a.minScaleDenominator&&this.writeNode("MinScaleDenominator",a.minScaleDenominator,b);void 0!=a.maxScaleDenominator&&this.writeNode("MaxScaleDenominator",a.maxScaleDenominator,b);var c,d;if(this.multipleSymbolizers&&a.symbolizers)for(var e=0,f=a.symbolizers.length;e<
f;++e)d=a.symbolizers[e],c=d.CLASS_NAME.split(".").pop(),this.writeNode(c+"Symbolizer",d,b);else for(var f=OpenLayers.Style.SYMBOLIZER_PREFIXES,e=0,g=f.length;e<g;++e)c=f[e],(d=a.symbolizer[c])&&this.writeNode(c+"Symbolizer",d,b);return b},ElseFilter:function(){return this.createElementNSPlus("sld:ElseFilter")},MinScaleDenominator:function(a){return this.createElementNSPlus("sld:MinScaleDenominator",{value:a})},MaxScaleDenominator:function(a){return this.createElementNSPlus("sld:MaxScaleDenominator",
{value:a})},LineSymbolizer:function(a){var b=this.createElementNSPlus("sld:LineSymbolizer");this.writeNode("Stroke",a,b);return b},Stroke:function(a){var b=this.createElementNSPlus("sld:Stroke");void 0!=a.strokeColor&&this.writeNode("CssParameter",{symbolizer:a,key:"strokeColor"},b);void 0!=a.strokeOpacity&&this.writeNode("CssParameter",{symbolizer:a,key:"strokeOpacity"},b);void 0!=a.strokeWidth&&this.writeNode("CssParameter",{symbolizer:a,key:"strokeWidth"},b);void 0!=a.strokeDashstyle&&"solid"!==
a.strokeDashstyle&&this.writeNode("CssParameter",{symbolizer:a,key:"strokeDashstyle"},b);void 0!=a.strokeLinecap&&this.writeNode("CssParameter",{symbolizer:a,key:"strokeLinecap"},b);return b},CssParameter:function(a){return this.createElementNSPlus("sld:CssParameter",{attributes:{name:this.getCssProperty(a.key)},value:a.symbolizer[a.key]})},TextSymbolizer:function(a){var b=this.createElementNSPlus("sld:TextSymbolizer");null!=a.label&&this.writeNode("Label",a.label,b);null==a.fontFamily&&null==a.fontSize&&
null==a.fontWeight&&null==a.fontStyle||this.writeNode("Font",a,b);null==a.labelAnchorPointX&&null==a.labelAnchorPointY&&null==a.labelAlign&&null==a.labelXOffset&&null==a.labelYOffset&&null==a.labelRotation&&null==a.labelPerpendicularOffset||this.writeNode("LabelPlacement",a,b);null==a.haloRadius&&null==a.haloColor&&null==a.haloOpacity||this.writeNode("Halo",a,b);null==a.fontColor&&null==a.fontOpacity||this.writeNode("Fill",{fillColor:a.fontColor,fillOpacity:a.fontOpacity},b);return b},LabelPlacement:function(a){var b=
this.createElementNSPlus("sld:LabelPlacement");null==a.labelAnchorPointX&&null==a.labelAnchorPointY&&null==a.labelAlign&&null==a.labelXOffset&&null==a.labelYOffset&&null==a.labelRotation||null!=a.labelPerpendicularOffset||this.writeNode("PointPlacement",a,b);null!=a.labelPerpendicularOffset&&this.writeNode("LinePlacement",a,b);return b},LinePlacement:function(a){var b=this.createElementNSPlus("sld:LinePlacement");this.writeNode("PerpendicularOffset",a.labelPerpendicularOffset,b);return b},PerpendicularOffset:function(a){return this.createElementNSPlus("sld:PerpendicularOffset",
{value:a})},PointPlacement:function(a){var b=this.createElementNSPlus("sld:PointPlacement");null==a.labelAnchorPointX&&null==a.labelAnchorPointY&&null==a.labelAlign||this.writeNode("AnchorPoint",a,b);null==a.labelXOffset&&null==a.labelYOffset||this.writeNode("Displacement",a,b);null!=a.labelRotation&&this.writeNode("Rotation",a.labelRotation,b);return b},AnchorPoint:function(a){var b=this.createElementNSPlus("sld:AnchorPoint"),c=a.labelAnchorPointX,d=a.labelAnchorPointY;null!=c&&this.writeNode("AnchorPointX",
c,b);null!=d&&this.writeNode("AnchorPointY",d,b);if(null==c&&null==d){var e=a.labelAlign.substr(0,1);a=a.labelAlign.substr(1,1);"l"===e?c=0:"c"===e?c=0.5:"r"===e&&(c=1);"b"===a?d=0:"m"===a?d=0.5:"t"===a&&(d=1);this.writeNode("AnchorPointX",c,b);this.writeNode("AnchorPointY",d,b)}return b},AnchorPointX:function(a){return this.createElementNSPlus("sld:AnchorPointX",{value:a})},AnchorPointY:function(a){return this.createElementNSPlus("sld:AnchorPointY",{value:a})},Displacement:function(a){var b=this.createElementNSPlus("sld:Displacement");
null!=a.labelXOffset&&this.writeNode("DisplacementX",a.labelXOffset,b);null!=a.labelYOffset&&this.writeNode("DisplacementY",a.labelYOffset,b);return b},DisplacementX:function(a){return this.createElementNSPlus("sld:DisplacementX",{value:a})},DisplacementY:function(a){return this.createElementNSPlus("sld:DisplacementY",{value:a})},Font:function(a){var b=this.createElementNSPlus("sld:Font");a.fontFamily&&this.writeNode("CssParameter",{symbolizer:a,key:"fontFamily"},b);a.fontSize&&this.writeNode("CssParameter",
{symbolizer:a,key:"fontSize"},b);a.fontWeight&&this.writeNode("CssParameter",{symbolizer:a,key:"fontWeight"},b);a.fontStyle&&this.writeNode("CssParameter",{symbolizer:a,key:"fontStyle"},b);return b},Label:function(a){return this.writers.sld._OGCExpression.call(this,"sld:Label",a)},Halo:function(a){var b=this.createElementNSPlus("sld:Halo");a.haloRadius&&this.writeNode("Radius",a.haloRadius,b);(a.haloColor||a.haloOpacity)&&this.writeNode("Fill",{fillColor:a.haloColor,fillOpacity:a.haloOpacity},b);
return b},Radius:function(a){return this.createElementNSPlus("sld:Radius",{value:a})},RasterSymbolizer:function(a){var b=this.createElementNSPlus("sld:RasterSymbolizer");a.geometry&&this.writeNode("Geometry",a.geometry,b);a.opacity&&this.writeNode("Opacity",a.opacity,b);a.colorMap&&this.writeNode("ColorMap",a.colorMap,b);return b},Geometry:function(a){var b=this.createElementNSPlus("sld:Geometry");a.property&&this.writeNode("ogc:PropertyName",a,b);return b},ColorMap:function(a){for(var b=this.createElementNSPlus("sld:ColorMap"),
c=0,d=a.length;c<d;++c)this.writeNode("ColorMapEntry",a[c],b);return b},ColorMapEntry:function(a){var b=this.createElementNSPlus("sld:ColorMapEntry");b.setAttribute("color",a.color);void 0!==a.opacity&&b.setAttribute("opacity",parseFloat(a.opacity));void 0!==a.quantity&&b.setAttribute("quantity",parseFloat(a.quantity));void 0!==a.label&&b.setAttribute("label",a.label);return b},PolygonSymbolizer:function(a){var b=this.createElementNSPlus("sld:PolygonSymbolizer");!1!==a.fill&&this.writeNode("Fill",
a,b);!1!==a.stroke&&this.writeNode("Stroke",a,b);return b},Fill:function(a){var b=this.createElementNSPlus("sld:Fill");a.fillColor&&this.writeNode("CssParameter",{symbolizer:a,key:"fillColor"},b);null!=a.fillOpacity&&this.writeNode("CssParameter",{symbolizer:a,key:"fillOpacity"},b);return b},PointSymbolizer:function(a){var b=this.createElementNSPlus("sld:PointSymbolizer");this.writeNode("Graphic",a,b);return b},Graphic:function(a){var b=this.createElementNSPlus("sld:Graphic");void 0!=a.externalGraphic?
this.writeNode("ExternalGraphic",a,b):this.writeNode("Mark",a,b);void 0!=a.graphicOpacity&&this.writeNode("Opacity",a.graphicOpacity,b);void 0!=a.pointRadius?this.writeNode("Size",2*a.pointRadius,b):void 0!=a.graphicWidth&&this.writeNode("Size",a.graphicWidth,b);void 0!=a.rotation&&this.writeNode("Rotation",a.rotation,b);return b},ExternalGraphic:function(a){var b=this.createElementNSPlus("sld:ExternalGraphic");this.writeNode("OnlineResource",a.externalGraphic,b);a=a.graphicFormat||this.getGraphicFormat(a.externalGraphic);
this.writeNode("Format",a,b);return b},Mark:function(a){var b=this.createElementNSPlus("sld:Mark");a.graphicName&&this.writeNode("WellKnownName",a.graphicName,b);!1!==a.fill&&this.writeNode("Fill",a,b);!1!==a.stroke&&this.writeNode("Stroke",a,b);return b},WellKnownName:function(a){return this.createElementNSPlus("sld:WellKnownName",{value:a})},Opacity:function(a){return this.createElementNSPlus("sld:Opacity",{value:a})},Size:function(a){return this.writers.sld._OGCExpression.call(this,"sld:Size",
a)},Rotation:function(a){return this.createElementNSPlus("sld:Rotation",{value:a})},OnlineResource:function(a){return this.createElementNSPlus("sld:OnlineResource",{attributes:{"xlink:type":"simple","xlink:href":a}})},Format:function(a){return this.createElementNSPlus("sld:Format",{value:a})}}},OpenLayers.Format.Filter.v1_0_0.prototype.writers),CLASS_NAME:"OpenLayers.Format.SLD.v1"});OpenLayers.Layer.WMS=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{service:"WMS",version:"1.1.1",request:"GetMap",styles:"",format:"image/jpeg"},isBaseLayer:!0,encodeBBOX:!1,noMagic:!1,yx:{},initialize:function(a,b,c,d){var e=[];c=OpenLayers.Util.upperCaseObject(c);1.3<=parseFloat(c.VERSION)&&!c.EXCEPTIONS&&(c.EXCEPTIONS="INIMAGE");e.push(a,b,c,d);OpenLayers.Layer.Grid.prototype.initialize.apply(this,e);OpenLayers.Util.applyDefaults(this.params,OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));
!this.noMagic&&(this.params.TRANSPARENT&&"true"==this.params.TRANSPARENT.toString().toLowerCase())&&(null!=d&&d.isBaseLayer||(this.isBaseLayer=!1),"image/jpeg"==this.params.FORMAT&&(this.params.FORMAT=OpenLayers.Util.alphaHack()?"image/gif":"image/png"))},clone:function(a){null==a&&(a=new OpenLayers.Layer.WMS(this.name,this.url,this.params,this.getOptions()));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,[a])},reverseAxisOrder:function(){var a=this.projection.getCode();return 1.3<=parseFloat(this.params.VERSION)&&
!!(this.yx[a]||OpenLayers.Projection.defaults[a]&&OpenLayers.Projection.defaults[a].yx)},getURL:function(a){a=this.adjustBounds(a);var b=this.getImageSize(),c={},d=this.reverseAxisOrder();c.BBOX=this.encodeBBOX?a.toBBOX(null,d):a.toArray(d);c.WIDTH=b.w;c.HEIGHT=b.h;return this.getFullRequestString(c)},mergeNewParams:function(a){a=[OpenLayers.Util.upperCaseObject(a)];return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,a)},getFullRequestString:function(a,b){var c=this.map.getProjectionObject(),
c=this.projection&&this.projection.equals(c)?this.projection.getCode():c.getCode(),c="none"==c?null:c;1.3<=parseFloat(this.params.VERSION)?this.params.CRS=c:this.params.SRS=c;"boolean"==typeof this.params.TRANSPARENT&&(a.TRANSPARENT=this.params.TRANSPARENT?"TRUE":"FALSE");return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this,arguments)},CLASS_NAME:"OpenLayers.Layer.WMS"});OpenLayers.Layer.KaMap=OpenLayers.Class(OpenLayers.Layer.Grid,{isBaseLayer:!0,DEFAULT_PARAMS:{i:"jpeg",map:""},initialize:function(a,b,c,d){OpenLayers.Layer.Grid.prototype.initialize.apply(this,arguments);this.params=OpenLayers.Util.applyDefaults(this.params,this.DEFAULT_PARAMS)},getURL:function(a){a=this.adjustBounds(a);var b=this.map.getResolution(),c=Math.round(1E4*this.map.getScale())/1E4,d=Math.round(a.left/b);a=-Math.round(a.top/b);return this.getFullRequestString({t:a,l:d,s:c})},calculateGridLayout:function(a,
b,c){b=c*this.tileSize.w;c*=this.tileSize.h;return{tilelon:b,tilelat:c,startcol:Math.floor(a.left/b)-this.buffer,startrow:Math.floor(a.top/c)+this.buffer}},getTileBoundsForGridIndex:function(a,b){this.getTileOrigin();var c=this.gridLayout,d=c.tilelon,e=c.tilelat,f=(c.startcol+b)*d,c=(c.startrow-a)*e;return new OpenLayers.Bounds(f,c,f+d,c+e)},clone:function(a){null==a&&(a=new OpenLayers.Layer.KaMap(this.name,this.url,this.params,this.getOptions()));a=OpenLayers.Layer.Grid.prototype.clone.apply(this,
[a]);null!=this.tileSize&&(a.tileSize=this.tileSize.clone());a.grid=[];return a},getTileBounds:function(a){var b=this.getResolution(),c=b*this.tileSize.w,b=b*this.tileSize.h,d=this.getLonLatFromViewPortPx(a);a=c*Math.floor(d.lon/c);d=b*Math.floor(d.lat/b);return new OpenLayers.Bounds(a,d,a+c,d+b)},CLASS_NAME:"OpenLayers.Layer.KaMap"});OpenLayers.Format.WMC.v1_1_0=OpenLayers.Class(OpenLayers.Format.WMC.v1,{VERSION:"1.1.0",schemaLocation:"http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd",initialize:function(a){OpenLayers.Format.WMC.v1.prototype.initialize.apply(this,[a])},read_sld_MinScaleDenominator:function(a,b){var c=parseFloat(this.getChildValue(b));0<c&&(a.maxScale=c)},read_sld_MaxScaleDenominator:function(a,b){a.minScale=parseFloat(this.getChildValue(b))},read_wmc_SRS:function(a,b){"srs"in
a||(a.srs={});a.srs[this.getChildValue(b)]=!0},write_wmc_Layer:function(a){var b=OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(this,[a]);if(a.maxScale){var c=this.createElementNS(this.namespaces.sld,"sld:MinScaleDenominator");c.appendChild(this.createTextNode(a.maxScale.toPrecision(16)));b.appendChild(c)}a.minScale&&(c=this.createElementNS(this.namespaces.sld,"sld:MaxScaleDenominator"),c.appendChild(this.createTextNode(a.minScale.toPrecision(16))),b.appendChild(c));if(a.srs)for(var d in a.srs)b.appendChild(this.createElementDefaultNS("SRS",
d));b.appendChild(this.write_wmc_FormatList(a));b.appendChild(this.write_wmc_StyleList(a));a.dimensions&&b.appendChild(this.write_wmc_DimensionList(a));b.appendChild(this.write_wmc_LayerExtension(a));return b},CLASS_NAME:"OpenLayers.Format.WMC.v1_1_0"});OpenLayers.Format.XLS=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.1.0",stringifyOutput:!0,CLASS_NAME:"OpenLayers.Format.XLS"});OpenLayers.Format.XLS.v1=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{xls:"http://www.opengis.net/xls",gml:"http://www.opengis.net/gml",xsi:"http://www.w3.org/2001/XMLSchema-instance"},regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},xy:!0,defaultPrefix:"xls",schemaLocation:null,read:function(a,b){OpenLayers.Util.applyDefaults(b,this.options);var c={};this.readChildNodes(a,c);return c},readers:{xls:{XLS:function(a,b){b.version=a.getAttribute("version");
this.readChildNodes(a,b)},Response:function(a,b){this.readChildNodes(a,b)},GeocodeResponse:function(a,b){b.responseLists=[];this.readChildNodes(a,b)},GeocodeResponseList:function(a,b){var c={features:[],numberOfGeocodedAddresses:parseInt(a.getAttribute("numberOfGeocodedAddresses"))};b.responseLists.push(c);this.readChildNodes(a,c)},GeocodedAddress:function(a,b){var c=new OpenLayers.Feature.Vector;b.features.push(c);this.readChildNodes(a,c);c.geometry=c.components[0]},GeocodeMatchCode:function(a,b){b.attributes.matchCode=
{accuracy:parseFloat(a.getAttribute("accuracy")),matchType:a.getAttribute("matchType")}},Address:function(a,b){var c={countryCode:a.getAttribute("countryCode"),addressee:a.getAttribute("addressee"),street:[],place:[]};b.attributes.address=c;this.readChildNodes(a,c)},freeFormAddress:function(a,b){b.freeFormAddress=this.getChildValue(a)},StreetAddress:function(a,b){this.readChildNodes(a,b)},Building:function(a,b){b.building={number:a.getAttribute("number"),subdivision:a.getAttribute("subdivision"),
buildingName:a.getAttribute("buildingName")}},Street:function(a,b){b.street.push(this.getChildValue(a))},Place:function(a,b){b.place[a.getAttribute("type")]=this.getChildValue(a)},PostalCode:function(a,b){b.postalCode=this.getChildValue(a)}},gml:OpenLayers.Format.GML.v3.prototype.readers.gml},write:function(a){return this.writers.xls.XLS.apply(this,[a])},writers:{xls:{XLS:function(a){var b=this.createElementNSPlus("xls:XLS",{attributes:{version:this.VERSION,"xsi:schemaLocation":this.schemaLocation}});
this.writeNode("RequestHeader",a.header,b);this.writeNode("Request",a,b);return b},RequestHeader:function(a){return this.createElementNSPlus("xls:RequestHeader")},Request:function(a){var b=this.createElementNSPlus("xls:Request",{attributes:{methodName:"GeocodeRequest",requestID:a.requestID||"",version:this.VERSION}});this.writeNode("GeocodeRequest",a.addresses,b);return b},GeocodeRequest:function(a){for(var b=this.createElementNSPlus("xls:GeocodeRequest"),c=0,d=a.length;c<d;c++)this.writeNode("Address",
a[c],b);return b},Address:function(a){var b=this.createElementNSPlus("xls:Address",{attributes:{countryCode:a.countryCode}});a.freeFormAddress?this.writeNode("freeFormAddress",a.freeFormAddress,b):(a.street&&this.writeNode("StreetAddress",a,b),a.municipality&&this.writeNode("Municipality",a.municipality,b),a.countrySubdivision&&this.writeNode("CountrySubdivision",a.countrySubdivision,b),a.postalCode&&this.writeNode("PostalCode",a.postalCode,b));return b},freeFormAddress:function(a){return this.createElementNSPlus("freeFormAddress",
{value:a})},StreetAddress:function(a){var b=this.createElementNSPlus("xls:StreetAddress");a.building&&this.writeNode(b,"Building",a.building);a=a.street;OpenLayers.Util.isArray(a)||(a=[a]);for(var c=0,d=a.length;c<d;c++)this.writeNode("Street",a[c],b);return b},Building:function(a){return this.createElementNSPlus("xls:Building",{attributes:{number:a.number,subdivision:a.subdivision,buildingName:a.buildingName}})},Street:function(a){return this.createElementNSPlus("xls:Street",{value:a})},Municipality:function(a){return this.createElementNSPlus("xls:Place",
{attributes:{type:"Municipality"},value:a})},CountrySubdivision:function(a){return this.createElementNSPlus("xls:Place",{attributes:{type:"CountrySubdivision"},value:a})},PostalCode:function(a){return this.createElementNSPlus("xls:PostalCode",{value:a})}}},CLASS_NAME:"OpenLayers.Format.XLS.v1"});OpenLayers.Format.XLS.v1_1_0=OpenLayers.Class(OpenLayers.Format.XLS.v1,{VERSION:"1.1",schemaLocation:"http://www.opengis.net/xls http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd",CLASS_NAME:"OpenLayers.Format.XLS.v1_1_0"});OpenLayers.Format.XLS.v1_1=OpenLayers.Format.XLS.v1_1_0;OpenLayers.Renderer.SVG=OpenLayers.Class(OpenLayers.Renderer.Elements,{xmlns:"http://www.w3.org/2000/svg",xlinkns:"http://www.w3.org/1999/xlink",MAX_PIXEL:15E3,translationParameters:null,symbolMetrics:null,initialize:function(a){this.supported()&&(OpenLayers.Renderer.Elements.prototype.initialize.apply(this,arguments),this.translationParameters={x:0,y:0},this.symbolMetrics={})},supported:function(){return document.implementation&&(document.implementation.hasFeature("org.w3c.svg","1.0")||document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#SVG",
"1.1")||document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1"))},inValidRange:function(a,b,c){a+=c?0:this.translationParameters.x;b+=c?0:this.translationParameters.y;return a>=-this.MAX_PIXEL&&a<=this.MAX_PIXEL&&b>=-this.MAX_PIXEL&&b<=this.MAX_PIXEL},setExtent:function(a,b){var c=OpenLayers.Renderer.Elements.prototype.setExtent.apply(this,arguments),d=this.getResolution(),e=-a.left/d,d=a.top/d;if(b)return this.left=e,this.top=d,this.rendererRoot.setAttributeNS(null,
"viewBox","0 0 "+this.size.w+" "+this.size.h),this.translate(this.xOffset,0),!0;(e=this.translate(e-this.left+this.xOffset,d-this.top))||this.setExtent(a,!0);return c&&e},translate:function(a,b){if(this.inValidRange(a,b,!0)){var c="";if(a||b)c="translate("+a+","+b+")";this.root.setAttributeNS(null,"transform",c);this.translationParameters={x:a,y:b};return!0}return!1},setSize:function(a){OpenLayers.Renderer.prototype.setSize.apply(this,arguments);this.rendererRoot.setAttributeNS(null,"width",this.size.w);
this.rendererRoot.setAttributeNS(null,"height",this.size.h)},getNodeType:function(a,b){var c=null;switch(a.CLASS_NAME){case "OpenLayers.Geometry.Point":c=b.externalGraphic?"image":this.isComplexSymbol(b.graphicName)?"svg":"circle";break;case "OpenLayers.Geometry.Rectangle":c="rect";break;case "OpenLayers.Geometry.LineString":c="polyline";break;case "OpenLayers.Geometry.LinearRing":c="polygon";break;case "OpenLayers.Geometry.Polygon":case "OpenLayers.Geometry.Curve":c="path"}return c},setStyle:function(a,
b,c){b=b||a._style;c=c||a._options;var d=b.title||b.graphicTitle;if(d){a.setAttributeNS(null,"title",d);var e=a.getElementsByTagName("title");0<e.length?e[0].firstChild.textContent=d:(e=this.nodeFactory(null,"title"),e.textContent=d,a.appendChild(e))}var e=parseFloat(a.getAttributeNS(null,"r")),d=1,f;if("OpenLayers.Geometry.Point"==a._geometryClass&&e){a.style.visibility="";if(!1===b.graphic)a.style.visibility="hidden";else if(b.externalGraphic){f=this.getPosition(a);b.graphicWidth&&b.graphicHeight&&
a.setAttributeNS(null,"preserveAspectRatio","none");var e=b.graphicWidth||b.graphicHeight,g=b.graphicHeight||b.graphicWidth,e=e?e:2*b.pointRadius,g=g?g:2*b.pointRadius,h=void 0!=b.graphicYOffset?b.graphicYOffset:-(0.5*g),k=b.graphicOpacity||b.fillOpacity;a.setAttributeNS(null,"x",(f.x+(void 0!=b.graphicXOffset?b.graphicXOffset:-(0.5*e))).toFixed());a.setAttributeNS(null,"y",(f.y+h).toFixed());a.setAttributeNS(null,"width",e);a.setAttributeNS(null,"height",g);a.setAttributeNS(this.xlinkns,"xlink:href",
b.externalGraphic);a.setAttributeNS(null,"style","opacity: "+k);a.onclick=OpenLayers.Event.preventDefault}else if(this.isComplexSymbol(b.graphicName)){var e=3*b.pointRadius,g=2*e,l=this.importSymbol(b.graphicName);f=this.getPosition(a);d=3*this.symbolMetrics[l.id][0]/g;h=a.parentNode;k=a.nextSibling;h&&h.removeChild(a);a.firstChild&&a.removeChild(a.firstChild);a.appendChild(l.firstChild.cloneNode(!0));a.setAttributeNS(null,"viewBox",l.getAttributeNS(null,"viewBox"));a.setAttributeNS(null,"width",
g);a.setAttributeNS(null,"height",g);a.setAttributeNS(null,"x",f.x-e);a.setAttributeNS(null,"y",f.y-e);k?h.insertBefore(a,k):h&&h.appendChild(a)}else a.setAttributeNS(null,"r",b.pointRadius);e=b.rotation;void 0===e&&void 0===a._rotation||!f||(a._rotation=e,e|=0,"svg"!==a.nodeName?a.setAttributeNS(null,"transform","rotate("+e+" "+f.x+" "+f.y+")"):(f=this.symbolMetrics[l.id],a.firstChild.setAttributeNS(null,"transform","rotate("+e+" "+f[1]+" "+f[2]+")")))}c.isFilled?(a.setAttributeNS(null,"fill",b.fillColor),
a.setAttributeNS(null,"fill-opacity",b.fillOpacity)):a.setAttributeNS(null,"fill","none");c.isStroked?(a.setAttributeNS(null,"stroke",b.strokeColor),a.setAttributeNS(null,"stroke-opacity",b.strokeOpacity),a.setAttributeNS(null,"stroke-width",b.strokeWidth*d),a.setAttributeNS(null,"stroke-linecap",b.strokeLinecap||"round"),a.setAttributeNS(null,"stroke-linejoin","round"),b.strokeDashstyle&&a.setAttributeNS(null,"stroke-dasharray",this.dashStyle(b,d))):a.setAttributeNS(null,"stroke","none");b.pointerEvents&&
a.setAttributeNS(null,"pointer-events",b.pointerEvents);null!=b.cursor&&a.setAttributeNS(null,"cursor",b.cursor);return a},dashStyle:function(a,b){var c=a.strokeWidth*b,d=a.strokeDashstyle;switch(d){case "solid":return"none";case "dot":return[1,4*c].join();case "dash":return[4*c,4*c].join();case "dashdot":return[4*c,4*c,1,4*c].join();case "longdash":return[8*c,4*c].join();case "longdashdot":return[8*c,4*c,1,4*c].join();default:return OpenLayers.String.trim(d).replace(/\s+/g,",")}},createNode:function(a,
b){var c=document.createElementNS(this.xmlns,a);b&&c.setAttributeNS(null,"id",b);return c},nodeTypeCompare:function(a,b){return b==a.nodeName},createRenderRoot:function(){var a=this.nodeFactory(this.container.id+"_svgRoot","svg");a.style.display="block";return a},createRoot:function(a){return this.nodeFactory(this.container.id+a,"g")},createDefs:function(){var a=this.nodeFactory(this.container.id+"_defs","defs");this.rendererRoot.appendChild(a);return a},drawPoint:function(a,b){return this.drawCircle(a,
b,1)},drawCircle:function(a,b,c){var d=this.getResolution(),e=(b.x-this.featureDx)/d+this.left;b=this.top-b.y/d;return this.inValidRange(e,b)?(a.setAttributeNS(null,"cx",e),a.setAttributeNS(null,"cy",b),a.setAttributeNS(null,"r",c),a):!1},drawLineString:function(a,b){var c=this.getComponentsString(b.components);return c.path?(a.setAttributeNS(null,"points",c.path),c.complete?a:null):!1},drawLinearRing:function(a,b){var c=this.getComponentsString(b.components);return c.path?(a.setAttributeNS(null,
"points",c.path),c.complete?a:null):!1},drawPolygon:function(a,b){for(var c="",d=!0,e=!0,f,g,h=0,k=b.components.length;h<k;h++)c+=" M",f=this.getComponentsString(b.components[h].components," "),(g=f.path)?(c+=" "+g,e=f.complete&&e):d=!1;return d?(a.setAttributeNS(null,"d",c+" z"),a.setAttributeNS(null,"fill-rule","evenodd"),e?a:null):!1},drawRectangle:function(a,b){var c=this.getResolution(),d=(b.x-this.featureDx)/c+this.left,e=this.top-b.y/c;return this.inValidRange(d,e)?(a.setAttributeNS(null,"x",
d),a.setAttributeNS(null,"y",e),a.setAttributeNS(null,"width",b.width/c),a.setAttributeNS(null,"height",b.height/c),a):!1},drawText:function(a,b,c){var d=!!b.labelOutlineWidth;if(d){var e=OpenLayers.Util.extend({},b);e.fontColor=e.labelOutlineColor;e.fontStrokeColor=e.labelOutlineColor;e.fontStrokeWidth=b.labelOutlineWidth;b.labelOutlineOpacity&&(e.fontOpacity=b.labelOutlineOpacity);delete e.labelOutlineWidth;this.drawText(a,e,c)}var f=this.getResolution(),e=(c.x-this.featureDx)/f+this.left,g=c.y/
f-this.top,d=d?this.LABEL_OUTLINE_SUFFIX:this.LABEL_ID_SUFFIX,f=this.nodeFactory(a+d,"text");f.setAttributeNS(null,"x",e);f.setAttributeNS(null,"y",-g);b.fontColor&&f.setAttributeNS(null,"fill",b.fontColor);b.fontStrokeColor&&f.setAttributeNS(null,"stroke",b.fontStrokeColor);b.fontStrokeWidth&&f.setAttributeNS(null,"stroke-width",b.fontStrokeWidth);b.fontOpacity&&f.setAttributeNS(null,"opacity",b.fontOpacity);b.fontFamily&&f.setAttributeNS(null,"font-family",b.fontFamily);b.fontSize&&f.setAttributeNS(null,
"font-size",b.fontSize);b.fontWeight&&f.setAttributeNS(null,"font-weight",b.fontWeight);b.fontStyle&&f.setAttributeNS(null,"font-style",b.fontStyle);!0===b.labelSelect?(f.setAttributeNS(null,"pointer-events","visible"),f._featureId=a):f.setAttributeNS(null,"pointer-events","none");g=b.labelAlign||OpenLayers.Renderer.defaultSymbolizer.labelAlign;f.setAttributeNS(null,"text-anchor",OpenLayers.Renderer.SVG.LABEL_ALIGN[g[0]]||"middle");!0===OpenLayers.IS_GECKO&&f.setAttributeNS(null,"dominant-baseline",
OpenLayers.Renderer.SVG.LABEL_ALIGN[g[1]]||"central");for(var h=b.label.split("\n"),k=h.length;f.childNodes.length>k;)f.removeChild(f.lastChild);for(var l=0;l<k;l++){var m=this.nodeFactory(a+d+"_tspan_"+l,"tspan");!0===b.labelSelect&&(m._featureId=a,m._geometry=c,m._geometryClass=c.CLASS_NAME);!1===OpenLayers.IS_GECKO&&m.setAttributeNS(null,"baseline-shift",OpenLayers.Renderer.SVG.LABEL_VSHIFT[g[1]]||"-35%");m.setAttribute("x",e);if(0==l){var n=OpenLayers.Renderer.SVG.LABEL_VFACTOR[g[1]];null==n&&
(n=-0.5);m.setAttribute("dy",n*(k-1)+"em")}else m.setAttribute("dy","1em");m.textContent=""===h[l]?" ":h[l];m.parentNode||f.appendChild(m)}f.parentNode||this.textRoot.appendChild(f)},getComponentsString:function(a,b){for(var c=[],d=!0,e=a.length,f=[],g,h=0;h<e;h++)g=a[h],c.push(g),(g=this.getShortString(g))?f.push(g):(0<h&&this.getShortString(a[h-1])&&f.push(this.clipLine(a[h],a[h-1])),h<e-1&&this.getShortString(a[h+1])&&f.push(this.clipLine(a[h],a[h+1])),d=!1);return{path:f.join(b||","),complete:d}},
clipLine:function(a,b){if(b.equals(a))return"";var c=this.getResolution(),d=this.MAX_PIXEL-this.translationParameters.x,e=this.MAX_PIXEL-this.translationParameters.y,f=(b.x-this.featureDx)/c+this.left,g=this.top-b.y/c,h=(a.x-this.featureDx)/c+this.left,c=this.top-a.y/c,k;if(h<-d||h>d)k=(c-g)/(h-f),h=0>h?-d:d,c=g+(h-f)*k;if(c<-e||c>e)k=(h-f)/(c-g),c=0>c?-e:e,h=f+(c-g)*k;return h+","+c},getShortString:function(a){var b=this.getResolution(),c=(a.x-this.featureDx)/b+this.left;a=this.top-a.y/b;return this.inValidRange(c,
a)?c+","+a:!1},getPosition:function(a){return{x:parseFloat(a.getAttributeNS(null,"cx")),y:parseFloat(a.getAttributeNS(null,"cy"))}},importSymbol:function(a){this.defs||(this.defs=this.createDefs());var b=this.container.id+"-"+a,c=document.getElementById(b);if(null!=c)return c;var d=OpenLayers.Renderer.symbol[a];if(!d)throw Error(a+" is not a valid symbol name");a=this.nodeFactory(b,"symbol");var e=this.nodeFactory(null,"polygon");a.appendChild(e);for(var c=new OpenLayers.Bounds(Number.MAX_VALUE,Number.MAX_VALUE,
0,0),f=[],g,h,k=0;k<d.length;k+=2)g=d[k],h=d[k+1],c.left=Math.min(c.left,g),c.bottom=Math.min(c.bottom,h),c.right=Math.max(c.right,g),c.top=Math.max(c.top,h),f.push(g,",",h);e.setAttributeNS(null,"points",f.join(" "));d=c.getWidth();e=c.getHeight();a.setAttributeNS(null,"viewBox",[c.left-d,c.bottom-e,3*d,3*e].join(" "));this.symbolMetrics[b]=[Math.max(d,e),c.getCenterLonLat().lon,c.getCenterLonLat().lat];this.defs.appendChild(a);return a},getFeatureIdFromEvent:function(a){var b=OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this,
arguments);b||(b=a.target,b=b.parentNode&&b!=this.rendererRoot?b.parentNode._featureId:void 0);return b},CLASS_NAME:"OpenLayers.Renderer.SVG"});OpenLayers.Renderer.SVG.LABEL_ALIGN={l:"start",r:"end",b:"bottom",t:"hanging"};OpenLayers.Renderer.SVG.LABEL_VSHIFT={t:"-70%",b:"0"};OpenLayers.Renderer.SVG.LABEL_VFACTOR={t:0,b:-1};OpenLayers.Renderer.SVG.preventDefault=function(a){OpenLayers.Event.preventDefault(a)};OpenLayers.Format.SLD.v1_0_0=OpenLayers.Class(OpenLayers.Format.SLD.v1,{VERSION:"1.0.0",schemaLocation:"http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd",CLASS_NAME:"OpenLayers.Format.SLD.v1_0_0"});OpenLayers.Format.OWSContext=OpenLayers.Class(OpenLayers.Format.Context,{defaultVersion:"0.3.1",getVersion:function(a,b){var c=OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(this,arguments);"0.3.0"===c&&(c=this.defaultVersion);return c},toContext:function(a){var b={};"OpenLayers.Map"==a.CLASS_NAME&&(b.bounds=a.getExtent(),b.maxExtent=a.maxExtent,b.projection=a.projection,b.size=a.getSize(),b.layers=a.layers);return b},CLASS_NAME:"OpenLayers.Format.OWSContext"});OpenLayers.Format.OWSContext.v0_3_1=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{owc:"http://www.opengis.net/ows-context",gml:"http://www.opengis.net/gml",kml:"http://www.opengis.net/kml/2.2",ogc:"http://www.opengis.net/ogc",ows:"http://www.opengis.net/ows",sld:"http://www.opengis.net/sld",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},VERSION:"0.3.1",schemaLocation:"http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd",
defaultPrefix:"owc",extractAttributes:!0,xy:!0,regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},featureNS:"http://mapserver.gis.umn.edu/mapserver",featureType:"vector",geometryName:"geometry",nestingLayerLookup:null,initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a]);OpenLayers.Format.GML.v2.prototype.setGeometryTypes.call(this)},setNestingPath:function(a){if(a.layersContext)for(var b=0,c=a.layersContext.length;b<c;b++){var d=
a.layersContext[b],e=[],f=a.title||"";a.metadata&&a.metadata.nestingPath&&(e=a.metadata.nestingPath.slice());""!=f&&e.push(f);d.metadata.nestingPath=e;d.layersContext&&this.setNestingPath(d)}},decomposeNestingPath:function(a){var b=[];if(OpenLayers.Util.isArray(a)){for(a=a.slice();0<a.length;)b.push(a.slice()),a.pop();b.reverse()}return b},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={};this.readNode(a,
b);this.setNestingPath({layersContext:b.layersContext});a=[];this.processLayer(a,b);delete b.layersContext;b.layersContext=a;return b},processLayer:function(a,b){if(b.layersContext)for(var c=0,d=b.layersContext.length;c<d;c++){var e=b.layersContext[c];a.push(e);e.layersContext&&this.processLayer(a,e)}},write:function(a,b){this.nestingLayerLookup={};b=b||{};OpenLayers.Util.applyDefaults(b,a);var c=this.writeNode("OWSContext",b);this.nestingLayerLookup=null;this.setAttributeNS(c,this.namespaces.xsi,
"xsi:schemaLocation",this.schemaLocation);return OpenLayers.Format.XML.prototype.write.apply(this,[c])},readers:{kml:{Document:function(a,b){b.features=(new OpenLayers.Format.KML({kmlns:this.namespaces.kml,extractStyles:!0})).read(a)}},owc:{OWSContext:function(a,b){this.readChildNodes(a,b)},General:function(a,b){this.readChildNodes(a,b)},ResourceList:function(a,b){this.readChildNodes(a,b)},Layer:function(a,b){var c={metadata:{},visibility:"1"!=a.getAttribute("hidden"),queryable:"1"==a.getAttribute("queryable"),
opacity:null!=a.getAttribute("opacity")?parseFloat(a.getAttribute("opacity")):null,name:a.getAttribute("name"),categoryLayer:null==a.getAttribute("name"),formats:[],styles:[]};b.layersContext||(b.layersContext=[]);b.layersContext.push(c);this.readChildNodes(a,c)},InlineGeometry:function(a,b){b.features=[];var c=this.getElementsByTagNameNS(a,this.namespaces.gml,"featureMember"),d;1<=c.length&&(d=c[0]);d&&d.firstChild&&(c=d.firstChild.nextSibling?d.firstChild.nextSibling:d.firstChild,this.setNamespace("feature",
c.namespaceURI),this.featureType=c.localName||c.nodeName.split(":").pop(),this.readChildNodes(a,b))},Server:function(a,b){if(!b.service&&!b.version||b.service!=OpenLayers.Format.Context.serviceTypes.WMS)b.service=a.getAttribute("service"),b.version=a.getAttribute("version"),this.readChildNodes(a,b)},Name:function(a,b){b.name=this.getChildValue(a);this.readChildNodes(a,b)},Title:function(a,b){b.title=this.getChildValue(a);this.readChildNodes(a,b)},StyleList:function(a,b){this.readChildNodes(a,b.styles)},
Style:function(a,b){var c={};b.push(c);this.readChildNodes(a,c)},LegendURL:function(a,b){var c={};b.legend=c;this.readChildNodes(a,c)},OnlineResource:function(a,b){b.url=this.getAttributeNS(a,this.namespaces.xlink,"href");this.readChildNodes(a,b)}},ows:OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows,gml:OpenLayers.Format.GML.v2.prototype.readers.gml,sld:OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld,feature:OpenLayers.Format.GML.v2.prototype.readers.feature},writers:{owc:{OWSContext:function(a){var b=
this.createElementNSPlus("OWSContext",{attributes:{version:this.VERSION,id:a.id||OpenLayers.Util.createUniqueID("OpenLayers_OWSContext_")}});this.writeNode("General",a,b);this.writeNode("ResourceList",a,b);return b},General:function(a){var b=this.createElementNSPlus("General");this.writeNode("ows:BoundingBox",a,b);this.writeNode("ows:Title",a.title||"OpenLayers OWSContext",b);return b},ResourceList:function(a){for(var b=this.createElementNSPlus("ResourceList"),c=0,d=a.layers.length;c<d;c++){var e=
a.layers[c],f=this.decomposeNestingPath(e.metadata.nestingPath);this.writeNode("_Layer",{layer:e,subPaths:f},b)}return b},Server:function(a){var b=this.createElementNSPlus("Server",{attributes:{version:a.version,service:a.service}});this.writeNode("OnlineResource",a,b);return b},OnlineResource:function(a){return this.createElementNSPlus("OnlineResource",{attributes:{"xlink:href":a.url}})},InlineGeometry:function(a){var b=this.createElementNSPlus("InlineGeometry"),c=a.getDataExtent();null!==c&&this.writeNode("gml:boundedBy",
c,b);for(var c=0,d=a.features.length;c<d;c++)this.writeNode("gml:featureMember",a.features[c],b);return b},StyleList:function(a){for(var b=this.createElementNSPlus("StyleList"),c=0,d=a.length;c<d;c++)this.writeNode("Style",a[c],b);return b},Style:function(a){var b=this.createElementNSPlus("Style");this.writeNode("Name",a,b);this.writeNode("Title",a,b);a.legend&&this.writeNode("LegendURL",a,b);return b},Name:function(a){return this.createElementNSPlus("Name",{value:a.name})},Title:function(a){return this.createElementNSPlus("Title",
{value:a.title})},LegendURL:function(a){var b=this.createElementNSPlus("LegendURL");this.writeNode("OnlineResource",a.legend,b);return b},_WMS:function(a){var b=this.createElementNSPlus("Layer",{attributes:{name:a.params.LAYERS,queryable:a.queryable?"1":"0",hidden:a.visibility?"0":"1",opacity:a.hasOwnProperty("opacity")?a.opacity:null}});this.writeNode("ows:Title",a.name,b);this.writeNode("ows:OutputFormat",a.params.FORMAT,b);this.writeNode("Server",{service:OpenLayers.Format.Context.serviceTypes.WMS,
version:a.params.VERSION,url:a.url},b);a.metadata.styles&&0<a.metadata.styles.length&&this.writeNode("StyleList",a.metadata.styles,b);return b},_Layer:function(a){var b,c,d;b=a.layer;c=a.subPaths;d=null;0<c.length?(b=c[0].join("/"),c=b.lastIndexOf("/"),d=this.nestingLayerLookup[b],c=0<c?b.substring(c+1,b.length):b,d||(d=this.createElementNSPlus("Layer"),this.writeNode("ows:Title",c,d),this.nestingLayerLookup[b]=d),a.subPaths.shift(),this.writeNode("_Layer",a,d)):(b instanceof OpenLayers.Layer.WMS?
d=this.writeNode("_WMS",b):b instanceof OpenLayers.Layer.Vector&&(b.protocol instanceof OpenLayers.Protocol.WFS.v1?d=this.writeNode("_WFS",b):b.protocol instanceof OpenLayers.Protocol.HTTP?b.protocol.format instanceof OpenLayers.Format.GML?(b.protocol.format.version="2.1.2",d=this.writeNode("_GML",b)):b.protocol.format instanceof OpenLayers.Format.KML&&(b.protocol.format.version="2.2",d=this.writeNode("_KML",b)):(this.setNamespace("feature",this.featureNS),d=this.writeNode("_InlineGeometry",b))),
b.options.maxScale&&this.writeNode("sld:MinScaleDenominator",b.options.maxScale,d),b.options.minScale&&this.writeNode("sld:MaxScaleDenominator",b.options.minScale,d),this.nestingLayerLookup[b.name]=d);return d},_WFS:function(a){var b=this.createElementNSPlus("Layer",{attributes:{name:a.protocol.featurePrefix+":"+a.protocol.featureType,hidden:a.visibility?"0":"1"}});this.writeNode("ows:Title",a.name,b);this.writeNode("Server",{service:OpenLayers.Format.Context.serviceTypes.WFS,version:a.protocol.version,
url:a.protocol.url},b);return b},_InlineGeometry:function(a){var b=this.createElementNSPlus("Layer",{attributes:{name:this.featureType,hidden:a.visibility?"0":"1"}});this.writeNode("ows:Title",a.name,b);this.writeNode("InlineGeometry",a,b);return b},_GML:function(a){var b=this.createElementNSPlus("Layer");this.writeNode("ows:Title",a.name,b);this.writeNode("Server",{service:OpenLayers.Format.Context.serviceTypes.GML,url:a.protocol.url,version:a.protocol.format.version},b);return b},_KML:function(a){var b=
this.createElementNSPlus("Layer");this.writeNode("ows:Title",a.name,b);this.writeNode("Server",{service:OpenLayers.Format.Context.serviceTypes.KML,version:a.protocol.format.version,url:a.protocol.url},b);return b}},gml:OpenLayers.Util.applyDefaults({boundedBy:function(a){var b=this.createElementNSPlus("gml:boundedBy");this.writeNode("gml:Box",a,b);return b}},OpenLayers.Format.GML.v2.prototype.writers.gml),ows:OpenLayers.Format.OWSCommon.v1_0_0.prototype.writers.ows,sld:OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld,
feature:OpenLayers.Format.GML.v2.prototype.writers.feature},CLASS_NAME:"OpenLayers.Format.OWSContext.v0_3_1"});OpenLayers.Popup=OpenLayers.Class({events:null,id:"",lonlat:null,div:null,contentSize:null,size:null,contentHTML:null,backgroundColor:"",opacity:"",border:"",contentDiv:null,groupDiv:null,closeDiv:null,autoSize:!1,minSize:null,maxSize:null,displayClass:"olPopup",contentDisplayClass:"olPopupContent",padding:0,disableFirefoxOverflowHack:!1,fixPadding:function(){"number"==typeof this.padding&&(this.padding=new OpenLayers.Bounds(this.padding,this.padding,this.padding,this.padding))},panMapIfOutOfView:!1,
keepInMap:!1,closeOnMove:!1,map:null,initialize:function(a,b,c,d,e,f){null==a&&(a=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_"));this.id=a;this.lonlat=b;this.contentSize=null!=c?c:new OpenLayers.Size(OpenLayers.Popup.WIDTH,OpenLayers.Popup.HEIGHT);null!=d&&(this.contentHTML=d);this.backgroundColor=OpenLayers.Popup.COLOR;this.opacity=OpenLayers.Popup.OPACITY;this.border=OpenLayers.Popup.BORDER;this.div=OpenLayers.Util.createDiv(this.id,null,null,null,null,null,"hidden");this.div.className=this.displayClass;
this.groupDiv=OpenLayers.Util.createDiv(this.id+"_GroupDiv",null,null,null,"relative",null,"hidden");a=this.div.id+"_contentDiv";this.contentDiv=OpenLayers.Util.createDiv(a,null,this.contentSize.clone(),null,"relative");this.contentDiv.className=this.contentDisplayClass;this.groupDiv.appendChild(this.contentDiv);this.div.appendChild(this.groupDiv);e&&this.addCloseBox(f);this.registerEvents()},destroy:function(){this.border=this.opacity=this.backgroundColor=this.contentHTML=this.size=this.lonlat=this.id=
null;this.closeOnMove&&this.map&&this.map.events.unregister("movestart",this,this.hide);this.events.destroy();this.events=null;this.closeDiv&&(OpenLayers.Event.stopObservingElement(this.closeDiv),this.groupDiv.removeChild(this.closeDiv));this.closeDiv=null;this.div.removeChild(this.groupDiv);this.groupDiv=null;null!=this.map&&this.map.removePopup(this);this.panMapIfOutOfView=this.padding=this.maxSize=this.minSize=this.autoSize=this.div=this.map=null},draw:function(a){null==a&&null!=this.lonlat&&null!=
this.map&&(a=this.map.getLayerPxFromLonLat(this.lonlat));this.closeOnMove&&this.map.events.register("movestart",this,this.hide);this.disableFirefoxOverflowHack||"firefox"!=OpenLayers.BROWSER_NAME||(this.map.events.register("movestart",this,function(){var a=document.defaultView.getComputedStyle(this.contentDiv,null).getPropertyValue("overflow");"hidden"!=a&&(this.contentDiv._oldOverflow=a,this.contentDiv.style.overflow="hidden")}),this.map.events.register("moveend",this,function(){var a=this.contentDiv._oldOverflow;
a&&(this.contentDiv.style.overflow=a,this.contentDiv._oldOverflow=null)}));this.moveTo(a);this.autoSize||this.size||this.setSize(this.contentSize);this.setBackgroundColor();this.setOpacity();this.setBorder();this.setContentHTML();this.panMapIfOutOfView&&this.panIntoView();return this.div},updatePosition:function(){if(this.lonlat&&this.map){var a=this.map.getLayerPxFromLonLat(this.lonlat);a&&this.moveTo(a)}},moveTo:function(a){null!=a&&null!=this.div&&(this.div.style.left=a.x+"px",this.div.style.top=
a.y+"px")},visible:function(){return OpenLayers.Element.visible(this.div)},toggle:function(){this.visible()?this.hide():this.show()},show:function(){this.div.style.display="";this.panMapIfOutOfView&&this.panIntoView()},hide:function(){this.div.style.display="none"},setSize:function(a){this.size=a.clone();var b=this.getContentDivPadding(),c=b.left+b.right,d=b.top+b.bottom;this.fixPadding();c+=this.padding.left+this.padding.right;d+=this.padding.top+this.padding.bottom;if(this.closeDiv)var e=parseInt(this.closeDiv.style.width),
c=c+(e+b.right);this.size.w+=c;this.size.h+=d;"msie"==OpenLayers.BROWSER_NAME&&(this.contentSize.w+=b.left+b.right,this.contentSize.h+=b.bottom+b.top);null!=this.div&&(this.div.style.width=this.size.w+"px",this.div.style.height=this.size.h+"px");null!=this.contentDiv&&(this.contentDiv.style.width=a.w+"px",this.contentDiv.style.height=a.h+"px")},updateSize:function(){var a="<div class='"+this.contentDisplayClass+"'>"+this.contentDiv.innerHTML+"</div>",b=this.map?this.map.div:document.body,c=OpenLayers.Util.getRenderedDimensions(a,
null,{displayClass:this.displayClass,containerElement:b}),d=this.getSafeContentSize(c),e=null;d.equals(c)?e=c:(c={w:d.w<c.w?d.w:null,h:d.h<c.h?d.h:null},c.w&&c.h?e=d:(a=OpenLayers.Util.getRenderedDimensions(a,c,{displayClass:this.contentDisplayClass,containerElement:b}),"hidden"!=OpenLayers.Element.getStyle(this.contentDiv,"overflow")&&a.equals(d)&&(d=OpenLayers.Util.getScrollbarWidth(),c.w?a.h+=d:a.w+=d),e=this.getSafeContentSize(a)));this.setSize(e)},setBackgroundColor:function(a){void 0!=a&&(this.backgroundColor=
a);null!=this.div&&(this.div.style.backgroundColor=this.backgroundColor)},setOpacity:function(a){void 0!=a&&(this.opacity=a);null!=this.div&&(this.div.style.opacity=this.opacity,this.div.style.filter="alpha(opacity="+100*this.opacity+")")},setBorder:function(a){void 0!=a&&(this.border=a);null!=this.div&&(this.div.style.border=this.border)},setContentHTML:function(a){null!=a&&(this.contentHTML=a);null!=this.contentDiv&&(null!=this.contentHTML&&this.contentHTML!=this.contentDiv.innerHTML)&&(this.contentDiv.innerHTML=
this.contentHTML,this.autoSize&&(this.registerImageListeners(),this.updateSize()))},registerImageListeners:function(){for(var a=function(){null!==this.popup.id&&(this.popup.updateSize(),this.popup.visible()&&this.popup.panMapIfOutOfView&&this.popup.panIntoView(),OpenLayers.Event.stopObserving(this.img,"load",this.img._onImgLoad))},b=this.contentDiv.getElementsByTagName("img"),c=0,d=b.length;c<d;c++){var e=b[c];if(0==e.width||0==e.height)e._onImgLoad=OpenLayers.Function.bind(a,{popup:this,img:e}),
OpenLayers.Event.observe(e,"load",e._onImgLoad)}},getSafeContentSize:function(a){a=a.clone();var b=this.getContentDivPadding(),c=b.left+b.right,d=b.top+b.bottom;this.fixPadding();c+=this.padding.left+this.padding.right;d+=this.padding.top+this.padding.bottom;if(this.closeDiv)var e=parseInt(this.closeDiv.style.width),c=c+(e+b.right);this.minSize&&(a.w=Math.max(a.w,this.minSize.w-c),a.h=Math.max(a.h,this.minSize.h-d));this.maxSize&&(a.w=Math.min(a.w,this.maxSize.w-c),a.h=Math.min(a.h,this.maxSize.h-
d));if(this.map&&this.map.size){e=b=0;if(this.keepInMap&&!this.panMapIfOutOfView)switch(e=this.map.getPixelFromLonLat(this.lonlat),this.relativePosition){case "tr":b=e.x;e=this.map.size.h-e.y;break;case "tl":b=this.map.size.w-e.x;e=this.map.size.h-e.y;break;case "bl":b=this.map.size.w-e.x;e=e.y;break;case "br":b=e.x;e=e.y;break;default:b=e.x,e=this.map.size.h-e.y}d=this.map.size.h-this.map.paddingForPopups.top-this.map.paddingForPopups.bottom-d-e;a.w=Math.min(a.w,this.map.size.w-this.map.paddingForPopups.left-
this.map.paddingForPopups.right-c-b);a.h=Math.min(a.h,d)}return a},getContentDivPadding:function(){var a=this._contentDivPadding;a||(null==this.div.parentNode&&(this.div.style.display="none",document.body.appendChild(this.div)),this._contentDivPadding=a=new OpenLayers.Bounds(OpenLayers.Element.getStyle(this.contentDiv,"padding-left"),OpenLayers.Element.getStyle(this.contentDiv,"padding-bottom"),OpenLayers.Element.getStyle(this.contentDiv,"padding-right"),OpenLayers.Element.getStyle(this.contentDiv,
"padding-top")),this.div.parentNode==document.body&&(document.body.removeChild(this.div),this.div.style.display=""));return a},addCloseBox:function(a){this.closeDiv=OpenLayers.Util.createDiv(this.id+"_close",null,{w:17,h:17});this.closeDiv.className="olPopupCloseBox";var b=this.getContentDivPadding();this.closeDiv.style.right=b.right+"px";this.closeDiv.style.top=b.top+"px";this.groupDiv.appendChild(this.closeDiv);a=a||function(a){this.hide();OpenLayers.Event.stop(a)};OpenLayers.Event.observe(this.closeDiv,
"touchend",OpenLayers.Function.bindAsEventListener(a,this));OpenLayers.Event.observe(this.closeDiv,"click",OpenLayers.Function.bindAsEventListener(a,this))},panIntoView:function(){var a=this.map.getSize(),b=this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(parseInt(this.div.style.left),parseInt(this.div.style.top))),c=b.clone();b.x<this.map.paddingForPopups.left?c.x=this.map.paddingForPopups.left:b.x+this.size.w>a.w-this.map.paddingForPopups.right&&(c.x=a.w-this.map.paddingForPopups.right-this.size.w);
b.y<this.map.paddingForPopups.top?c.y=this.map.paddingForPopups.top:b.y+this.size.h>a.h-this.map.paddingForPopups.bottom&&(c.y=a.h-this.map.paddingForPopups.bottom-this.size.h);this.map.pan(b.x-c.x,b.y-c.y)},registerEvents:function(){this.events=new OpenLayers.Events(this,this.div,null,!0);this.events.on({mousedown:this.onmousedown,mousemove:this.onmousemove,mouseup:this.onmouseup,click:this.onclick,mouseout:this.onmouseout,dblclick:this.ondblclick,touchstart:function(a){OpenLayers.Event.stop(a,!0)},
scope:this})},onmousedown:function(a){this.mousedown=!0;OpenLayers.Event.stop(a,!0)},onmousemove:function(a){this.mousedown&&OpenLayers.Event.stop(a,!0)},onmouseup:function(a){this.mousedown&&(this.mousedown=!1,OpenLayers.Event.stop(a,!0))},onclick:function(a){OpenLayers.Event.stop(a,!0)},onmouseout:function(a){this.mousedown=!1},ondblclick:function(a){OpenLayers.Event.stop(a,!0)},CLASS_NAME:"OpenLayers.Popup"});OpenLayers.Popup.WIDTH=200;OpenLayers.Popup.HEIGHT=200;OpenLayers.Popup.COLOR="white";
OpenLayers.Popup.OPACITY=1;OpenLayers.Popup.BORDER="0px";OpenLayers.Control.ScaleLine=OpenLayers.Class(OpenLayers.Control,{maxWidth:100,topOutUnits:"km",topInUnits:"m",bottomOutUnits:"mi",bottomInUnits:"ft",eTop:null,eBottom:null,geodesic:!1,draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);this.eTop||(this.eTop=document.createElement("div"),this.eTop.className=this.displayClass+"Top",this.div.appendChild(this.eTop),this.eTop.style.visibility=""==this.topOutUnits||""==this.topInUnits?"hidden":"visible",this.eBottom=document.createElement("div"),
this.eBottom.className=this.displayClass+"Bottom",this.div.appendChild(this.eBottom),this.eBottom.style.visibility=""==this.bottomOutUnits||""==this.bottomInUnits?"hidden":"visible");this.map.events.register("moveend",this,this.update);this.update();return this.div},getBarLen:function(a){var b=parseInt(Math.log(a)/Math.log(10)),b=Math.pow(10,b);a=parseInt(a/b);return(5<a?5:2<a?2:1)*b},update:function(){var a=this.map.getResolution();if(a){var b=this.map.getUnits(),c=OpenLayers.INCHES_PER_UNIT,d=this.maxWidth*
a*c[b],e=1;!0===this.geodesic&&(e=(this.map.getGeodesicPixelSize().w||1E-6)*this.maxWidth/(d/c.km),d*=e);var f,g;1E5<d?(f=this.topOutUnits,g=this.bottomOutUnits):(f=this.topInUnits,g=this.bottomInUnits);var h=d/c[f],k=d/c[g],d=this.getBarLen(h),l=this.getBarLen(k),h=d/c[b]*c[f],k=l/c[b]*c[g],b=h/a/e,a=k/a/e;"visible"==this.eBottom.style.visibility&&(this.eBottom.style.width=Math.round(a)+"px",this.eBottom.innerHTML=l+" "+g);"visible"==this.eTop.style.visibility&&(this.eTop.style.width=Math.round(b)+
"px",this.eTop.innerHTML=d+" "+f)}},CLASS_NAME:"OpenLayers.Control.ScaleLine"});OpenLayers.Icon=OpenLayers.Class({url:null,size:null,offset:null,calculateOffset:null,imageDiv:null,px:null,initialize:function(a,b,c,d){this.url=a;this.size=b||{w:20,h:20};this.offset=c||{x:-(this.size.w/2),y:-(this.size.h/2)};this.calculateOffset=d;a=OpenLayers.Util.createUniqueID("OL_Icon_");this.imageDiv=OpenLayers.Util.createAlphaImageDiv(a)},destroy:function(){this.erase();OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);this.imageDiv.innerHTML="";this.imageDiv=null},clone:function(){return new OpenLayers.Icon(this.url,
this.size,this.offset,this.calculateOffset)},setSize:function(a){null!=a&&(this.size=a);this.draw()},setUrl:function(a){null!=a&&(this.url=a);this.draw()},draw:function(a){OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,null,this.size,this.url,"absolute");this.moveTo(a);return this.imageDiv},erase:function(){null!=this.imageDiv&&null!=this.imageDiv.parentNode&&OpenLayers.Element.remove(this.imageDiv)},setOpacity:function(a){OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,null,null,null,
null,null,null,a)},moveTo:function(a){null!=a&&(this.px=a);null!=this.imageDiv&&(null==this.px?this.display(!1):(this.calculateOffset&&(this.offset=this.calculateOffset(this.size)),OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,null,{x:this.px.x+this.offset.x,y:this.px.y+this.offset.y})))},display:function(a){this.imageDiv.style.display=a?"":"none"},isDrawn:function(){return this.imageDiv&&this.imageDiv.parentNode&&11!=this.imageDiv.parentNode.nodeType},CLASS_NAME:"OpenLayers.Icon"});OpenLayers.Marker=OpenLayers.Class({icon:null,lonlat:null,events:null,map:null,initialize:function(a,b){this.lonlat=a;var c=b?b:OpenLayers.Marker.defaultIcon();null==this.icon?this.icon=c:(this.icon.url=c.url,this.icon.size=c.size,this.icon.offset=c.offset,this.icon.calculateOffset=c.calculateOffset);this.events=new OpenLayers.Events(this,this.icon.imageDiv)},destroy:function(){this.erase();this.map=null;this.events.destroy();this.events=null;null!=this.icon&&(this.icon.destroy(),this.icon=null)},
draw:function(a){return this.icon.draw(a)},erase:function(){null!=this.icon&&this.icon.erase()},moveTo:function(a){null!=a&&null!=this.icon&&this.icon.moveTo(a);this.lonlat=this.map.getLonLatFromLayerPx(a)},isDrawn:function(){return this.icon&&this.icon.isDrawn()},onScreen:function(){var a=!1;this.map&&(a=this.map.getExtent().containsLonLat(this.lonlat));return a},inflate:function(a){this.icon&&this.icon.setSize({w:this.icon.size.w*a,h:this.icon.size.h*a})},setOpacity:function(a){this.icon.setOpacity(a)},
setUrl:function(a){this.icon.setUrl(a)},display:function(a){this.icon.display(a)},CLASS_NAME:"OpenLayers.Marker"});OpenLayers.Marker.defaultIcon=function(){return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"),{w:21,h:25},{x:-10.5,y:-25})};OpenLayers.Layer.TileCache=OpenLayers.Class(OpenLayers.Layer.Grid,{isBaseLayer:!0,format:"image/png",serverResolutions:null,initialize:function(a,b,c,d){this.layername=c;OpenLayers.Layer.Grid.prototype.initialize.apply(this,[a,b,{},d]);this.extension=this.format.split("/")[1].toLowerCase();this.extension="jpg"==this.extension?"jpeg":this.extension},clone:function(a){null==a&&(a=new OpenLayers.Layer.TileCache(this.name,this.url,this.layername,this.getOptions()));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,
[a])},getURL:function(a){var b=this.getServerResolution(),c=this.maxExtent,d=this.tileSize,e=Math.round((a.left-c.left)/(b*d.w));a=Math.round((a.bottom-c.bottom)/(b*d.h));b=null!=this.serverResolutions?OpenLayers.Util.indexOf(this.serverResolutions,b):this.map.getZoom();e=[this.layername,OpenLayers.Number.zeroPad(b,2),OpenLayers.Number.zeroPad(parseInt(e/1E6),3),OpenLayers.Number.zeroPad(parseInt(e/1E3)%1E3,3),OpenLayers.Number.zeroPad(parseInt(e)%1E3,3),OpenLayers.Number.zeroPad(parseInt(a/1E6),
3),OpenLayers.Number.zeroPad(parseInt(a/1E3)%1E3,3),OpenLayers.Number.zeroPad(parseInt(a)%1E3,3)+"."+this.extension].join("/");b=this.url;OpenLayers.Util.isArray(b)&&(b=this.selectUrl(e,b));b="/"==b.charAt(b.length-1)?b:b+"/";return b+e},CLASS_NAME:"OpenLayers.Layer.TileCache"});OpenLayers.Strategy.Paging=OpenLayers.Class(OpenLayers.Strategy,{features:null,length:10,num:null,paging:!1,activate:function(){var a=OpenLayers.Strategy.prototype.activate.call(this);if(a)this.layer.events.on({beforefeaturesadded:this.cacheFeatures,scope:this});return a},deactivate:function(){var a=OpenLayers.Strategy.prototype.deactivate.call(this);a&&(this.clearCache(),this.layer.events.un({beforefeaturesadded:this.cacheFeatures,scope:this}));return a},cacheFeatures:function(a){this.paging||(this.clearCache(),
this.features=a.features,this.pageNext(a))},clearCache:function(){if(this.features)for(var a=0;a<this.features.length;++a)this.features[a].destroy();this.num=this.features=null},pageCount:function(){return Math.ceil((this.features?this.features.length:0)/this.length)},pageNum:function(){return this.num},pageLength:function(a){a&&0<a&&(this.length=a);return this.length},pageNext:function(a){var b=!1;this.features&&(null===this.num&&(this.num=-1),b=this.page((this.num+1)*this.length,a));return b},pagePrevious:function(){var a=
!1;this.features&&(null===this.num&&(this.num=this.pageCount()),a=this.page((this.num-1)*this.length));return a},page:function(a,b){var c=!1;if(this.features&&0<=a&&a<this.features.length){var d=Math.floor(a/this.length);d!=this.num&&(this.paging=!0,c=this.features.slice(a,a+this.length),this.layer.removeFeatures(this.layer.features),this.num=d,b&&b.features?b.features=c:this.layer.addFeatures(c),this.paging=!1,c=!0)}return c},CLASS_NAME:"OpenLayers.Strategy.Paging"});OpenLayers.Control.DragFeature=OpenLayers.Class(OpenLayers.Control,{geometryTypes:null,onStart:function(a,b){},onDrag:function(a,b){},onComplete:function(a,b){},onEnter:function(a){},onLeave:function(a){},documentDrag:!1,layer:null,feature:null,dragCallbacks:{},featureCallbacks:{},lastPixel:null,initialize:function(a,b){OpenLayers.Control.prototype.initialize.apply(this,[b]);this.layer=a;this.handlers={drag:new OpenLayers.Handler.Drag(this,OpenLayers.Util.extend({down:this.downFeature,move:this.moveFeature,
up:this.upFeature,out:this.cancel,done:this.doneDragging},this.dragCallbacks),{documentDrag:this.documentDrag}),feature:new OpenLayers.Handler.Feature(this,this.layer,OpenLayers.Util.extend({click:this.clickFeature,clickout:this.clickoutFeature,over:this.overFeature,out:this.outFeature},this.featureCallbacks),{geometryTypes:this.geometryTypes})}},clickFeature:function(a){this.handlers.feature.touch&&(!this.over&&this.overFeature(a))&&(this.handlers.drag.dragstart(this.handlers.feature.evt),this.handlers.drag.stopDown=
!1)},clickoutFeature:function(a){this.handlers.feature.touch&&this.over&&(this.outFeature(a),this.handlers.drag.stopDown=!0)},destroy:function(){this.layer=null;OpenLayers.Control.prototype.destroy.apply(this,[])},activate:function(){return this.handlers.feature.activate()&&OpenLayers.Control.prototype.activate.apply(this,arguments)},deactivate:function(){this.handlers.drag.deactivate();this.handlers.feature.deactivate();this.feature=null;this.dragging=!1;this.lastPixel=null;OpenLayers.Element.removeClass(this.map.viewPortDiv,
this.displayClass+"Over");return OpenLayers.Control.prototype.deactivate.apply(this,arguments)},overFeature:function(a){var b=!1;this.handlers.drag.dragging?this.over=this.feature.id==a.id?!0:!1:(this.feature=a,this.handlers.drag.activate(),this.over=b=!0,OpenLayers.Element.addClass(this.map.viewPortDiv,this.displayClass+"Over"),this.onEnter(a));return b},downFeature:function(a){this.lastPixel=a;this.onStart(this.feature,a)},moveFeature:function(a){var b=this.map.getResolution();this.feature.geometry.move(b*
(a.x-this.lastPixel.x),b*(this.lastPixel.y-a.y));this.layer.drawFeature(this.feature);this.lastPixel=a;this.onDrag(this.feature,a)},upFeature:function(a){this.over||this.handlers.drag.deactivate()},doneDragging:function(a){this.onComplete(this.feature,a)},outFeature:function(a){this.handlers.drag.dragging?this.feature.id==a.id&&(this.over=!1):(this.over=!1,this.handlers.drag.deactivate(),OpenLayers.Element.removeClass(this.map.viewPortDiv,this.displayClass+"Over"),this.onLeave(a),this.feature=null)},
cancel:function(){this.handlers.drag.deactivate();this.over=!1},setMap:function(a){this.handlers.drag.setMap(a);this.handlers.feature.setMap(a);OpenLayers.Control.prototype.setMap.apply(this,arguments)},CLASS_NAME:"OpenLayers.Control.DragFeature"});OpenLayers.Control.TransformFeature=OpenLayers.Class(OpenLayers.Control,{geometryTypes:null,layer:null,preserveAspectRatio:!1,rotate:!0,feature:null,renderIntent:"temporary",rotationHandleSymbolizer:null,box:null,center:null,scale:1,ratio:1,rotation:0,handles:null,rotationHandles:null,dragControl:null,irregular:!1,initialize:function(a,b){OpenLayers.Control.prototype.initialize.apply(this,[b]);this.layer=a;this.rotationHandleSymbolizer||(this.rotationHandleSymbolizer={stroke:!1,pointRadius:10,fillOpacity:0,
cursor:"pointer"});this.createBox();this.createControl()},activate:function(){var a=!1;OpenLayers.Control.prototype.activate.apply(this,arguments)&&(this.dragControl.activate(),this.layer.addFeatures([this.box]),this.rotate&&this.layer.addFeatures(this.rotationHandles),this.layer.addFeatures(this.handles),a=!0);return a},deactivate:function(){var a=!1;OpenLayers.Control.prototype.deactivate.apply(this,arguments)&&(this.layer.removeFeatures(this.handles),this.rotate&&this.layer.removeFeatures(this.rotationHandles),
this.layer.removeFeatures([this.box]),this.dragControl.deactivate(),a=!0);return a},setMap:function(a){this.dragControl.setMap(a);OpenLayers.Control.prototype.setMap.apply(this,arguments)},setFeature:function(a,b){b=OpenLayers.Util.applyDefaults(b,{rotation:0,scale:1,ratio:1});var c=this.rotation,d=this.center;OpenLayers.Util.extend(this,b);if(!1!==this.events.triggerEvent("beforesetfeature",{feature:a})){this.feature=a;this.activate();this._setfeature=!0;var e=this.feature.geometry.getBounds();this.box.move(e.getCenterLonLat());
this.box.geometry.rotate(-c,d);this._angle=0;this.rotation?(c=a.geometry.clone(),c.rotate(-this.rotation,this.center),c=new OpenLayers.Feature.Vector(c.getBounds().toGeometry()),c.geometry.rotate(this.rotation,this.center),this.box.geometry.rotate(this.rotation,this.center),this.box.move(c.geometry.getBounds().getCenterLonLat()),c=c.geometry.components[0].components[0].getBounds().getCenterLonLat()):c=new OpenLayers.LonLat(e.left,e.bottom);this.handles[0].move(c);delete this._setfeature;this.events.triggerEvent("setfeature",
{feature:a})}},unsetFeature:function(){this.active?this.deactivate():(this.feature=null,this.rotation=0,this.ratio=this.scale=1)},createBox:function(){var a=this;this.center=new OpenLayers.Geometry.Point(0,0);this.box=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([new OpenLayers.Geometry.Point(-1,-1),new OpenLayers.Geometry.Point(0,-1),new OpenLayers.Geometry.Point(1,-1),new OpenLayers.Geometry.Point(1,0),new OpenLayers.Geometry.Point(1,1),new OpenLayers.Geometry.Point(0,1),new OpenLayers.Geometry.Point(-1,
1),new OpenLayers.Geometry.Point(-1,0),new OpenLayers.Geometry.Point(-1,-1)]),null,"string"==typeof this.renderIntent?null:this.renderIntent);this.box.geometry.move=function(b,c){a._moving=!0;OpenLayers.Geometry.LineString.prototype.move.apply(this,arguments);a.center.move(b,c);delete a._moving};for(var b=function(a,b){OpenLayers.Geometry.Point.prototype.move.apply(this,arguments);this._rotationHandle&&this._rotationHandle.geometry.move(a,b);this._handle.geometry.move(a,b)},c=function(a,b,c){OpenLayers.Geometry.Point.prototype.resize.apply(this,
arguments);this._rotationHandle&&this._rotationHandle.geometry.resize(a,b,c);this._handle.geometry.resize(a,b,c)},d=function(a,b){OpenLayers.Geometry.Point.prototype.rotate.apply(this,arguments);this._rotationHandle&&this._rotationHandle.geometry.rotate(a,b);this._handle.geometry.rotate(a,b)},e=function(b,c){var d=this.x,e=this.y;OpenLayers.Geometry.Point.prototype.move.call(this,b,c);if(!a._moving){var f=a.dragControl.handlers.drag.evt,g=!(!a._setfeature&&a.preserveAspectRatio)&&!(f&&f.shiftKey),
h=new OpenLayers.Geometry.Point(d,e),f=a.center;this.rotate(-a.rotation,f);h.rotate(-a.rotation,f);var k=this.x-f.x,l=this.y-f.y,m=k-(this.x-h.x),n=l-(this.y-h.y);a.irregular&&!a._setfeature&&(k-=(this.x-h.x)/2,l-=(this.y-h.y)/2);this.x=d;this.y=e;h=1;g?(l=1E-5>Math.abs(n)?1:l/n,h=(1E-5>Math.abs(m)?1:k/m)/l):(m=Math.sqrt(m*m+n*n),l=Math.sqrt(k*k+l*l)/m);a._moving=!0;a.box.geometry.rotate(-a.rotation,f);delete a._moving;a.box.geometry.resize(l,f,h);a.box.geometry.rotate(a.rotation,f);a.transformFeature({scale:l,
ratio:h});a.irregular&&!a._setfeature&&(k=f.clone(),k.x+=1E-5>Math.abs(d-f.x)?0:this.x-d,k.y+=1E-5>Math.abs(e-f.y)?0:this.y-e,a.box.geometry.move(this.x-d,this.y-e),a.transformFeature({center:k}))}},f=function(b,c){var d=this.x,e=this.y;OpenLayers.Geometry.Point.prototype.move.call(this,b,c);if(!a._moving){var f=a.dragControl.handlers.drag.evt,f=f&&f.shiftKey?45:1,g=a.center,h=this.x-g.x,k=this.y-g.y;this.x=d;this.y=e;d=Math.atan2(k-c,h-b);d=Math.atan2(k,h)-d;d*=180/Math.PI;a._angle=(a._angle+d)%
360;d=a.rotation%f;if(Math.abs(a._angle)>=f||0!==d)d=Math.round(a._angle/f)*f-d,a._angle=0,a.box.geometry.rotate(d,g),a.transformFeature({rotation:d})}},g=Array(8),h=Array(4),k,l,m,n="sw s se e ne n nw w".split(" "),p=0;8>p;++p)k=this.box.geometry.components[p],l=new OpenLayers.Feature.Vector(k.clone(),{role:n[p]+"-resize"},"string"==typeof this.renderIntent?null:this.renderIntent),0==p%2&&(m=new OpenLayers.Feature.Vector(k.clone(),{role:n[p]+"-rotate"},"string"==typeof this.rotationHandleSymbolizer?
null:this.rotationHandleSymbolizer),m.geometry.move=f,k._rotationHandle=m,h[p/2]=m),k.move=b,k.resize=c,k.rotate=d,l.geometry.move=e,k._handle=l,g[p]=l;this.rotationHandles=h;this.handles=g},createControl:function(){var a=this;this.dragControl=new OpenLayers.Control.DragFeature(this.layer,{documentDrag:!0,moveFeature:function(b){this.feature===a.feature&&(this.feature=a.box);OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this,arguments)},onDrag:function(b,c){b===a.box&&a.transformFeature({center:a.center})},
onStart:function(b,c){var d=!a.geometryTypes||-1!==OpenLayers.Util.indexOf(a.geometryTypes,b.geometry.CLASS_NAME),e=OpenLayers.Util.indexOf(a.handles,b),e=e+OpenLayers.Util.indexOf(a.rotationHandles,b);b!==a.feature&&(b!==a.box&&-2==e&&d)&&a.setFeature(b)},onComplete:function(b,c){a.events.triggerEvent("transformcomplete",{feature:a.feature})}})},drawHandles:function(){for(var a=this.layer,b=0;8>b;++b)this.rotate&&0===b%2&&a.drawFeature(this.rotationHandles[b/2],this.rotationHandleSymbolizer),a.drawFeature(this.handles[b],
this.renderIntent)},transformFeature:function(a){if(!this._setfeature){this.scale*=a.scale||1;this.ratio*=a.ratio||1;var b=this.rotation;this.rotation=(this.rotation+(a.rotation||0))%360;if(!1!==this.events.triggerEvent("beforetransform",a)){var c=this.feature,d=c.geometry,e=this.center;d.rotate(-b,e);a.scale||a.ratio?d.resize(a.scale,e,a.ratio):a.center&&c.move(a.center.getBounds().getCenterLonLat());d.rotate(this.rotation,e);this.layer.drawFeature(c);c.toState(OpenLayers.State.UPDATE);this.events.triggerEvent("transform",
a)}}this.layer.drawFeature(this.box,this.renderIntent);this.drawHandles()},destroy:function(){for(var a,b=0;8>b;++b)a=this.box.geometry.components[b],a._handle.destroy(),a._handle=null,a._rotationHandle&&a._rotationHandle.destroy(),a._rotationHandle=null;this.rotationHandles=this.rotationHandleSymbolizer=this.handles=this.feature=this.center=null;this.box.destroy();this.layer=this.box=null;this.dragControl.destroy();this.dragControl=null;OpenLayers.Control.prototype.destroy.apply(this,arguments)},
CLASS_NAME:"OpenLayers.Control.TransformFeature"});OpenLayers.Handler.Box=OpenLayers.Class(OpenLayers.Handler,{dragHandler:null,boxDivClassName:"olHandlerBoxZoomBox",boxOffsets:null,initialize:function(a,b,c){OpenLayers.Handler.prototype.initialize.apply(this,arguments);this.dragHandler=new OpenLayers.Handler.Drag(this,{down:this.startBox,move:this.moveBox,out:this.removeBox,up:this.endBox},{keyMask:this.keyMask})},destroy:function(){OpenLayers.Handler.prototype.destroy.apply(this,arguments);this.dragHandler&&(this.dragHandler.destroy(),this.dragHandler=
null)},setMap:function(a){OpenLayers.Handler.prototype.setMap.apply(this,arguments);this.dragHandler&&this.dragHandler.setMap(a)},startBox:function(a){this.callback("start",[]);this.zoomBox=OpenLayers.Util.createDiv("zoomBox",{x:-9999,y:-9999});this.zoomBox.className=this.boxDivClassName;this.zoomBox.style.zIndex=this.map.Z_INDEX_BASE.Popup-1;this.map.viewPortDiv.appendChild(this.zoomBox);OpenLayers.Element.addClass(this.map.viewPortDiv,"olDrawBox")},moveBox:function(a){var b=this.dragHandler.start.x,
c=this.dragHandler.start.y,d=Math.abs(b-a.x),e=Math.abs(c-a.y),f=this.getBoxOffsets();this.zoomBox.style.width=d+f.width+1+"px";this.zoomBox.style.height=e+f.height+1+"px";this.zoomBox.style.left=(a.x<b?b-d-f.left:b-f.left)+"px";this.zoomBox.style.top=(a.y<c?c-e-f.top:c-f.top)+"px"},endBox:function(a){var b;if(5<Math.abs(this.dragHandler.start.x-a.x)||5<Math.abs(this.dragHandler.start.y-a.y)){var c=this.dragHandler.start;b=Math.min(c.y,a.y);var d=Math.max(c.y,a.y),e=Math.min(c.x,a.x);a=Math.max(c.x,
a.x);b=new OpenLayers.Bounds(e,d,a,b)}else b=this.dragHandler.start.clone();this.removeBox();this.callback("done",[b])},removeBox:function(){this.map.viewPortDiv.removeChild(this.zoomBox);this.boxOffsets=this.zoomBox=null;OpenLayers.Element.removeClass(this.map.viewPortDiv,"olDrawBox")},activate:function(){return OpenLayers.Handler.prototype.activate.apply(this,arguments)?(this.dragHandler.activate(),!0):!1},deactivate:function(){return OpenLayers.Handler.prototype.deactivate.apply(this,arguments)?
(this.dragHandler.deactivate()&&this.zoomBox&&this.removeBox(),!0):!1},getBoxOffsets:function(){if(!this.boxOffsets){var a=document.createElement("div");a.style.position="absolute";a.style.border="1px solid black";a.style.width="3px";document.body.appendChild(a);var b=3==a.clientWidth;document.body.removeChild(a);var a=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-left-width")),c=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-right-width")),d=parseInt(OpenLayers.Element.getStyle(this.zoomBox,
"border-top-width")),e=parseInt(OpenLayers.Element.getStyle(this.zoomBox,"border-bottom-width"));this.boxOffsets={left:a,right:c,top:d,bottom:e,width:!1===b?a+c:0,height:!1===b?d+e:0}}return this.boxOffsets},CLASS_NAME:"OpenLayers.Handler.Box"});OpenLayers.Control.ZoomBox=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,out:!1,keyMask:null,alwaysZoom:!1,zoomOnClick:!0,draw:function(){this.handler=new OpenLayers.Handler.Box(this,{done:this.zoomBox},{keyMask:this.keyMask})},zoomBox:function(a){if(a instanceof OpenLayers.Bounds){var b,c=a.getCenterPixel();if(this.out){b=Math.min(this.map.size.h/(a.bottom-a.top),this.map.size.w/(a.right-a.left));var d=this.map.getExtent(),e=this.map.getLonLatFromPixel(c),f=e.lon-d.getWidth()/
2*b;a=e.lon+d.getWidth()/2*b;var g=e.lat-d.getHeight()/2*b;b=e.lat+d.getHeight()/2*b;b=new OpenLayers.Bounds(f,g,a,b)}else f=this.map.getLonLatFromPixel({x:a.left,y:a.bottom}),a=this.map.getLonLatFromPixel({x:a.right,y:a.top}),b=new OpenLayers.Bounds(f.lon,f.lat,a.lon,a.lat);f=this.map.getZoom();g=this.map.getSize();a=g.w/2;g=g.h/2;b=this.map.getZoomForExtent(b);d=this.map.getResolution();e=this.map.getResolutionForZoom(b);d==e?this.map.setCenter(this.map.getLonLatFromPixel(c)):this.map.zoomTo(b,
{x:(d*c.x-e*a)/(d-e),y:(d*c.y-e*g)/(d-e)});f==this.map.getZoom()&&!0==this.alwaysZoom&&this.map.zoomTo(f+(this.out?-1:1))}else this.zoomOnClick&&(this.out?this.map.zoomTo(this.map.getZoom()-1,a):this.map.zoomTo(this.map.getZoom()+1,a))},CLASS_NAME:"OpenLayers.Control.ZoomBox"});OpenLayers.Control.DragPan=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,panned:!1,interval:0,documentDrag:!1,kinetic:null,enableKinetic:!0,kineticInterval:10,draw:function(){if(this.enableKinetic&&OpenLayers.Kinetic){var a={interval:this.kineticInterval};"object"===typeof this.enableKinetic&&(a=OpenLayers.Util.extend(a,this.enableKinetic));this.kinetic=new OpenLayers.Kinetic(a)}this.handler=new OpenLayers.Handler.Drag(this,{move:this.panMap,done:this.panMapDone,down:this.panMapStart},
{interval:this.interval,documentDrag:this.documentDrag})},panMapStart:function(){this.kinetic&&this.kinetic.begin()},panMap:function(a){this.kinetic&&this.kinetic.update(a);this.panned=!0;this.map.pan(this.handler.last.x-a.x,this.handler.last.y-a.y,{dragging:!0,animate:!1})},panMapDone:function(a){if(this.panned){var b=null;this.kinetic&&(b=this.kinetic.end(a));this.map.pan(this.handler.last.x-a.x,this.handler.last.y-a.y,{dragging:!!b,animate:!1});if(b){var c=this;this.kinetic.move(b,function(a,b,
f){c.map.pan(a,b,{dragging:!f,animate:!1})})}this.panned=!1}},CLASS_NAME:"OpenLayers.Control.DragPan"});OpenLayers.Control.Navigation=OpenLayers.Class(OpenLayers.Control,{dragPan:null,dragPanOptions:null,pinchZoom:null,pinchZoomOptions:null,documentDrag:!1,zoomBox:null,zoomBoxEnabled:!0,zoomWheelEnabled:!0,mouseWheelOptions:null,handleRightClicks:!1,zoomBoxKeyMask:OpenLayers.Handler.MOD_SHIFT,autoActivate:!0,initialize:function(a){this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,arguments)},destroy:function(){this.deactivate();this.dragPan&&this.dragPan.destroy();this.dragPan=null;
this.zoomBox&&this.zoomBox.destroy();this.zoomBox=null;this.pinchZoom&&this.pinchZoom.destroy();this.pinchZoom=null;OpenLayers.Control.prototype.destroy.apply(this,arguments)},activate:function(){this.dragPan.activate();this.zoomWheelEnabled&&this.handlers.wheel.activate();this.handlers.click.activate();this.zoomBoxEnabled&&this.zoomBox.activate();this.pinchZoom&&this.pinchZoom.activate();return OpenLayers.Control.prototype.activate.apply(this,arguments)},deactivate:function(){this.pinchZoom&&this.pinchZoom.deactivate();
this.zoomBox.deactivate();this.dragPan.deactivate();this.handlers.click.deactivate();this.handlers.wheel.deactivate();return OpenLayers.Control.prototype.deactivate.apply(this,arguments)},draw:function(){this.handleRightClicks&&(this.map.viewPortDiv.oncontextmenu=OpenLayers.Function.False);this.handlers.click=new OpenLayers.Handler.Click(this,{click:this.defaultClick,dblclick:this.defaultDblClick,dblrightclick:this.defaultDblRightClick},{"double":!0,stopDouble:!0});this.dragPan=new OpenLayers.Control.DragPan(OpenLayers.Util.extend({map:this.map,
documentDrag:this.documentDrag},this.dragPanOptions));this.zoomBox=new OpenLayers.Control.ZoomBox({map:this.map,keyMask:this.zoomBoxKeyMask});this.dragPan.draw();this.zoomBox.draw();this.handlers.wheel=new OpenLayers.Handler.MouseWheel(this,{up:this.wheelUp,down:this.wheelDown},OpenLayers.Util.extend(this.map.fractionalZoom?{}:{cumulative:!1,interval:50,maxDelta:6},this.mouseWheelOptions));OpenLayers.Control.PinchZoom&&(this.pinchZoom=new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({map:this.map},
this.pinchZoomOptions)))},defaultClick:function(a){a.lastTouches&&2==a.lastTouches.length&&this.map.zoomOut()},defaultDblClick:function(a){this.map.zoomTo(this.map.zoom+1,a.xy)},defaultDblRightClick:function(a){this.map.zoomTo(this.map.zoom-1,a.xy)},wheelChange:function(a,b){this.map.fractionalZoom||(b=Math.round(b));var c=this.map.getZoom(),d;d=Math.max(c+b,0);d=Math.min(d,this.map.getNumZoomLevels());d!==c&&this.map.zoomTo(d,a.xy)},wheelUp:function(a,b){this.wheelChange(a,b||1)},wheelDown:function(a,
b){this.wheelChange(a,b||-1)},disableZoomBox:function(){this.zoomBoxEnabled=!1;this.zoomBox.deactivate()},enableZoomBox:function(){this.zoomBoxEnabled=!0;this.active&&this.zoomBox.activate()},disableZoomWheel:function(){this.zoomWheelEnabled=!1;this.handlers.wheel.deactivate()},enableZoomWheel:function(){this.zoomWheelEnabled=!0;this.active&&this.handlers.wheel.activate()},CLASS_NAME:"OpenLayers.Control.Navigation"});OpenLayers.Control.DrawFeature=OpenLayers.Class(OpenLayers.Control,{layer:null,callbacks:null,multi:!1,featureAdded:function(){},initialize:function(a,b,c){OpenLayers.Control.prototype.initialize.apply(this,[c]);this.callbacks=OpenLayers.Util.extend({done:this.drawFeature,modify:function(a,b){this.layer.events.triggerEvent("sketchmodified",{vertex:a,feature:b})},create:function(a,b){this.layer.events.triggerEvent("sketchstarted",{vertex:a,feature:b})}},this.callbacks);this.layer=a;this.handlerOptions=
this.handlerOptions||{};this.handlerOptions.layerOptions=OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions,{renderers:a.renderers,rendererOptions:a.rendererOptions});"multi"in this.handlerOptions||(this.handlerOptions.multi=this.multi);if(a=this.layer.styleMap&&this.layer.styleMap.styles.temporary)this.handlerOptions.layerOptions=OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions,{styleMap:new OpenLayers.StyleMap({"default":a})});this.handler=new b(this,this.callbacks,this.handlerOptions)},
drawFeature:function(a){a=new OpenLayers.Feature.Vector(a);!1!==this.layer.events.triggerEvent("sketchcomplete",{feature:a})&&(a.state=OpenLayers.State.INSERT,this.layer.addFeatures([a]),this.featureAdded(a),this.events.triggerEvent("featureadded",{feature:a}))},insertXY:function(a,b){this.handler&&this.handler.line&&this.handler.insertXY(a,b)},insertDeltaXY:function(a,b){this.handler&&this.handler.line&&this.handler.insertDeltaXY(a,b)},insertDirectionLength:function(a,b){this.handler&&this.handler.line&&
this.handler.insertDirectionLength(a,b)},insertDeflectionLength:function(a,b){this.handler&&this.handler.line&&this.handler.insertDeflectionLength(a,b)},undo:function(){return this.handler.undo&&this.handler.undo()},redo:function(){return this.handler.redo&&this.handler.redo()},finishSketch:function(){this.handler.finishGeometry()},cancel:function(){this.handler.cancel()},CLASS_NAME:"OpenLayers.Control.DrawFeature"});OpenLayers.Handler.Polygon=OpenLayers.Class(OpenLayers.Handler.Path,{holeModifier:null,drawingHole:!1,polygon:null,createFeature:function(a){a=this.layer.getLonLatFromViewPortPx(a);a=new OpenLayers.Geometry.Point(a.lon,a.lat);this.point=new OpenLayers.Feature.Vector(a);this.line=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LinearRing([this.point.geometry]));this.polygon=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([this.line.geometry]));this.callback("create",[this.point.geometry,
this.getSketch()]);this.point.geometry.clearBounds();this.layer.addFeatures([this.polygon,this.point],{silent:!0})},addPoint:function(a){if(!this.drawingHole&&this.holeModifier&&this.evt&&this.evt[this.holeModifier])for(var b=this.point.geometry,c=this.control.layer.features,d,e=c.length-1;0<=e;--e)if(d=c[e].geometry,(d instanceof OpenLayers.Geometry.Polygon||d instanceof OpenLayers.Geometry.MultiPolygon)&&d.intersects(b)){b=c[e];this.control.layer.removeFeatures([b],{silent:!0});this.control.layer.events.registerPriority("sketchcomplete",
this,this.finalizeInteriorRing);this.control.layer.events.registerPriority("sketchmodified",this,this.enforceTopology);b.geometry.addComponent(this.line.geometry);this.polygon=b;this.drawingHole=!0;break}OpenLayers.Handler.Path.prototype.addPoint.apply(this,arguments)},getCurrentPointIndex:function(){return this.line.geometry.components.length-2},enforceTopology:function(a){a=a.vertex;var b=this.line.geometry.components;this.polygon.geometry.intersects(a)||(b=b[b.length-3],a.x=b.x,a.y=b.y)},finishGeometry:function(){this.line.geometry.removeComponent(this.line.geometry.components[this.line.geometry.components.length-
2]);this.removePoint();this.finalize()},finalizeInteriorRing:function(){var a=this.line.geometry,b=0!==a.getArea();if(b){for(var c=this.polygon.geometry.components,d=c.length-2;0<=d;--d)if(a.intersects(c[d])){b=!1;break}if(b)a:for(d=c.length-2;0<d;--d)for(var e=c[d].components,f=0,g=e.length;f<g;++f)if(a.containsPoint(e[f])){b=!1;break a}}b?this.polygon.state!==OpenLayers.State.INSERT&&(this.polygon.state=OpenLayers.State.UPDATE):this.polygon.geometry.removeComponent(a);this.restoreFeature();return!1},
cancel:function(){this.drawingHole&&(this.polygon.geometry.removeComponent(this.line.geometry),this.restoreFeature(!0));return OpenLayers.Handler.Path.prototype.cancel.apply(this,arguments)},restoreFeature:function(a){this.control.layer.events.unregister("sketchcomplete",this,this.finalizeInteriorRing);this.control.layer.events.unregister("sketchmodified",this,this.enforceTopology);this.layer.removeFeatures([this.polygon],{silent:!0});this.control.layer.addFeatures([this.polygon],{silent:!0});this.drawingHole=
!1;a||this.control.layer.events.triggerEvent("sketchcomplete",{feature:this.polygon})},destroyFeature:function(a){OpenLayers.Handler.Path.prototype.destroyFeature.call(this,a);this.polygon=null},drawFeature:function(){this.layer.drawFeature(this.polygon,this.style);this.layer.drawFeature(this.point,this.style)},getSketch:function(){return this.polygon},getGeometry:function(){var a=this.polygon&&this.polygon.geometry;a&&this.multi&&(a=new OpenLayers.Geometry.MultiPolygon([a]));return a},CLASS_NAME:"OpenLayers.Handler.Polygon"});OpenLayers.Control.EditingToolbar=OpenLayers.Class(OpenLayers.Control.Panel,{citeCompliant:!1,initialize:function(a,b){OpenLayers.Control.Panel.prototype.initialize.apply(this,[b]);this.addControls([new OpenLayers.Control.Navigation]);var c=[new OpenLayers.Control.DrawFeature(a,OpenLayers.Handler.Point,{displayClass:"olControlDrawFeaturePoint",handlerOptions:{citeCompliant:this.citeCompliant}}),new OpenLayers.Control.DrawFeature(a,OpenLayers.Handler.Path,{displayClass:"olControlDrawFeaturePath",handlerOptions:{citeCompliant:this.citeCompliant}}),
new OpenLayers.Control.DrawFeature(a,OpenLayers.Handler.Polygon,{displayClass:"olControlDrawFeaturePolygon",handlerOptions:{citeCompliant:this.citeCompliant}})];this.addControls(c)},draw:function(){var a=OpenLayers.Control.Panel.prototype.draw.apply(this,arguments);null===this.defaultControl&&(this.defaultControl=this.controls[0]);return a},CLASS_NAME:"OpenLayers.Control.EditingToolbar"});OpenLayers.Strategy.BBOX=OpenLayers.Class(OpenLayers.Strategy,{bounds:null,resolution:null,ratio:2,resFactor:null,response:null,activate:function(){var a=OpenLayers.Strategy.prototype.activate.call(this);a&&(this.layer.events.on({moveend:this.update,refresh:this.update,visibilitychanged:this.update,scope:this}),this.update());return a},deactivate:function(){var a=OpenLayers.Strategy.prototype.deactivate.call(this);a&&this.layer.events.un({moveend:this.update,refresh:this.update,visibilitychanged:this.update,
scope:this});return a},update:function(a){var b=this.getMapBounds();null!==b&&(a&&a.force||this.layer.visibility&&this.layer.calculateInRange()&&this.invalidBounds(b))&&(this.calculateBounds(b),this.resolution=this.layer.map.getResolution(),this.triggerRead(a))},getMapBounds:function(){if(null===this.layer.map)return null;var a=this.layer.map.getExtent();a&&!this.layer.projection.equals(this.layer.map.getProjectionObject())&&(a=a.clone().transform(this.layer.map.getProjectionObject(),this.layer.projection));
return a},invalidBounds:function(a){a||(a=this.getMapBounds());a=!this.bounds||!this.bounds.containsBounds(a);!a&&this.resFactor&&(a=this.resolution/this.layer.map.getResolution(),a=a>=this.resFactor||a<=1/this.resFactor);return a},calculateBounds:function(a){a||(a=this.getMapBounds());var b=a.getCenterLonLat(),c=a.getWidth()*this.ratio;a=a.getHeight()*this.ratio;this.bounds=new OpenLayers.Bounds(b.lon-c/2,b.lat-a/2,b.lon+c/2,b.lat+a/2)},triggerRead:function(a){!this.response||a&&!0===a.noAbort||
(this.layer.protocol.abort(this.response),this.layer.events.triggerEvent("loadend"));var b={filter:this.createFilter()};this.layer.events.triggerEvent("loadstart",b);this.response=this.layer.protocol.read(OpenLayers.Util.applyDefaults({filter:b.filter,callback:this.merge,scope:this},a))},createFilter:function(){var a=new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.BBOX,value:this.bounds,projection:this.layer.projection});this.layer.filter&&(a=new OpenLayers.Filter.Logical({type:OpenLayers.Filter.Logical.AND,
filters:[this.layer.filter,a]}));return a},merge:function(a){this.layer.destroyFeatures();if(a.success()){var b=a.features;if(b&&0<b.length){var c=this.layer.projection,d=this.layer.map.getProjectionObject();if(!d.equals(c))for(var e,f=0,g=b.length;f<g;++f)(e=b[f].geometry)&&e.transform(c,d);this.layer.addFeatures(b)}}else this.bounds=null;this.response=null;this.layer.events.triggerEvent("loadend",{response:a})},CLASS_NAME:"OpenLayers.Strategy.BBOX"});OpenLayers.Layer.WorldWind=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{},isBaseLayer:!0,lzd:null,zoomLevels:null,initialize:function(a,b,c,d,e,f){this.lzd=c;this.zoomLevels=d;c=[];c.push(a,b,e,f);OpenLayers.Layer.Grid.prototype.initialize.apply(this,c);this.params=OpenLayers.Util.applyDefaults(this.params,this.DEFAULT_PARAMS)},getZoom:function(){var a=this.map.getZoom();this.map.getMaxExtent();return a-=Math.log(this.maxResolution/(this.lzd/512))/Math.log(2)},getURL:function(a){a=this.adjustBounds(a);
var b=this.getZoom(),c=this.map.getMaxExtent(),d=this.lzd/Math.pow(2,this.getZoom()),e=Math.floor((a.left-c.left)/d);a=Math.floor((a.bottom-c.bottom)/d);return this.map.getResolution()<=this.lzd/512&&this.getZoom()<=this.zoomLevels?this.getFullRequestString({L:b,X:e,Y:a}):OpenLayers.Util.getImageLocation("blank.gif")},CLASS_NAME:"OpenLayers.Layer.WorldWind"});OpenLayers.Protocol.CSW=function(a){a=OpenLayers.Util.applyDefaults(a,OpenLayers.Protocol.CSW.DEFAULTS);var b=OpenLayers.Protocol.CSW["v"+a.version.replace(/\./g,"_")];if(!b)throw"Unsupported CSW version: "+a.version;return new b(a)};OpenLayers.Protocol.CSW.DEFAULTS={version:"2.0.2"};OpenLayers.Format.WMTSCapabilities=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.0.0",yx:{"urn:ogc:def:crs:EPSG::4326":!0},createLayer:function(a,b){if(!("layer"in b))throw Error("Missing property 'layer' in configuration.");for(var c=a.contents,d,e=0,f=c.layers.length;e<f;++e)if(c.layers[e].identifier===b.layer){d=c.layers[e];break}if(!d)throw Error("Layer not found");var g=b.format;!g&&(d.formats&&d.formats.length)&&(g=d.formats[0]);var h;b.matrixSet?h=c.tileMatrixSets[b.matrixSet]:
1<=d.tileMatrixSetLinks.length&&(h=c.tileMatrixSets[d.tileMatrixSetLinks[0].tileMatrixSet]);if(!h)throw Error("matrixSet not found");for(var k,e=0,f=d.styles.length;e<f&&(k=d.styles[e],!k.isDefault);++e);c=b.requestEncoding;if(!c&&(c="KVP",a.operationsMetadata.GetTile.dcp.http)){var l=a.operationsMetadata.GetTile.dcp.http;l.get[0].constraints&&(l=l.get[0].constraints.GetEncoding.allowedValues,l.KVP||!l.REST&&!l.RESTful||(c="REST"))}var l=[],m=b.params||{};delete b.params;for(var n=0,p=d.dimensions.length;n<
p;n++){var q=d.dimensions[n];l.push(q.identifier);m.hasOwnProperty(q.identifier)||(m[q.identifier]=q["default"])}var n=b.projection||h.supportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"),p=b.units||("EPSG:4326"===n?"degrees":"m"),q=[],r;for(r in h.matrixIds)h.matrixIds.hasOwnProperty(r)&&q.push(2.8E-4*h.matrixIds[r].scaleDenominator/OpenLayers.METERS_PER_INCH/OpenLayers.INCHES_PER_UNIT[p]);if("REST"===c&&d.resourceUrls){r=[];for(var f=0,s=d.resourceUrls.length;f<s;++f)e=d.resourceUrls[f],
e.format===g&&"tile"===e.resourceType&&r.push(e.template)}else{s=a.operationsMetadata.GetTile.dcp.http.get;r=[];for(var t,e=0,f=s.length;e<f;e++)t=s[e].constraints,(!t||t&&t.GetEncoding.allowedValues[c])&&r.push(s[e].url)}return new OpenLayers.Layer.WMTS(OpenLayers.Util.applyDefaults(b,{url:r,requestEncoding:c,name:d.title,style:k.identifier,format:g,matrixIds:h.matrixIds,matrixSet:h.identifier,projection:n,units:p,resolutions:!1===b.isBaseLayer?void 0:q,serverResolutions:q,tileFullExtent:h.bounds,
dimensions:l,params:m}))},CLASS_NAME:"OpenLayers.Format.WMTSCapabilities"});OpenLayers.Layer.Google.v3={DEFAULTS:{sphericalMercator:!0,projection:"EPSG:900913"},animationEnabled:!0,loadMapObject:function(){this.type||(this.type=google.maps.MapTypeId.ROADMAP);var a,b=OpenLayers.Layer.Google.cache[this.map.id];b?(a=b.mapObject,++b.count):(a=this.map.getCenter(),b=document.createElement("div"),b.className="olForeignContainer",b.style.width="100%",b.style.height="100%",a=new google.maps.Map(b,{center:a?new google.maps.LatLng(a.lat,a.lon):new google.maps.LatLng(0,0),zoom:this.map.getZoom()||
0,mapTypeId:this.type,disableDefaultUI:!0,keyboardShortcuts:!1,draggable:!1,disableDoubleClickZoom:!0,scrollwheel:!1,streetViewControl:!1}),b=document.createElement("div"),b.style.width="100%",b.style.height="100%",a.controls[google.maps.ControlPosition.TOP_LEFT].push(b),b={googleControl:b,mapObject:a,count:1},OpenLayers.Layer.Google.cache[this.map.id]=b);this.mapObject=a;this.setGMapVisibility(this.visibility)},onMapResize:function(){this.visibility&&google.maps.event.trigger(this.mapObject,"resize")},
setGMapVisibility:function(a){var b=OpenLayers.Layer.Google.cache[this.map.id],c=this.map;if(b){for(var d=this.type,e=c.layers,f,g=e.length-1;0<=g;--g)if(f=e[g],f instanceof OpenLayers.Layer.Google&&!0===f.visibility&&!0===f.inRange){d=f.type;a=!0;break}e=this.mapObject.getDiv();if(!0===a){if(e.parentNode!==c.div)if(b.rendered)c.div.appendChild(e),b.googleControl.appendChild(c.viewPortDiv),google.maps.event.trigger(this.mapObject,"resize");else{var h=this;google.maps.event.addListenerOnce(this.mapObject,
"tilesloaded",function(){b.rendered=!0;h.setGMapVisibility(h.getVisibility());h.moveTo(h.map.getCenter())})}this.mapObject.setMapTypeId(d)}else b.googleControl.hasChildNodes()&&(c.div.appendChild(c.viewPortDiv),c.div.removeChild(e))}},getMapContainer:function(){return this.mapObject.getDiv()},getMapObjectBoundsFromOLBounds:function(a){var b=null;null!=a&&(b=this.sphericalMercator?this.inverseMercator(a.bottom,a.left):new OpenLayers.LonLat(a.bottom,a.left),a=this.sphericalMercator?this.inverseMercator(a.top,
a.right):new OpenLayers.LonLat(a.top,a.right),b=new google.maps.LatLngBounds(new google.maps.LatLng(b.lat,b.lon),new google.maps.LatLng(a.lat,a.lon)));return b},getMapObjectLonLatFromMapObjectPixel:function(a){var b=this.map.getSize(),c=this.getLongitudeFromMapObjectLonLat(this.mapObject.center),d=this.getLatitudeFromMapObjectLonLat(this.mapObject.center),e=this.map.getResolution();a=new OpenLayers.LonLat(c+(a.x-b.w/2)*e,d-(a.y-b.h/2)*e);this.wrapDateLine&&(a=a.wrapDateLine(this.maxExtent));return this.getMapObjectLonLatFromLonLat(a.lon,
a.lat)},getMapObjectPixelFromMapObjectLonLat:function(a){var b=this.getLongitudeFromMapObjectLonLat(a);a=this.getLatitudeFromMapObjectLonLat(a);var c=this.map.getResolution(),d=this.map.getExtent();return this.getMapObjectPixelFromXY(1/c*(b-d.left),1/c*(d.top-a))},setMapObjectCenter:function(a,b){if(!1===this.animationEnabled&&b!=this.mapObject.zoom){var c=this.getMapContainer();google.maps.event.addListenerOnce(this.mapObject,"idle",function(){c.style.visibility=""});c.style.visibility="hidden"}this.mapObject.setOptions({center:a,
zoom:b})},getMapObjectZoomFromMapObjectBounds:function(a){return this.mapObject.getBoundsZoomLevel(a)},getMapObjectLonLatFromLonLat:function(a,b){var c;this.sphericalMercator?(c=this.inverseMercator(a,b),c=new google.maps.LatLng(c.lat,c.lon)):c=new google.maps.LatLng(b,a);return c},getMapObjectPixelFromXY:function(a,b){return new google.maps.Point(a,b)}};OpenLayers.Format.WPSDescribeProcess=OpenLayers.Class(OpenLayers.Format.XML,{VERSION:"1.0.0",namespaces:{wps:"http://www.opengis.net/wps/1.0.0",ows:"http://www.opengis.net/ows/1.1",xsi:"http://www.w3.org/2001/XMLSchema-instance"},schemaLocation:"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd",defaultPrefix:"wps",regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,
[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={};this.readNode(a,b);return b},readers:{wps:{ProcessDescriptions:function(a,b){b.processDescriptions={};this.readChildNodes(a,b.processDescriptions)},ProcessDescription:function(a,b){var c={processVersion:this.getAttributeNS(a,this.namespaces.wps,"processVersion"),statusSupported:"true"===a.getAttribute("statusSupported"),storeSupported:"true"===a.getAttribute("storeSupported")};this.readChildNodes(a,c);b[c.identifier]=c},DataInputs:function(a,
b){b.dataInputs=[];this.readChildNodes(a,b.dataInputs)},ProcessOutputs:function(a,b){b.processOutputs=[];this.readChildNodes(a,b.processOutputs)},Output:function(a,b){var c={};this.readChildNodes(a,c);b.push(c)},ComplexOutput:function(a,b){b.complexOutput={};this.readChildNodes(a,b.complexOutput)},LiteralOutput:function(a,b){b.literalOutput={};this.readChildNodes(a,b.literalOutput)},Input:function(a,b){var c={maxOccurs:parseInt(a.getAttribute("maxOccurs")),minOccurs:parseInt(a.getAttribute("minOccurs"))};
this.readChildNodes(a,c);b.push(c)},BoundingBoxData:function(a,b){b.boundingBoxData={};this.readChildNodes(a,b.boundingBoxData)},CRS:function(a,b){b.CRSs||(b.CRSs={});b.CRSs[this.getChildValue(a)]=!0},LiteralData:function(a,b){b.literalData={};this.readChildNodes(a,b.literalData)},ComplexData:function(a,b){b.complexData={};this.readChildNodes(a,b.complexData)},Default:function(a,b){b["default"]={};this.readChildNodes(a,b["default"])},Supported:function(a,b){b.supported={};this.readChildNodes(a,b.supported)},
Format:function(a,b){var c={};this.readChildNodes(a,c);b.formats||(b.formats={});b.formats[c.mimeType]=!0},MimeType:function(a,b){b.mimeType=this.getChildValue(a)}},ows:OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers.ows},CLASS_NAME:"OpenLayers.Format.WPSDescribeProcess"});OpenLayers.Format.WKT=OpenLayers.Class(OpenLayers.Format,{initialize:function(a){this.regExes={typeStr:/^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,spaces:/\s+/,parenComma:/\)\s*,\s*\(/,doubleParenComma:/\)\s*\)\s*,\s*\(\s*\(/,trimParens:/^\s*\(?(.*?)\)?\s*$/};OpenLayers.Format.prototype.initialize.apply(this,[a])},read:function(a){var b,c;a=a.replace(/[\n\r]/g," ");if(c=this.regExes.typeStr.exec(a))if(a=c[1].toLowerCase(),c=c[2],this.parse[a]&&(b=this.parse[a].apply(this,[c])),this.internalProjection&&this.externalProjection)if(b&&
"OpenLayers.Feature.Vector"==b.CLASS_NAME)b.geometry.transform(this.externalProjection,this.internalProjection);else if(b&&"geometrycollection"!=a&&"object"==typeof b)for(a=0,c=b.length;a<c;a++)b[a].geometry.transform(this.externalProjection,this.internalProjection);return b},write:function(a){var b,c;a.constructor==Array?c=!0:(a=[a],c=!1);var d=[];c&&d.push("GEOMETRYCOLLECTION(");for(var e=0,f=a.length;e<f;++e)c&&0<e&&d.push(","),b=a[e].geometry,d.push(this.extractGeometry(b));c&&d.push(")");return d.join("")},
extractGeometry:function(a){var b=a.CLASS_NAME.split(".")[2].toLowerCase();if(!this.extract[b])return null;this.internalProjection&&this.externalProjection&&(a=a.clone(),a.transform(this.internalProjection,this.externalProjection));return("collection"==b?"GEOMETRYCOLLECTION":b.toUpperCase())+"("+this.extract[b].apply(this,[a])+")"},extract:{point:function(a){return a.x+" "+a.y},multipoint:function(a){for(var b=[],c=0,d=a.components.length;c<d;++c)b.push("("+this.extract.point.apply(this,[a.components[c]])+
")");return b.join(",")},linestring:function(a){for(var b=[],c=0,d=a.components.length;c<d;++c)b.push(this.extract.point.apply(this,[a.components[c]]));return b.join(",")},multilinestring:function(a){for(var b=[],c=0,d=a.components.length;c<d;++c)b.push("("+this.extract.linestring.apply(this,[a.components[c]])+")");return b.join(",")},polygon:function(a){for(var b=[],c=0,d=a.components.length;c<d;++c)b.push("("+this.extract.linestring.apply(this,[a.components[c]])+")");return b.join(",")},multipolygon:function(a){for(var b=
[],c=0,d=a.components.length;c<d;++c)b.push("("+this.extract.polygon.apply(this,[a.components[c]])+")");return b.join(",")},collection:function(a){for(var b=[],c=0,d=a.components.length;c<d;++c)b.push(this.extractGeometry.apply(this,[a.components[c]]));return b.join(",")}},parse:{point:function(a){a=OpenLayers.String.trim(a).split(this.regExes.spaces);return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(a[0],a[1]))},multipoint:function(a){for(var b=OpenLayers.String.trim(a).split(","),
c=[],d=0,e=b.length;d<e;++d)a=b[d].replace(this.regExes.trimParens,"$1"),c.push(this.parse.point.apply(this,[a]).geometry);return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPoint(c))},linestring:function(a){a=OpenLayers.String.trim(a).split(",");for(var b=[],c=0,d=a.length;c<d;++c)b.push(this.parse.point.apply(this,[a[c]]).geometry);return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(b))},multilinestring:function(a){for(var b=OpenLayers.String.trim(a).split(this.regExes.parenComma),
c=[],d=0,e=b.length;d<e;++d)a=b[d].replace(this.regExes.trimParens,"$1"),c.push(this.parse.linestring.apply(this,[a]).geometry);return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiLineString(c))},polygon:function(a){var b;a=OpenLayers.String.trim(a).split(this.regExes.parenComma);for(var c=[],d=0,e=a.length;d<e;++d)b=a[d].replace(this.regExes.trimParens,"$1"),b=this.parse.linestring.apply(this,[b]).geometry,b=new OpenLayers.Geometry.LinearRing(b.components),c.push(b);return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon(c))},
multipolygon:function(a){for(var b=OpenLayers.String.trim(a).split(this.regExes.doubleParenComma),c=[],d=0,e=b.length;d<e;++d)a=b[d].replace(this.regExes.trimParens,"$1"),c.push(this.parse.polygon.apply(this,[a]).geometry);return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPolygon(c))},geometrycollection:function(a){a=a.replace(/,\s*([A-Za-z])/g,"|$1");a=OpenLayers.String.trim(a).split("|");for(var b=[],c=0,d=a.length;c<d;++c)b.push(OpenLayers.Format.WKT.prototype.read.apply(this,[a[c]]));
return b}},CLASS_NAME:"OpenLayers.Format.WKT"});OpenLayers.WPSProcess=OpenLayers.Class({client:null,server:null,identifier:null,description:null,localWPS:"http://geoserver/wps",formats:null,chained:0,executeCallbacks:null,initialize:function(a){OpenLayers.Util.extend(this,a);this.executeCallbacks=[];this.formats={"application/wkt":new OpenLayers.Format.WKT,"application/json":new OpenLayers.Format.GeoJSON}},describe:function(a){a=a||{};if(!this.description)this.client.describeProcess(this.server,this.identifier,function(b){this.description||this.parseDescription(b);
a.callback&&a.callback.call(a.scope,this.description)},this);else if(a.callback){var b=this.description;window.setTimeout(function(){a.callback.call(a.scope,b)},0)}},configure:function(a){this.describe({callback:function(){var b=this.description,c=a.inputs,d,e,f;e=0;for(f=b.dataInputs.length;e<f;++e)d=b.dataInputs[e],this.setInputData(d,c[d.identifier]);a.callback&&a.callback.call(a.scope)},scope:this});return this},execute:function(a){this.configure({inputs:a.inputs,callback:function(){var b=this,
c=this.getOutputIndex(b.description.processOutputs,a.output);b.setResponseForm({outputIndex:c});(function e(){OpenLayers.Util.removeItem(b.executeCallbacks,e);0!==b.chained?b.executeCallbacks.push(e):OpenLayers.Request.POST({url:b.client.servers[b.server].url,data:(new OpenLayers.Format.WPSExecute).write(b.description),success:function(e){var g=b.findMimeType(b.description.processOutputs[c].complexOutput.supported.formats);e=b.formats[g].read(e.responseText);e instanceof OpenLayers.Feature.Vector&&
(e=[e]);a.success&&(g={},g[a.output||"result"]=e,a.success.call(a.scope,g))},scope:b})})()},scope:this})},output:function(a){return new OpenLayers.WPSProcess.ChainLink({process:this,output:a})},parseDescription:function(a){a=this.client.servers[this.server];this.description=(new OpenLayers.Format.WPSDescribeProcess).read(a.processDescription[this.identifier]).processDescriptions[this.identifier]},setInputData:function(a,b){delete a.data;delete a.reference;if(b instanceof OpenLayers.WPSProcess.ChainLink)++this.chained,
a.reference={method:"POST",href:b.process.server===this.server?this.localWPS:this.client.servers[b.process.server].url},b.process.describe({callback:function(){--this.chained;this.chainProcess(a,b)},scope:this});else{a.data={};var c=a.complexData;c?(c=this.findMimeType(c.supported.formats),a.data.complexData={mimeType:c,value:this.formats[c].write(this.toFeatures(b))}):a.data.literalData={value:b}}},setResponseForm:function(a){a=a||{};var b=this.description.processOutputs[a.outputIndex||0];this.description.responseForm=
{rawDataOutput:{identifier:b.identifier,mimeType:this.findMimeType(b.complexOutput.supported.formats,a.supportedFormats)}}},getOutputIndex:function(a,b){var c;if(b)for(var d=a.length-1;0<=d;--d){if(a[d].identifier===b){c=d;break}}else c=0;return c},chainProcess:function(a,b){var c=this.getOutputIndex(b.process.description.processOutputs,b.output);a.reference.mimeType=this.findMimeType(a.complexData.supported.formats,b.process.description.processOutputs[c].complexOutput.supported.formats);var d={};
d[a.reference.mimeType]=!0;b.process.setResponseForm({outputIndex:c,supportedFormats:d});for(a.reference.body=b.process.description;0<this.executeCallbacks.length;)this.executeCallbacks[0]()},toFeatures:function(a){var b=OpenLayers.Util.isArray(a);b||(a=[a]);for(var c=Array(a.length),d,e=0,f=a.length;e<f;++e)d=a[e],c[e]=d instanceof OpenLayers.Feature.Vector?d:new OpenLayers.Feature.Vector(d);return b?c:c[0]},findMimeType:function(a,b){b=b||this.formats;for(var c in a)if(c in b)return c},CLASS_NAME:"OpenLayers.WPSProcess"});
OpenLayers.WPSProcess.ChainLink=OpenLayers.Class({process:null,output:null,initialize:function(a){OpenLayers.Util.extend(this,a)},CLASS_NAME:"OpenLayers.WPSProcess.ChainLink"});OpenLayers.WPSClient=OpenLayers.Class({servers:null,version:"1.0.0",lazy:!1,events:null,initialize:function(a){OpenLayers.Util.extend(this,a);this.events=new OpenLayers.Events(this);this.servers={};for(var b in a.servers)this.servers[b]="string"==typeof a.servers[b]?{url:a.servers[b],version:this.version,processDescription:{}}:a.servers[b]},execute:function(a){this.getProcess(a.server,a.process).execute({inputs:a.inputs,success:a.success,scope:a.scope})},getProcess:function(a,b){var c=new OpenLayers.WPSProcess({client:this,
server:a,identifier:b});this.lazy||c.describe();return c},describeProcess:function(a,b,c,d){var e=this.servers[a];e.processDescription[b]?window.setTimeout(function(){c.call(d,e.processDescription[b])},0):b in e.processDescription?this.events.register("describeprocess",this,function g(a){a.identifier===b&&(this.events.unregister("describeprocess",this,g),c.call(d,a.raw))}):(e.processDescription[b]=null,OpenLayers.Request.GET({url:e.url,params:{SERVICE:"WPS",VERSION:e.version,REQUEST:"DescribeProcess",
IDENTIFIER:b},success:function(a){e.processDescription[b]=a.responseText;this.events.triggerEvent("describeprocess",{identifier:b,raw:a.responseText})},scope:this}))},destroy:function(){this.events.destroy();this.servers=this.events=null},CLASS_NAME:"OpenLayers.WPSClient"});OpenLayers.Format.CSWGetRecords.v2_0_2=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{csw:"http://www.opengis.net/cat/csw/2.0.2",dc:"http://purl.org/dc/elements/1.1/",dct:"http://purl.org/dc/terms/",gmd:"http://www.isotc211.org/2005/gmd",geonet:"http://www.fao.org/geonetwork",ogc:"http://www.opengis.net/ogc",ows:"http://www.opengis.net/ows",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},defaultPrefix:"csw",version:"2.0.2",schemaLocation:"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd",
requestId:null,resultType:null,outputFormat:null,outputSchema:null,startPosition:null,maxRecords:null,DistributedSearch:null,ResponseHandler:null,Query:null,regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={};this.readNode(a,b);return b},
readers:{csw:{GetRecordsResponse:function(a,b){b.records=[];this.readChildNodes(a,b);var c=this.getAttributeNS(a,"","version");""!=c&&(b.version=c)},RequestId:function(a,b){b.RequestId=this.getChildValue(a)},SearchStatus:function(a,b){b.SearchStatus={};var c=this.getAttributeNS(a,"","timestamp");""!=c&&(b.SearchStatus.timestamp=c)},SearchResults:function(a,b){this.readChildNodes(a,b);for(var c=a.attributes,d={},e=0,f=c.length;e<f;++e)d[c[e].name]="numberOfRecordsMatched"==c[e].name||"numberOfRecordsReturned"==
c[e].name||"nextRecord"==c[e].name?parseInt(c[e].nodeValue):c[e].nodeValue;b.SearchResults=d},SummaryRecord:function(a,b){var c={type:"SummaryRecord"};this.readChildNodes(a,c);b.records.push(c)},BriefRecord:function(a,b){var c={type:"BriefRecord"};this.readChildNodes(a,c);b.records.push(c)},DCMIRecord:function(a,b){var c={type:"DCMIRecord"};this.readChildNodes(a,c);b.records.push(c)},Record:function(a,b){var c={type:"Record"};this.readChildNodes(a,c);b.records.push(c)},"*":function(a,b){var c=a.localName||
a.nodeName.split(":").pop();b[c]=this.getChildValue(a)}},geonet:{info:function(a,b){var c={};this.readChildNodes(a,c);b.gninfo=c}},dc:{"*":function(a,b){var c=a.localName||a.nodeName.split(":").pop();OpenLayers.Util.isArray(b[c])||(b[c]=[]);for(var d={},e=a.attributes,f=0,g=e.length;f<g;++f)d[e[f].name]=e[f].nodeValue;d.value=this.getChildValue(a);""!=d.value&&b[c].push(d)}},dct:{"*":function(a,b){var c=a.localName||a.nodeName.split(":").pop();OpenLayers.Util.isArray(b[c])||(b[c]=[]);b[c].push(this.getChildValue(a))}},
ows:OpenLayers.Util.applyDefaults({BoundingBox:function(a,b){b.bounds&&(b.BoundingBox=[{crs:b.projection,value:[b.bounds.left,b.bounds.bottom,b.bounds.right,b.bounds.top]}],delete b.projection,delete b.bounds);OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows.BoundingBox.apply(this,arguments)}},OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows)},write:function(a){a=this.writeNode("csw:GetRecords",a);a.setAttribute("xmlns:gmd",this.namespaces.gmd);return OpenLayers.Format.XML.prototype.write.apply(this,
[a])},writers:{csw:{GetRecords:function(a){a||(a={});var b=this.createElementNSPlus("csw:GetRecords",{attributes:{service:"CSW",version:this.version,requestId:a.requestId||this.requestId,resultType:a.resultType||this.resultType,outputFormat:a.outputFormat||this.outputFormat,outputSchema:a.outputSchema||this.outputSchema,startPosition:a.startPosition||this.startPosition,maxRecords:a.maxRecords||this.maxRecords}});(a.DistributedSearch||this.DistributedSearch)&&this.writeNode("csw:DistributedSearch",
a.DistributedSearch||this.DistributedSearch,b);var c=a.ResponseHandler||this.ResponseHandler;if(OpenLayers.Util.isArray(c)&&0<c.length)for(var d=0,e=c.length;d<e;d++)this.writeNode("csw:ResponseHandler",c[d],b);this.writeNode("Query",a.Query||this.Query,b);return b},DistributedSearch:function(a){return this.createElementNSPlus("csw:DistributedSearch",{attributes:{hopCount:a.hopCount}})},ResponseHandler:function(a){return this.createElementNSPlus("csw:ResponseHandler",{value:a.value})},Query:function(a){a||
(a={});var b=this.createElementNSPlus("csw:Query",{attributes:{typeNames:a.typeNames||"csw:Record"}}),c=a.ElementName;if(OpenLayers.Util.isArray(c)&&0<c.length)for(var d=0,e=c.length;d<e;d++)this.writeNode("csw:ElementName",c[d],b);else this.writeNode("csw:ElementSetName",a.ElementSetName||{value:"summary"},b);a.Constraint&&this.writeNode("csw:Constraint",a.Constraint,b);a.SortBy&&this.writeNode("ogc:SortBy",a.SortBy,b);return b},ElementName:function(a){return this.createElementNSPlus("csw:ElementName",
{value:a.value})},ElementSetName:function(a){return this.createElementNSPlus("csw:ElementSetName",{attributes:{typeNames:a.typeNames},value:a.value})},Constraint:function(a){var b=this.createElementNSPlus("csw:Constraint",{attributes:{version:a.version}});if(a.Filter){var c=new OpenLayers.Format.Filter({version:a.version});b.appendChild(c.write(a.Filter))}else a.CqlText&&(a=this.createElementNSPlus("CqlText",{value:a.CqlText.value}),b.appendChild(a));return b}},ogc:OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc},
CLASS_NAME:"OpenLayers.Format.CSWGetRecords.v2_0_2"});/*
 Apache 2 

 Contains portions of Rico <http://openrico.org/>

 Copyright 2005 Sabre Airline Solutions  

 Licensed under the Apache License, Version 2.0 (the "License"); you
 may not use this file except in compliance with the License. You
 may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0  

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 implied. See the License for the specific language governing
 permissions and limitations under the License. 
*/
OpenLayers.Marker.Box=OpenLayers.Class(OpenLayers.Marker,{bounds:null,div:null,initialize:function(a,b,c){this.bounds=a;this.div=OpenLayers.Util.createDiv();this.div.style.overflow="hidden";this.events=new OpenLayers.Events(this,this.div);this.setBorder(b,c)},destroy:function(){this.div=this.bounds=null;OpenLayers.Marker.prototype.destroy.apply(this,arguments)},setBorder:function(a,b){a||(a="red");b||(b=2);this.div.style.border=b+"px solid "+a},draw:function(a,b){OpenLayers.Util.modifyDOMElement(this.div,
null,a,b);return this.div},onScreen:function(){var a=!1;this.map&&(a=this.map.getExtent().containsBounds(this.bounds,!0,!0));return a},display:function(a){this.div.style.display=a?"":"none"},CLASS_NAME:"OpenLayers.Marker.Box"});OpenLayers.Format.Text=OpenLayers.Class(OpenLayers.Format,{defaultStyle:null,extractStyles:!0,initialize:function(a){a=a||{};!1!==a.extractStyles&&(a.defaultStyle={externalGraphic:OpenLayers.Util.getImageLocation("marker.png"),graphicWidth:21,graphicHeight:25,graphicXOffset:-10.5,graphicYOffset:-12.5});OpenLayers.Format.prototype.initialize.apply(this,[a])},read:function(a){a=a.split("\n");for(var b,c=[],d=0;d<a.length-1;d++){var e=a[d].replace(/^\s*/,"").replace(/\s*$/,"");if("#"!=e.charAt(0))if(b){for(var e=
e.split("\t"),f=new OpenLayers.Geometry.Point(0,0),g={},h=this.defaultStyle?OpenLayers.Util.applyDefaults({},this.defaultStyle):null,k=!1,l=0;l<e.length;l++)if(e[l])if("point"==b[l])k=e[l].split(","),f.y=parseFloat(k[0]),f.x=parseFloat(k[1]),k=!0;else if("lat"==b[l])f.y=parseFloat(e[l]),k=!0;else if("lon"==b[l])f.x=parseFloat(e[l]),k=!0;else if("title"==b[l])g.title=e[l];else if("image"==b[l]||"icon"==b[l]&&h)h.externalGraphic=e[l];else if("iconSize"==b[l]&&h){var m=e[l].split(",");h.graphicWidth=
parseFloat(m[0]);h.graphicHeight=parseFloat(m[1])}else"iconOffset"==b[l]&&h?(m=e[l].split(","),h.graphicXOffset=parseFloat(m[0]),h.graphicYOffset=parseFloat(m[1])):"description"==b[l]?g.description=e[l]:"overflow"==b[l]?g.overflow=e[l]:g[b[l]]=e[l];k&&(this.internalProjection&&this.externalProjection&&f.transform(this.externalProjection,this.internalProjection),e=new OpenLayers.Feature.Vector(f,g,h),c.push(e))}else b=e.split("\t")}return c},CLASS_NAME:"OpenLayers.Format.Text"});OpenLayers.Layer.Text=OpenLayers.Class(OpenLayers.Layer.Markers,{location:null,features:null,formatOptions:null,selectedFeature:null,initialize:function(a,b){OpenLayers.Layer.Markers.prototype.initialize.apply(this,arguments);this.features=[]},destroy:function(){OpenLayers.Layer.Markers.prototype.destroy.apply(this,arguments);this.clearFeatures();this.features=null},loadText:function(){this.loaded||null==this.location||(this.events.triggerEvent("loadstart"),OpenLayers.Request.GET({url:this.location,
success:this.parseData,failure:function(a){this.events.triggerEvent("loadend")},scope:this}),this.loaded=!0)},moveTo:function(a,b,c){OpenLayers.Layer.Markers.prototype.moveTo.apply(this,arguments);this.visibility&&!this.loaded&&this.loadText()},parseData:function(a){a=a.responseText;var b={};OpenLayers.Util.extend(b,this.formatOptions);this.map&&!this.projection.equals(this.map.getProjectionObject())&&(b.externalProjection=this.projection,b.internalProjection=this.map.getProjectionObject());a=(new OpenLayers.Format.Text(b)).read(a);
for(var b=0,c=a.length;b<c;b++){var d={},e=a[b],f,g,h;f=new OpenLayers.LonLat(e.geometry.x,e.geometry.y);e.style.graphicWidth&&e.style.graphicHeight&&(g=new OpenLayers.Size(e.style.graphicWidth,e.style.graphicHeight));void 0!==e.style.graphicXOffset&&void 0!==e.style.graphicYOffset&&(h=new OpenLayers.Pixel(e.style.graphicXOffset,e.style.graphicYOffset));null!=e.style.externalGraphic?d.icon=new OpenLayers.Icon(e.style.externalGraphic,g,h):(d.icon=OpenLayers.Marker.defaultIcon(),null!=g&&d.icon.setSize(g));
null!=e.attributes.title&&null!=e.attributes.description&&(d.popupContentHTML="<h2>"+e.attributes.title+"</h2><p>"+e.attributes.description+"</p>");d.overflow=e.attributes.overflow||"auto";d=new OpenLayers.Feature(this,f,d);this.features.push(d);f=d.createMarker();null!=e.attributes.title&&null!=e.attributes.description&&f.events.register("click",d,this.markerClick);this.addMarker(f)}this.events.triggerEvent("loadend")},markerClick:function(a){var b=this==this.layer.selectedFeature;this.layer.selectedFeature=
b?null:this;for(var c=0,d=this.layer.map.popups.length;c<d;c++)this.layer.map.removePopup(this.layer.map.popups[c]);b||this.layer.map.addPopup(this.createPopup());OpenLayers.Event.stop(a)},clearFeatures:function(){if(null!=this.features)for(;0<this.features.length;){var a=this.features[0];OpenLayers.Util.removeItem(this.features,a);a.destroy()}},CLASS_NAME:"OpenLayers.Layer.Text"});OpenLayers.Handler.RegularPolygon=OpenLayers.Class(OpenLayers.Handler.Drag,{sides:4,radius:null,snapAngle:null,snapToggle:"shiftKey",layerOptions:null,persist:!1,irregular:!1,citeCompliant:!1,angle:null,fixedRadius:!1,feature:null,layer:null,origin:null,initialize:function(a,b,c){c&&c.layerOptions&&c.layerOptions.styleMap||(this.style=OpenLayers.Util.extend(OpenLayers.Feature.Vector.style["default"],{}));OpenLayers.Handler.Drag.prototype.initialize.apply(this,[a,b,c]);this.options=c?c:{}},setOptions:function(a){OpenLayers.Util.extend(this.options,
a);OpenLayers.Util.extend(this,a)},activate:function(){var a=!1;OpenLayers.Handler.Drag.prototype.activate.apply(this,arguments)&&(a=OpenLayers.Util.extend({displayInLayerSwitcher:!1,calculateInRange:OpenLayers.Function.True,wrapDateLine:this.citeCompliant},this.layerOptions),this.layer=new OpenLayers.Layer.Vector(this.CLASS_NAME,a),this.map.addLayer(this.layer),a=!0);return a},deactivate:function(){var a=!1;OpenLayers.Handler.Drag.prototype.deactivate.apply(this,arguments)&&(this.dragging&&this.cancel(),
null!=this.layer.map&&(this.layer.destroy(!1),this.feature&&this.feature.destroy()),this.feature=this.layer=null,a=!0);return a},down:function(a){this.fixedRadius=!!this.radius;a=this.layer.getLonLatFromViewPortPx(a.xy);this.origin=new OpenLayers.Geometry.Point(a.lon,a.lat);if(!this.fixedRadius||this.irregular)this.radius=this.map.getResolution();this.persist&&this.clear();this.feature=new OpenLayers.Feature.Vector;this.createGeometry();this.callback("create",[this.origin,this.feature]);this.layer.addFeatures([this.feature],
{silent:!0});this.layer.drawFeature(this.feature,this.style)},move:function(a){var b=this.layer.getLonLatFromViewPortPx(a.xy),b=new OpenLayers.Geometry.Point(b.lon,b.lat);this.irregular?(a=Math.sqrt(2)*Math.abs(b.y-this.origin.y)/2,this.radius=Math.max(this.map.getResolution()/2,a)):this.fixedRadius?this.origin=b:(this.calculateAngle(b,a),this.radius=Math.max(this.map.getResolution()/2,b.distanceTo(this.origin)));this.modifyGeometry();if(this.irregular){a=b.x-this.origin.x;var b=b.y-this.origin.y,
c;c=0==b?a/(this.radius*Math.sqrt(2)):a/b;this.feature.geometry.resize(1,this.origin,c);this.feature.geometry.move(a/2,b/2)}this.layer.drawFeature(this.feature,this.style)},up:function(a){this.finalize();this.start==this.last&&this.callback("done",[a.xy])},out:function(a){this.finalize()},createGeometry:function(){this.angle=Math.PI*(1/this.sides-0.5);this.snapAngle&&(this.angle+=this.snapAngle*(Math.PI/180));this.feature.geometry=OpenLayers.Geometry.Polygon.createRegularPolygon(this.origin,this.radius,
this.sides,this.snapAngle)},modifyGeometry:function(){var a,b,c=this.feature.geometry.components[0];c.components.length!=this.sides+1&&(this.createGeometry(),c=this.feature.geometry.components[0]);for(var d=0;d<this.sides;++d)b=c.components[d],a=this.angle+2*d*Math.PI/this.sides,b.x=this.origin.x+this.radius*Math.cos(a),b.y=this.origin.y+this.radius*Math.sin(a),b.clearBounds()},calculateAngle:function(a,b){var c=Math.atan2(a.y-this.origin.y,a.x-this.origin.x);if(this.snapAngle&&this.snapToggle&&!b[this.snapToggle]){var d=
Math.PI/180*this.snapAngle;this.angle=Math.round(c/d)*d}else this.angle=c},cancel:function(){this.callback("cancel",null);this.finalize()},finalize:function(){this.origin=null;this.radius=this.options.radius},clear:function(){this.layer&&(this.layer.renderer.clear(),this.layer.destroyFeatures())},callback:function(a,b){this.callbacks[a]&&this.callbacks[a].apply(this.control,[this.feature.geometry.clone()]);this.persist||"done"!=a&&"cancel"!=a||this.clear()},CLASS_NAME:"OpenLayers.Handler.RegularPolygon"});OpenLayers.Control.SLDSelect=OpenLayers.Class(OpenLayers.Control,{clearOnDeactivate:!1,layers:null,callbacks:null,selectionSymbolizer:{Polygon:{fillColor:"#FF0000",stroke:!1},Line:{strokeColor:"#FF0000",strokeWidth:2},Point:{graphicName:"square",fillColor:"#FF0000",pointRadius:5}},layerOptions:null,sketchStyle:null,wfsCache:{},layerCache:{},initialize:function(a,b){OpenLayers.Control.prototype.initialize.apply(this,[b]);this.callbacks=OpenLayers.Util.extend({done:this.select,click:this.select},this.callbacks);
this.handlerOptions=this.handlerOptions||{};this.layerOptions=OpenLayers.Util.applyDefaults(this.layerOptions,{displayInLayerSwitcher:!1,tileOptions:{maxGetUrlLength:2048}});this.sketchStyle&&(this.handlerOptions.layerOptions=OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions,{styleMap:new OpenLayers.StyleMap({"default":this.sketchStyle})}));this.handler=new a(this,this.callbacks,this.handlerOptions)},destroy:function(){for(var a in this.layerCache)delete this.layerCache[a];for(a in this.wfsCache)delete this.wfsCache[a];
OpenLayers.Control.prototype.destroy.apply(this,arguments)},coupleLayerVisiblity:function(a){this.setVisibility(a.object.getVisibility())},createSelectionLayer:function(a){var b;if(this.layerCache[a.id])b=this.layerCache[a.id];else{b=new OpenLayers.Layer.WMS(a.name,a.url,a.params,OpenLayers.Util.applyDefaults(this.layerOptions,a.getOptions()));this.layerCache[a.id]=b;if(!1===this.layerOptions.displayInLayerSwitcher)a.events.on({visibilitychanged:this.coupleLayerVisiblity,scope:b});this.map.addLayer(b)}return b},
createSLD:function(a,b,c){for(var d={version:"1.0.0",namedLayers:{}},e=(""+a.params.LAYERS).split(","),f=0,g=e.length;f<g;f++){var h=e[f];d.namedLayers[h]={name:h,userStyles:[]};var k=this.selectionSymbolizer,l=c[f];0<=l.type.indexOf("Polygon")?k={Polygon:this.selectionSymbolizer.Polygon}:0<=l.type.indexOf("LineString")?k={Line:this.selectionSymbolizer.Line}:0<=l.type.indexOf("Point")&&(k={Point:this.selectionSymbolizer.Point});d.namedLayers[h].userStyles.push({name:"default",rules:[new OpenLayers.Rule({symbolizer:k,
filter:b[f],maxScaleDenominator:a.options.minScale})]})}return(new OpenLayers.Format.SLD({srsName:this.map.getProjection()})).write(d)},parseDescribeLayer:function(a){var b=new OpenLayers.Format.WMSDescribeLayer,c=a.responseXML;c&&c.documentElement||(c=a.responseText);a=b.read(c);for(var b=[],c=null,d=0,e=a.length;d<e;d++)"WFS"==a[d].owsType&&(b.push(a[d].typeName),c=a[d].owsURL);OpenLayers.Request.GET({url:c,params:{SERVICE:"WFS",TYPENAME:b.toString(),REQUEST:"DescribeFeatureType",VERSION:"1.0.0"},
callback:function(a){var b=new OpenLayers.Format.WFSDescribeFeatureType,c=a.responseXML;c&&c.documentElement||(c=a.responseText);a=b.read(c);this.control.wfsCache[this.layer.id]=a;this.control._queue&&this.control.applySelection()},scope:this})},getGeometryAttributes:function(a){var b=[];a=this.wfsCache[a.id];for(var c=0,d=a.featureTypes.length;c<d;c++)for(var e=a.featureTypes[c].properties,f=0,g=e.length;f<g;f++){var h=e[f],k=h.type;(0<=k.indexOf("LineString")||0<=k.indexOf("GeometryAssociationType")||
0<=k.indexOf("GeometryPropertyType")||0<=k.indexOf("Point")||0<=k.indexOf("Polygon"))&&b.push(h)}return b},activate:function(){var a=OpenLayers.Control.prototype.activate.call(this);if(a)for(var b=0,c=this.layers.length;b<c;b++){var d=this.layers[b];d&&!this.wfsCache[d.id]&&OpenLayers.Request.GET({url:d.url,params:{SERVICE:"WMS",VERSION:d.params.VERSION,LAYERS:d.params.LAYERS,REQUEST:"DescribeLayer"},callback:this.parseDescribeLayer,scope:{layer:d,control:this}})}return a},deactivate:function(){var a=
OpenLayers.Control.prototype.deactivate.call(this);if(a)for(var b=0,c=this.layers.length;b<c;b++){var d=this.layers[b];if(d&&!0===this.clearOnDeactivate){var e=this.layerCache,f=e[d.id];f&&(d.events.un({visibilitychanged:this.coupleLayerVisiblity,scope:f}),f.destroy(),delete e[d.id])}}return a},setLayers:function(a){this.active?(this.deactivate(),this.layers=a,this.activate()):this.layers=a},createFilter:function(a,b){var c=null;this.handler instanceof OpenLayers.Handler.RegularPolygon?c=!0===this.handler.irregular?
new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.BBOX,property:a.name,value:b.getBounds()}):new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.INTERSECTS,property:a.name,value:b}):this.handler instanceof OpenLayers.Handler.Polygon?c=new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.INTERSECTS,property:a.name,value:b}):this.handler instanceof OpenLayers.Handler.Path?c=0<=a.type.indexOf("Point")?new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.DWITHIN,
property:a.name,distance:0.01*this.map.getExtent().getWidth(),distanceUnits:this.map.getUnits(),value:b}):new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.INTERSECTS,property:a.name,value:b}):this.handler instanceof OpenLayers.Handler.Click&&(c=0<=a.type.indexOf("Polygon")?new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.INTERSECTS,property:a.name,value:b}):new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.DWITHIN,property:a.name,distance:0.01*this.map.getExtent().getWidth(),
distanceUnits:this.map.getUnits(),value:b}));return c},select:function(a){this._queue=function(){for(var b=0,c=this.layers.length;b<c;b++){for(var d=this.layers[b],e=this.getGeometryAttributes(d),f=[],g=0,h=e.length;g<h;g++){var k=e[g];if(null!==k){if(!(a instanceof OpenLayers.Geometry)){var l=this.map.getLonLatFromPixel(a.xy);a=new OpenLayers.Geometry.Point(l.lon,l.lat)}k=this.createFilter(k,a);null!==k&&f.push(k)}}g=this.createSelectionLayer(d);this.events.triggerEvent("selected",{layer:d,filters:f});
d=this.createSLD(d,f,e);g.mergeNewParams({SLD_BODY:d});delete this._queue}};this.applySelection()},applySelection:function(){for(var a=!0,b=0,c=this.layers.length;b<c;b++)if(!this.wfsCache[this.layers[b].id]){a=!1;break}a&&this._queue.call(this)},CLASS_NAME:"OpenLayers.Control.SLDSelect"});OpenLayers.Control.Scale=OpenLayers.Class(OpenLayers.Control,{element:null,geodesic:!1,initialize:function(a,b){OpenLayers.Control.prototype.initialize.apply(this,[b]);this.element=OpenLayers.Util.getElement(a)},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);this.element||(this.element=document.createElement("div"),this.div.appendChild(this.element));this.map.events.register("moveend",this,this.updateScale);this.updateScale();return this.div},updateScale:function(){var a;
if(!0===this.geodesic){if(!this.map.getUnits())return;a=OpenLayers.INCHES_PER_UNIT;a=(this.map.getGeodesicPixelSize().w||1E-6)*a.km*OpenLayers.DOTS_PER_INCH}else a=this.map.getScale();a&&(a=9500<=a&&95E4>=a?Math.round(a/1E3)+"K":95E4<=a?Math.round(a/1E6)+"M":Math.round(a),this.element.innerHTML=OpenLayers.i18n("Scale = 1 : ${scaleDenom}",{scaleDenom:a}))},CLASS_NAME:"OpenLayers.Control.Scale"});OpenLayers.Layer.MapGuide=OpenLayers.Class(OpenLayers.Layer.Grid,{isBaseLayer:!0,useHttpTile:!1,singleTile:!1,useOverlay:!1,useAsyncOverlay:!0,TILE_PARAMS:{operation:"GETTILEIMAGE",version:"1.2.0"},SINGLE_TILE_PARAMS:{operation:"GETMAPIMAGE",format:"PNG",locale:"en",clip:"1",version:"1.0.0"},OVERLAY_PARAMS:{operation:"GETDYNAMICMAPOVERLAYIMAGE",format:"PNG",locale:"en",clip:"1",version:"2.0.0"},FOLDER_PARAMS:{tileColumnsPerFolder:30,tileRowsPerFolder:30,format:"png",querystring:null},defaultSize:new OpenLayers.Size(300,
300),tileOriginCorner:"tl",initialize:function(a,b,c,d){OpenLayers.Layer.Grid.prototype.initialize.apply(this,arguments);if(null==d||null==d.isBaseLayer)this.isBaseLayer="true"!=this.transparent&&!0!=this.transparent;d&&null!=d.useOverlay&&(this.useOverlay=d.useOverlay);this.singleTile?this.useOverlay?(OpenLayers.Util.applyDefaults(this.params,this.OVERLAY_PARAMS),this.useAsyncOverlay||(this.params.version="1.0.0")):OpenLayers.Util.applyDefaults(this.params,this.SINGLE_TILE_PARAMS):(this.useHttpTile?
OpenLayers.Util.applyDefaults(this.params,this.FOLDER_PARAMS):OpenLayers.Util.applyDefaults(this.params,this.TILE_PARAMS),this.setTileSize(this.defaultSize))},clone:function(a){null==a&&(a=new OpenLayers.Layer.MapGuide(this.name,this.url,this.params,this.getOptions()));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,[a])},getURL:function(a){var b;b=a.getCenterLonLat();var c=this.map.getSize();this.singleTile?(a={setdisplaydpi:OpenLayers.DOTS_PER_INCH,setdisplayheight:c.h*this.ratio,setdisplaywidth:c.w*
this.ratio,setviewcenterx:b.lon,setviewcentery:b.lat,setviewscale:this.map.getScale()},this.useOverlay&&!this.useAsyncOverlay&&(b={},b=OpenLayers.Util.extend(b,a),b.operation="GETVISIBLEMAPEXTENT",b.version="1.0.0",b.session=this.params.session,b.mapName=this.params.mapName,b.format="text/xml",b=this.getFullRequestString(b),OpenLayers.Request.GET({url:b,async:!1})),b=this.getFullRequestString(a)):(c=this.map.getResolution(),b=Math.floor((a.left-this.maxExtent.left)/c),b=Math.round(b/this.tileSize.w),
a=Math.floor((this.maxExtent.top-a.top)/c),a=Math.round(a/this.tileSize.h),b=this.useHttpTile?this.getImageFilePath({tilecol:b,tilerow:a,scaleindex:this.resolutions.length-this.map.zoom-1}):this.getFullRequestString({tilecol:b,tilerow:a,scaleindex:this.resolutions.length-this.map.zoom-1}));return b},getFullRequestString:function(a,b){var c=null==b?this.url:b;"object"==typeof c&&(c=c[Math.floor(Math.random()*c.length)]);var d=c,e=OpenLayers.Util.extend({},this.params),e=OpenLayers.Util.extend(e,a),
f=OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(c)),g;for(g in e)g.toUpperCase()in f&&delete e[g];e=OpenLayers.Util.getParameterString(e);e=e.replace(/,/g,"+");""!=e&&(f=c.charAt(c.length-1),d="&"==f||"?"==f?d+e:-1==c.indexOf("?")?d+("?"+e):d+("&"+e));return d},getImageFilePath:function(a,b){var c=null==b?this.url:b;"object"==typeof c&&(c=c[Math.floor(Math.random()*c.length)]);var d="",e="";0>a.tilerow&&(d="-");d=0==a.tilerow?d+"0":d+Math.floor(Math.abs(a.tilerow/this.params.tileRowsPerFolder))*
this.params.tileRowsPerFolder;0>a.tilecol&&(e="-");e=0==a.tilecol?e+"0":e+Math.floor(Math.abs(a.tilecol/this.params.tileColumnsPerFolder))*this.params.tileColumnsPerFolder;d="/S"+Math.floor(a.scaleindex)+"/"+this.params.basemaplayergroupname+"/R"+d+"/C"+e+"/"+a.tilerow%this.params.tileRowsPerFolder+"_"+a.tilecol%this.params.tileColumnsPerFolder+"."+this.params.format;this.params.querystring&&(d+="?"+this.params.querystring);return c+d},CLASS_NAME:"OpenLayers.Layer.MapGuide"});OpenLayers.Control.Measure=OpenLayers.Class(OpenLayers.Control,{callbacks:null,displaySystem:"metric",geodesic:!1,displaySystemUnits:{geographic:["dd"],english:["mi","ft","in"],metric:["km","m"]},partialDelay:300,delayedTrigger:null,persist:!1,immediate:!1,initialize:function(a,b){OpenLayers.Control.prototype.initialize.apply(this,[b]);var c={done:this.measureComplete,point:this.measurePartial};this.immediate&&(c.modify=this.measureImmediate);this.callbacks=OpenLayers.Util.extend(c,this.callbacks);
this.handlerOptions=OpenLayers.Util.extend({persist:this.persist},this.handlerOptions);this.handler=new a(this,this.callbacks,this.handlerOptions)},deactivate:function(){this.cancelDelay();return OpenLayers.Control.prototype.deactivate.apply(this,arguments)},cancel:function(){this.cancelDelay();this.handler.cancel()},setImmediate:function(a){(this.immediate=a)?this.callbacks.modify=this.measureImmediate:delete this.callbacks.modify},updateHandler:function(a,b){var c=this.active;c&&this.deactivate();
this.handler=new a(this,this.callbacks,b);c&&this.activate()},measureComplete:function(a){this.cancelDelay();this.measure(a,"measure")},measurePartial:function(a,b){this.cancelDelay();b=b.clone();this.handler.freehandMode(this.handler.evt)?this.measure(b,"measurepartial"):this.delayedTrigger=window.setTimeout(OpenLayers.Function.bind(function(){this.delayedTrigger=null;this.measure(b,"measurepartial")},this),this.partialDelay)},measureImmediate:function(a,b,c){c&&!this.handler.freehandMode(this.handler.evt)&&
(this.cancelDelay(),this.measure(b.geometry,"measurepartial"))},cancelDelay:function(){null!==this.delayedTrigger&&(window.clearTimeout(this.delayedTrigger),this.delayedTrigger=null)},measure:function(a,b){var c,d;-1<a.CLASS_NAME.indexOf("LineString")?(c=this.getBestLength(a),d=1):(c=this.getBestArea(a),d=2);this.events.triggerEvent(b,{measure:c[0],units:c[1],order:d,geometry:a})},getBestArea:function(a){for(var b=this.displaySystemUnits[this.displaySystem],c,d,e=0,f=b.length;e<f&&!(c=b[e],d=this.getArea(a,
c),1<d);++e);return[d,c]},getArea:function(a,b){var c,d;this.geodesic?(c=a.getGeodesicArea(this.map.getProjectionObject()),d="m"):(c=a.getArea(),d=this.map.getUnits());var e=OpenLayers.INCHES_PER_UNIT[b];e&&(c*=Math.pow(OpenLayers.INCHES_PER_UNIT[d]/e,2));return c},getBestLength:function(a){for(var b=this.displaySystemUnits[this.displaySystem],c,d,e=0,f=b.length;e<f&&!(c=b[e],d=this.getLength(a,c),1<d);++e);return[d,c]},getLength:function(a,b){var c,d;this.geodesic?(c=a.getGeodesicLength(this.map.getProjectionObject()),
d="m"):(c=a.getLength(),d=this.map.getUnits());var e=OpenLayers.INCHES_PER_UNIT[b];e&&(c*=OpenLayers.INCHES_PER_UNIT[d]/e);return c},CLASS_NAME:"OpenLayers.Control.Measure"});OpenLayers.Format.WMC.v1_0_0=OpenLayers.Class(OpenLayers.Format.WMC.v1,{VERSION:"1.0.0",schemaLocation:"http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd",initialize:function(a){OpenLayers.Format.WMC.v1.prototype.initialize.apply(this,[a])},read_wmc_SRS:function(a,b){var c=this.getChildValue(b);"object"!=typeof a.projections&&(a.projections={});for(var c=c.split(/ +/),d=0,e=c.length;d<e;d++)a.projections[c[d]]=!0},write_wmc_Layer:function(a){var b=OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(this,
[a]);if(a.srs){var c=[],d;for(d in a.srs)c.push(d);b.appendChild(this.createElementDefaultNS("SRS",c.join(" ")))}b.appendChild(this.write_wmc_FormatList(a));b.appendChild(this.write_wmc_StyleList(a));a.dimensions&&b.appendChild(this.write_wmc_DimensionList(a));b.appendChild(this.write_wmc_LayerExtension(a))},CLASS_NAME:"OpenLayers.Format.WMC.v1_0_0"});OpenLayers.Popup.Anchored=OpenLayers.Class(OpenLayers.Popup,{relativePosition:null,keepInMap:!0,anchor:null,initialize:function(a,b,c,d,e,f,g){OpenLayers.Popup.prototype.initialize.apply(this,[a,b,c,d,f,g]);this.anchor=null!=e?e:{size:new OpenLayers.Size(0,0),offset:new OpenLayers.Pixel(0,0)}},destroy:function(){this.relativePosition=this.anchor=null;OpenLayers.Popup.prototype.destroy.apply(this,arguments)},show:function(){this.updatePosition();OpenLayers.Popup.prototype.show.apply(this,arguments)},
moveTo:function(a){var b=this.relativePosition;this.relativePosition=this.calculateRelativePosition(a);OpenLayers.Popup.prototype.moveTo.call(this,this.calculateNewPx(a));this.relativePosition!=b&&this.updateRelativePosition()},setSize:function(a){OpenLayers.Popup.prototype.setSize.apply(this,arguments);if(this.lonlat&&this.map){var b=this.map.getLayerPxFromLonLat(this.lonlat);this.moveTo(b)}},calculateRelativePosition:function(a){a=this.map.getLonLatFromLayerPx(a);a=this.map.getExtent().determineQuadrant(a);
return OpenLayers.Bounds.oppositeQuadrant(a)},updateRelativePosition:function(){},calculateNewPx:function(a){a=a.offset(this.anchor.offset);var b=this.size||this.contentSize,c="t"==this.relativePosition.charAt(0);a.y+=c?-b.h:this.anchor.size.h;c="l"==this.relativePosition.charAt(1);a.x+=c?-b.w:this.anchor.size.w;return a},CLASS_NAME:"OpenLayers.Popup.Anchored"});OpenLayers.Popup.Framed=OpenLayers.Class(OpenLayers.Popup.Anchored,{imageSrc:null,imageSize:null,isAlphaImage:!1,positionBlocks:null,blocks:null,fixedRelativePosition:!1,initialize:function(a,b,c,d,e,f,g){OpenLayers.Popup.Anchored.prototype.initialize.apply(this,arguments);this.fixedRelativePosition&&(this.updateRelativePosition(),this.calculateRelativePosition=function(a){return this.relativePosition});this.contentDiv.style.position="absolute";this.contentDiv.style.zIndex=1;f&&(this.closeDiv.style.zIndex=
1);this.groupDiv.style.position="absolute";this.groupDiv.style.top="0px";this.groupDiv.style.left="0px";this.groupDiv.style.height="100%";this.groupDiv.style.width="100%"},destroy:function(){this.isAlphaImage=this.imageSize=this.imageSrc=null;this.fixedRelativePosition=!1;this.positionBlocks=null;for(var a=0;a<this.blocks.length;a++){var b=this.blocks[a];b.image&&b.div.removeChild(b.image);b.image=null;b.div&&this.groupDiv.removeChild(b.div);b.div=null}this.blocks=null;OpenLayers.Popup.Anchored.prototype.destroy.apply(this,
arguments)},setBackgroundColor:function(a){},setBorder:function(){},setOpacity:function(a){},setSize:function(a){OpenLayers.Popup.Anchored.prototype.setSize.apply(this,arguments);this.updateBlocks()},updateRelativePosition:function(){this.padding=this.positionBlocks[this.relativePosition].padding;if(this.closeDiv){var a=this.getContentDivPadding();this.closeDiv.style.right=a.right+this.padding.right+"px";this.closeDiv.style.top=a.top+this.padding.top+"px"}this.updateBlocks()},calculateNewPx:function(a){var b=
OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(this,arguments);return b=b.offset(this.positionBlocks[this.relativePosition].offset)},createBlocks:function(){this.blocks=[];var a=null,b;for(b in this.positionBlocks){a=b;break}a=this.positionBlocks[a];for(b=0;b<a.blocks.length;b++){var c={};this.blocks.push(c);c.div=OpenLayers.Util.createDiv(this.id+"_FrameDecorationDiv_"+b,null,null,null,"absolute",null,"hidden",null);c.image=(this.isAlphaImage?OpenLayers.Util.createAlphaImageDiv:OpenLayers.Util.createImage)(this.id+
"_FrameDecorationImg_"+b,null,this.imageSize,this.imageSrc,"absolute",null,null,null);c.div.appendChild(c.image);this.groupDiv.appendChild(c.div)}},updateBlocks:function(){this.blocks||this.createBlocks();if(this.size&&this.relativePosition){for(var a=this.positionBlocks[this.relativePosition],b=0;b<a.blocks.length;b++){var c=a.blocks[b],d=this.blocks[b],e=c.anchor.left,f=c.anchor.bottom,g=c.anchor.right,h=c.anchor.top,k=isNaN(c.size.w)?this.size.w-(g+e):c.size.w,l=isNaN(c.size.h)?this.size.h-(f+
h):c.size.h;d.div.style.width=(0>k?0:k)+"px";d.div.style.height=(0>l?0:l)+"px";d.div.style.left=null!=e?e+"px":"";d.div.style.bottom=null!=f?f+"px":"";d.div.style.right=null!=g?g+"px":"";d.div.style.top=null!=h?h+"px":"";d.image.style.left=c.position.x+"px";d.image.style.top=c.position.y+"px"}this.contentDiv.style.left=this.padding.left+"px";this.contentDiv.style.top=this.padding.top+"px"}},CLASS_NAME:"OpenLayers.Popup.Framed"});OpenLayers.Popup.FramedCloud=OpenLayers.Class(OpenLayers.Popup.Framed,{contentDisplayClass:"olFramedCloudPopupContent",autoSize:!0,panMapIfOutOfView:!0,imageSize:new OpenLayers.Size(1276,736),isAlphaImage:!1,fixedRelativePosition:!1,positionBlocks:{tl:{offset:new OpenLayers.Pixel(44,0),padding:new OpenLayers.Bounds(8,40,8,9),blocks:[{size:new OpenLayers.Size("auto","auto"),anchor:new OpenLayers.Bounds(0,51,22,0),position:new OpenLayers.Pixel(0,0)},{size:new OpenLayers.Size(22,"auto"),anchor:new OpenLayers.Bounds(null,
50,0,0),position:new OpenLayers.Pixel(-1238,0)},{size:new OpenLayers.Size("auto",19),anchor:new OpenLayers.Bounds(0,32,22,null),position:new OpenLayers.Pixel(0,-631)},{size:new OpenLayers.Size(22,18),anchor:new OpenLayers.Bounds(null,32,0,null),position:new OpenLayers.Pixel(-1238,-632)},{size:new OpenLayers.Size(81,35),anchor:new OpenLayers.Bounds(null,0,0,null),position:new OpenLayers.Pixel(0,-688)}]},tr:{offset:new OpenLayers.Pixel(-45,0),padding:new OpenLayers.Bounds(8,40,8,9),blocks:[{size:new OpenLayers.Size("auto",
"auto"),anchor:new OpenLayers.Bounds(0,51,22,0),position:new OpenLayers.Pixel(0,0)},{size:new OpenLayers.Size(22,"auto"),anchor:new OpenLayers.Bounds(null,50,0,0),position:new OpenLayers.Pixel(-1238,0)},{size:new OpenLayers.Size("auto",19),anchor:new OpenLayers.Bounds(0,32,22,null),position:new OpenLayers.Pixel(0,-631)},{size:new OpenLayers.Size(22,19),anchor:new OpenLayers.Bounds(null,32,0,null),position:new OpenLayers.Pixel(-1238,-631)},{size:new OpenLayers.Size(81,35),anchor:new OpenLayers.Bounds(0,
0,null,null),position:new OpenLayers.Pixel(-215,-687)}]},bl:{offset:new OpenLayers.Pixel(45,0),padding:new OpenLayers.Bounds(8,9,8,40),blocks:[{size:new OpenLayers.Size("auto","auto"),anchor:new OpenLayers.Bounds(0,21,22,32),position:new OpenLayers.Pixel(0,0)},{size:new OpenLayers.Size(22,"auto"),anchor:new OpenLayers.Bounds(null,21,0,32),position:new OpenLayers.Pixel(-1238,0)},{size:new OpenLayers.Size("auto",21),anchor:new OpenLayers.Bounds(0,0,22,null),position:new OpenLayers.Pixel(0,-629)},{size:new OpenLayers.Size(22,
21),anchor:new OpenLayers.Bounds(null,0,0,null),position:new OpenLayers.Pixel(-1238,-629)},{size:new OpenLayers.Size(81,33),anchor:new OpenLayers.Bounds(null,null,0,0),position:new OpenLayers.Pixel(-101,-674)}]},br:{offset:new OpenLayers.Pixel(-44,0),padding:new OpenLayers.Bounds(8,9,8,40),blocks:[{size:new OpenLayers.Size("auto","auto"),anchor:new OpenLayers.Bounds(0,21,22,32),position:new OpenLayers.Pixel(0,0)},{size:new OpenLayers.Size(22,"auto"),anchor:new OpenLayers.Bounds(null,21,0,32),position:new OpenLayers.Pixel(-1238,
0)},{size:new OpenLayers.Size("auto",21),anchor:new OpenLayers.Bounds(0,0,22,null),position:new OpenLayers.Pixel(0,-629)},{size:new OpenLayers.Size(22,21),anchor:new OpenLayers.Bounds(null,0,0,null),position:new OpenLayers.Pixel(-1238,-629)},{size:new OpenLayers.Size(81,33),anchor:new OpenLayers.Bounds(0,null,null,0),position:new OpenLayers.Pixel(-311,-674)}]}},minSize:new OpenLayers.Size(105,10),maxSize:new OpenLayers.Size(1200,660),initialize:function(a,b,c,d,e,f,g){this.imageSrc=OpenLayers.Util.getImageLocation("cloud-popup-relative.png");
OpenLayers.Popup.Framed.prototype.initialize.apply(this,arguments);this.contentDiv.className=this.contentDisplayClass},CLASS_NAME:"OpenLayers.Popup.FramedCloud"});OpenLayers.Tile.Image.IFrame={useIFrame:null,blankImageUrl:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7",draw:function(){if(OpenLayers.Tile.Image.prototype.shouldDraw.call(this)){var a=this.layer.getURL(this.bounds),b=this.useIFrame;this.useIFrame=null!==this.maxGetUrlLength&&!this.layer.async&&a.length>this.maxGetUrlLength;a=b&&!this.useIFrame;b=!b&&this.useIFrame;if(a||b)this.imgDiv&&this.imgDiv.parentNode===this.frame&&this.frame.removeChild(this.imgDiv),this.imgDiv=
null,a&&this.frame.removeChild(this.frame.firstChild)}return OpenLayers.Tile.Image.prototype.draw.apply(this,arguments)},getImage:function(){if(!0===this.useIFrame){if(!this.frame.childNodes.length){var a=document.createElement("div"),b=a.style;b.position="absolute";b.width="100%";b.height="100%";b.zIndex=1;b.backgroundImage="url("+this.blankImageUrl+")";this.frame.appendChild(a)}a=this.id+"_iFrame";9>parseFloat(navigator.appVersion.split("MSIE")[1])?(b=document.createElement('<iframe name="'+a+'">'),
b.style.backgroundColor="#FFFFFF",b.style.filter="chroma(color=#FFFFFF)"):(b=document.createElement("iframe"),b.style.backgroundColor="transparent",b.name=a);b.scrolling="no";b.marginWidth="0px";b.marginHeight="0px";b.frameBorder="0";b.style.position="absolute";b.style.width="100%";b.style.height="100%";1>this.layer.opacity&&OpenLayers.Util.modifyDOMElement(b,null,null,null,null,null,null,this.layer.opacity);this.frame.appendChild(b);return this.imgDiv=b}return OpenLayers.Tile.Image.prototype.getImage.apply(this,
arguments)},createRequestForm:function(){var a=document.createElement("form");a.method="POST";var b=this.layer.params._OLSALT,b=(b?b+"_":"")+this.bounds.toBBOX();a.action=OpenLayers.Util.urlAppend(this.layer.url,b);a.target=this.id+"_iFrame";this.layer.getImageSize();var b=OpenLayers.Util.getParameters(this.url),c,d;for(d in b)c=document.createElement("input"),c.type="hidden",c.name=d,c.value=b[d],a.appendChild(c);return a},setImgSrc:function(a){if(!0===this.useIFrame)if(a){var b=this.createRequestForm();
this.frame.appendChild(b);b.submit();this.frame.removeChild(b)}else this.imgDiv.parentNode===this.frame&&(this.frame.removeChild(this.imgDiv),this.imgDiv=null);else OpenLayers.Tile.Image.prototype.setImgSrc.apply(this,arguments)},onImageLoad:function(){OpenLayers.Tile.Image.prototype.onImageLoad.apply(this,arguments);!0===this.useIFrame&&(this.imgDiv.style.opacity=1,this.frame.style.opacity=this.layer.opacity)},createBackBuffer:function(){var a;!1===this.useIFrame&&(a=OpenLayers.Tile.Image.prototype.createBackBuffer.call(this));
return a}};OpenLayers.Format.SOSCapabilities=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.0.0",CLASS_NAME:"OpenLayers.Format.SOSCapabilities"});OpenLayers.Format.SOSCapabilities.v1_0_0=OpenLayers.Class(OpenLayers.Format.SOSCapabilities,{namespaces:{ows:"http://www.opengis.net/ows/1.1",sos:"http://www.opengis.net/sos/1.0",gml:"http://www.opengis.net/gml",xlink:"http://www.w3.org/1999/xlink"},regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a]);this.options=a},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,
[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={};this.readNode(a,b);return b},readers:{gml:OpenLayers.Util.applyDefaults({name:function(a,b){b.name=this.getChildValue(a)},TimePeriod:function(a,b){b.timePeriod={};this.readChildNodes(a,b.timePeriod)},beginPosition:function(a,b){b.beginPosition=this.getChildValue(a)},endPosition:function(a,b){b.endPosition=this.getChildValue(a)}},OpenLayers.Format.GML.v3.prototype.readers.gml),sos:{Capabilities:function(a,b){this.readChildNodes(a,b)},Contents:function(a,
b){b.contents={};this.readChildNodes(a,b.contents)},ObservationOfferingList:function(a,b){b.offeringList={};this.readChildNodes(a,b.offeringList)},ObservationOffering:function(a,b){var c=this.getAttributeNS(a,this.namespaces.gml,"id");b[c]={procedures:[],observedProperties:[],featureOfInterestIds:[],responseFormats:[],resultModels:[],responseModes:[]};this.readChildNodes(a,b[c])},time:function(a,b){b.time={};this.readChildNodes(a,b.time)},procedure:function(a,b){b.procedures.push(this.getAttributeNS(a,
this.namespaces.xlink,"href"))},observedProperty:function(a,b){b.observedProperties.push(this.getAttributeNS(a,this.namespaces.xlink,"href"))},featureOfInterest:function(a,b){b.featureOfInterestIds.push(this.getAttributeNS(a,this.namespaces.xlink,"href"))},responseFormat:function(a,b){b.responseFormats.push(this.getChildValue(a))},resultModel:function(a,b){b.resultModels.push(this.getChildValue(a))},responseMode:function(a,b){b.responseModes.push(this.getChildValue(a))}},ows:OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers.ows},
CLASS_NAME:"OpenLayers.Format.SOSCapabilities.v1_0_0"});OpenLayers.Handler.Pinch=OpenLayers.Class(OpenLayers.Handler,{started:!1,stopDown:!1,pinching:!1,last:null,start:null,touchstart:function(a){var b=!0;this.pinching=!1;if(OpenLayers.Event.isMultiTouch(a))this.started=!0,this.last=this.start={distance:this.getDistance(a.touches),delta:0,scale:1},this.callback("start",[a,this.start]),b=!this.stopDown;else{if(this.started)return!1;this.started=!1;this.last=this.start=null}OpenLayers.Event.preventDefault(a);return b},touchmove:function(a){if(this.started&&
OpenLayers.Event.isMultiTouch(a)){this.pinching=!0;var b=this.getPinchData(a);this.callback("move",[a,b]);this.last=b;OpenLayers.Event.stop(a)}else if(this.started)return!1;return!0},touchend:function(a){return this.started&&!OpenLayers.Event.isMultiTouch(a)?(this.pinching=this.started=!1,this.callback("done",[a,this.start,this.last]),this.last=this.start=null,!1):!0},activate:function(){var a=!1;OpenLayers.Handler.prototype.activate.apply(this,arguments)&&(this.pinching=!1,a=!0);return a},deactivate:function(){var a=
!1;OpenLayers.Handler.prototype.deactivate.apply(this,arguments)&&(this.pinching=this.started=!1,this.last=this.start=null,a=!0);return a},getDistance:function(a){var b=a[0];a=a[1];return Math.sqrt(Math.pow(b.olClientX-a.olClientX,2)+Math.pow(b.olClientY-a.olClientY,2))},getPinchData:function(a){a=this.getDistance(a.touches);return{distance:a,delta:this.last.distance-a,scale:a/this.start.distance}},CLASS_NAME:"OpenLayers.Handler.Pinch"});OpenLayers.Control.NavToolbar=OpenLayers.Class(OpenLayers.Control.Panel,{initialize:function(a){OpenLayers.Control.Panel.prototype.initialize.apply(this,[a]);this.addControls([new OpenLayers.Control.Navigation,new OpenLayers.Control.ZoomBox])},draw:function(){var a=OpenLayers.Control.Panel.prototype.draw.apply(this,arguments);null===this.defaultControl&&(this.defaultControl=this.controls[0]);return a},CLASS_NAME:"OpenLayers.Control.NavToolbar"});OpenLayers.Strategy.Refresh=OpenLayers.Class(OpenLayers.Strategy,{force:!1,interval:0,timer:null,activate:function(){var a=OpenLayers.Strategy.prototype.activate.call(this);a&&(!0===this.layer.visibility&&this.start(),this.layer.events.on({visibilitychanged:this.reset,scope:this}));return a},deactivate:function(){var a=OpenLayers.Strategy.prototype.deactivate.call(this);a&&(this.stop(),this.layer.events.un({visibilitychanged:this.reset,scope:this}));return a},reset:function(){!0===this.layer.visibility?
this.start():this.stop()},start:function(){this.interval&&("number"===typeof this.interval&&0<this.interval)&&(this.timer=window.setInterval(OpenLayers.Function.bind(this.refresh,this),this.interval))},refresh:function(){this.layer&&(this.layer.refresh&&"function"==typeof this.layer.refresh)&&this.layer.refresh({force:this.force})},stop:function(){null!==this.timer&&(window.clearInterval(this.timer),this.timer=null)},CLASS_NAME:"OpenLayers.Strategy.Refresh"});OpenLayers.Layer.ArcGIS93Rest=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{format:"png"},isBaseLayer:!0,initialize:function(a,b,c,d){var e=[];c=OpenLayers.Util.upperCaseObject(c);e.push(a,b,c,d);OpenLayers.Layer.Grid.prototype.initialize.apply(this,e);OpenLayers.Util.applyDefaults(this.params,OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));this.params.TRANSPARENT&&"true"==this.params.TRANSPARENT.toString().toLowerCase()&&(null!=d&&d.isBaseLayer||(this.isBaseLayer=!1),"jpg"==this.params.FORMAT&&
(this.params.FORMAT=OpenLayers.Util.alphaHack()?"gif":"png"))},clone:function(a){null==a&&(a=new OpenLayers.Layer.ArcGIS93Rest(this.name,this.url,this.params,this.getOptions()));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,[a])},getURL:function(a){a=this.adjustBounds(a);var b=this.projection.getCode().split(":"),b=b[b.length-1],c=this.getImageSize();a={BBOX:a.toBBOX(),SIZE:c.w+","+c.h,F:"image",BBOXSR:b,IMAGESR:b};if(this.layerDefs){var b=[],d;for(d in this.layerDefs)this.layerDefs.hasOwnProperty(d)&&
this.layerDefs[d]&&(b.push(d),b.push(":"),b.push(this.layerDefs[d]),b.push(";"));0<b.length&&(a.LAYERDEFS=b.join(""))}return this.getFullRequestString(a)},setLayerFilter:function(a,b){this.layerDefs||(this.layerDefs={});b?this.layerDefs[a]=b:delete this.layerDefs[a]},clearLayerFilter:function(a){a?delete this.layerDefs[a]:delete this.layerDefs},mergeNewParams:function(a){a=[OpenLayers.Util.upperCaseObject(a)];return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,a)},CLASS_NAME:"OpenLayers.Layer.ArcGIS93Rest"});OpenLayers.Handler.Hover=OpenLayers.Class(OpenLayers.Handler,{delay:500,pixelTolerance:null,stopMove:!1,px:null,timerId:null,mousemove:function(a){this.passesTolerance(a.xy)&&(this.clearTimer(),this.callback("move",[a]),this.px=a.xy,a=OpenLayers.Util.extend({},a),this.timerId=window.setTimeout(OpenLayers.Function.bind(this.delayedCall,this,a),this.delay));return!this.stopMove},mouseout:function(a){OpenLayers.Util.mouseLeft(a,this.map.viewPortDiv)&&(this.clearTimer(),this.callback("move",[a]));return!0},
passesTolerance:function(a){var b=!0;this.pixelTolerance&&this.px&&Math.sqrt(Math.pow(this.px.x-a.x,2)+Math.pow(this.px.y-a.y,2))<this.pixelTolerance&&(b=!1);return b},clearTimer:function(){null!=this.timerId&&(window.clearTimeout(this.timerId),this.timerId=null)},delayedCall:function(a){this.callback("pause",[a])},deactivate:function(){var a=!1;OpenLayers.Handler.prototype.deactivate.apply(this,arguments)&&(this.clearTimer(),a=!0);return a},CLASS_NAME:"OpenLayers.Handler.Hover"});OpenLayers.Control.GetFeature=OpenLayers.Class(OpenLayers.Control,{protocol:null,multipleKey:null,toggleKey:null,modifiers:null,multiple:!1,click:!0,single:!0,clickout:!0,toggle:!1,clickTolerance:5,hover:!1,box:!1,maxFeatures:10,features:null,hoverFeature:null,handlers:null,hoverResponse:null,filterType:OpenLayers.Filter.Spatial.BBOX,initialize:function(a){a.handlerOptions=a.handlerOptions||{};OpenLayers.Control.prototype.initialize.apply(this,[a]);this.features={};this.handlers={};this.click&&(this.handlers.click=
new OpenLayers.Handler.Click(this,{click:this.selectClick},this.handlerOptions.click||{}));this.box&&(this.handlers.box=new OpenLayers.Handler.Box(this,{done:this.selectBox},OpenLayers.Util.extend(this.handlerOptions.box,{boxDivClassName:"olHandlerBoxSelectFeature"})));this.hover&&(this.handlers.hover=new OpenLayers.Handler.Hover(this,{move:this.cancelHover,pause:this.selectHover},OpenLayers.Util.extend(this.handlerOptions.hover,{delay:250,pixelTolerance:2})))},activate:function(){if(!this.active)for(var a in this.handlers)this.handlers[a].activate();
return OpenLayers.Control.prototype.activate.apply(this,arguments)},deactivate:function(){if(this.active)for(var a in this.handlers)this.handlers[a].deactivate();return OpenLayers.Control.prototype.deactivate.apply(this,arguments)},selectClick:function(a){var b=this.pixelToBounds(a.xy);this.setModifiers(a);this.request(b,{single:this.single})},selectBox:function(a){var b;if(a instanceof OpenLayers.Bounds)b=this.map.getLonLatFromPixel({x:a.left,y:a.bottom}),a=this.map.getLonLatFromPixel({x:a.right,
y:a.top}),b=new OpenLayers.Bounds(b.lon,b.lat,a.lon,a.lat);else{if(this.click)return;b=this.pixelToBounds(a)}this.setModifiers(this.handlers.box.dragHandler.evt);this.request(b)},selectHover:function(a){a=this.pixelToBounds(a.xy);this.request(a,{single:!0,hover:!0})},cancelHover:function(){this.hoverResponse&&(this.protocol.abort(this.hoverResponse),this.hoverResponse=null,OpenLayers.Element.removeClass(this.map.viewPortDiv,"olCursorWait"))},request:function(a,b){b=b||{};var c=new OpenLayers.Filter.Spatial({type:this.filterType,
value:a});OpenLayers.Element.addClass(this.map.viewPortDiv,"olCursorWait");c=this.protocol.read({maxFeatures:!0==b.single?this.maxFeatures:void 0,filter:c,callback:function(c){c.success()&&(c.features.length?!0==b.single?this.selectBestFeature(c.features,a.getCenterLonLat(),b):this.select(c.features):b.hover?this.hoverSelect():(this.events.triggerEvent("clickout"),this.clickout&&this.unselectAll()));OpenLayers.Element.removeClass(this.map.viewPortDiv,"olCursorWait")},scope:this});!0==b.hover&&(this.hoverResponse=
c)},selectBestFeature:function(a,b,c){c=c||{};if(a.length){b=new OpenLayers.Geometry.Point(b.lon,b.lat);for(var d,e,f,g=Number.MAX_VALUE,h=0;h<a.length&&!(d=a[h],d.geometry&&(f=b.distanceTo(d.geometry,{edge:!1}),f<g&&(g=f,e=d,0==g)));++h);!0==c.hover?this.hoverSelect(e):this.select(e||a)}},setModifiers:function(a){this.modifiers={multiple:this.multiple||this.multipleKey&&a[this.multipleKey],toggle:this.toggle||this.toggleKey&&a[this.toggleKey]}},select:function(a){this.modifiers.multiple||this.modifiers.toggle||
this.unselectAll();OpenLayers.Util.isArray(a)||(a=[a]);var b=this.events.triggerEvent("beforefeaturesselected",{features:a});if(!1!==b){for(var c=[],d,e=0,f=a.length;e<f;++e)d=a[e],this.features[d.fid||d.id]?this.modifiers.toggle&&this.unselect(this.features[d.fid||d.id]):(b=this.events.triggerEvent("beforefeatureselected",{feature:d}),!1!==b&&(this.features[d.fid||d.id]=d,c.push(d),this.events.triggerEvent("featureselected",{feature:d})));this.events.triggerEvent("featuresselected",{features:c})}},
hoverSelect:function(a){var b=a?a.fid||a.id:null,c=this.hoverFeature?this.hoverFeature.fid||this.hoverFeature.id:null;c&&c!=b&&(this.events.triggerEvent("outfeature",{feature:this.hoverFeature}),this.hoverFeature=null);b&&b!=c&&(this.events.triggerEvent("hoverfeature",{feature:a}),this.hoverFeature=a)},unselect:function(a){delete this.features[a.fid||a.id];this.events.triggerEvent("featureunselected",{feature:a})},unselectAll:function(){for(var a in this.features)this.unselect(this.features[a])},
setMap:function(a){for(var b in this.handlers)this.handlers[b].setMap(a);OpenLayers.Control.prototype.setMap.apply(this,arguments)},pixelToBounds:function(a){var b=a.add(-this.clickTolerance/2,this.clickTolerance/2);a=a.add(this.clickTolerance/2,-this.clickTolerance/2);b=this.map.getLonLatFromPixel(b);a=this.map.getLonLatFromPixel(a);return new OpenLayers.Bounds(b.lon,b.lat,a.lon,a.lat)},CLASS_NAME:"OpenLayers.Control.GetFeature"});OpenLayers.Format.QueryStringFilter=function(){function a(a){a=a.replace(/%/g,"\\%");a=a.replace(/\\\\\.(\*)?/g,function(a,b){return b?a:"\\\\_"});a=a.replace(/\\\\\.\*/g,"\\\\%");a=a.replace(/(\\)?\.(\*)?/g,function(a,b,c){return b||c?a:"_"});a=a.replace(/(\\)?\.\*/g,function(a,b){return b?a:"%"});a=a.replace(/\\\./g,".");return a=a.replace(/(\\)?\\\*/g,function(a,b){return b?a:"*"})}var b={};b[OpenLayers.Filter.Comparison.EQUAL_TO]="eq";b[OpenLayers.Filter.Comparison.NOT_EQUAL_TO]="ne";b[OpenLayers.Filter.Comparison.LESS_THAN]=
"lt";b[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO]="lte";b[OpenLayers.Filter.Comparison.GREATER_THAN]="gt";b[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO]="gte";b[OpenLayers.Filter.Comparison.LIKE]="ilike";return OpenLayers.Class(OpenLayers.Format,{wildcarded:!1,srsInBBOX:!1,write:function(c,d){d=d||{};var e=c.CLASS_NAME,e=e.substring(e.lastIndexOf(".")+1);switch(e){case "Spatial":switch(c.type){case OpenLayers.Filter.Spatial.BBOX:d.bbox=c.value.toArray();this.srsInBBOX&&c.projection&&
d.bbox.push(c.projection.getCode());break;case OpenLayers.Filter.Spatial.DWITHIN:d.tolerance=c.distance;case OpenLayers.Filter.Spatial.WITHIN:d.lon=c.value.x;d.lat=c.value.y;break;default:OpenLayers.Console.warn("Unknown spatial filter type "+c.type)}break;case "Comparison":e=b[c.type];if(void 0!==e){var f=c.value;c.type==OpenLayers.Filter.Comparison.LIKE&&(f=a(f),this.wildcarded&&(f="%"+f+"%"));d[c.property+"__"+e]=f;d.queryable=d.queryable||[];d.queryable.push(c.property)}else OpenLayers.Console.warn("Unknown comparison filter type "+
c.type);break;case "Logical":if(c.type===OpenLayers.Filter.Logical.AND)for(e=0,f=c.filters.length;e<f;e++)d=this.write(c.filters[e],d);else OpenLayers.Console.warn("Unsupported logical filter type "+c.type);break;default:OpenLayers.Console.warn("Unknown filter type "+e)}return d},CLASS_NAME:"OpenLayers.Format.QueryStringFilter"})}();OpenLayers.Control.MousePosition=OpenLayers.Class(OpenLayers.Control,{autoActivate:!0,element:null,prefix:"",separator:", ",suffix:"",numDigits:5,granularity:10,emptyString:null,lastXy:null,displayProjection:null,destroy:function(){this.deactivate();OpenLayers.Control.prototype.destroy.apply(this,arguments)},activate:function(){return OpenLayers.Control.prototype.activate.apply(this,arguments)?(this.map.events.register("mousemove",this,this.redraw),this.map.events.register("mouseout",this,this.reset),
this.redraw(),!0):!1},deactivate:function(){return OpenLayers.Control.prototype.deactivate.apply(this,arguments)?(this.map.events.unregister("mousemove",this,this.redraw),this.map.events.unregister("mouseout",this,this.reset),this.element.innerHTML="",!0):!1},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);this.element||(this.div.left="",this.div.top="",this.element=this.div);return this.div},redraw:function(a){var b;if(null==a)this.reset();else if(null==this.lastXy||Math.abs(a.xy.x-
this.lastXy.x)>this.granularity||Math.abs(a.xy.y-this.lastXy.y)>this.granularity)this.lastXy=a.xy;else if(b=this.map.getLonLatFromPixel(a.xy))this.displayProjection&&b.transform(this.map.getProjectionObject(),this.displayProjection),this.lastXy=a.xy,a=this.formatOutput(b),a!=this.element.innerHTML&&(this.element.innerHTML=a)},reset:function(a){null!=this.emptyString&&(this.element.innerHTML=this.emptyString)},formatOutput:function(a){var b=parseInt(this.numDigits);return this.prefix+a.lon.toFixed(b)+
this.separator+a.lat.toFixed(b)+this.suffix},CLASS_NAME:"OpenLayers.Control.MousePosition"});OpenLayers.Control.Geolocate=OpenLayers.Class(OpenLayers.Control,{geolocation:null,available:"geolocation"in navigator,bind:!0,watch:!1,geolocationOptions:null,destroy:function(){this.deactivate();OpenLayers.Control.prototype.destroy.apply(this,arguments)},activate:function(){this.available&&!this.geolocation&&(this.geolocation=navigator.geolocation);return this.geolocation?OpenLayers.Control.prototype.activate.apply(this,arguments)?(this.watch?this.watchId=this.geolocation.watchPosition(OpenLayers.Function.bind(this.geolocate,
this),OpenLayers.Function.bind(this.failure,this),this.geolocationOptions):this.getCurrentLocation(),!0):!1:(this.events.triggerEvent("locationuncapable"),!1)},deactivate:function(){this.active&&null!==this.watchId&&this.geolocation.clearWatch(this.watchId);return OpenLayers.Control.prototype.deactivate.apply(this,arguments)},geolocate:function(a){var b=(new OpenLayers.LonLat(a.coords.longitude,a.coords.latitude)).transform(new OpenLayers.Projection("EPSG:4326"),this.map.getProjectionObject());this.bind&&
this.map.setCenter(b);this.events.triggerEvent("locationupdated",{position:a,point:new OpenLayers.Geometry.Point(b.lon,b.lat)})},getCurrentLocation:function(){if(!this.active||this.watch)return!1;this.geolocation.getCurrentPosition(OpenLayers.Function.bind(this.geolocate,this),OpenLayers.Function.bind(this.failure,this),this.geolocationOptions);return!0},failure:function(a){this.events.triggerEvent("locationfailed",{error:a})},CLASS_NAME:"OpenLayers.Control.Geolocate"});OpenLayers.Tile.UTFGrid=OpenLayers.Class(OpenLayers.Tile,{url:null,utfgridResolution:2,json:null,format:null,destroy:function(){this.clear();OpenLayers.Tile.prototype.destroy.apply(this,arguments)},draw:function(){var a=OpenLayers.Tile.prototype.draw.apply(this,arguments);if(a)if(this.isLoading?(this.abortLoading(),this.events.triggerEvent("reload")):(this.isLoading=!0,this.events.triggerEvent("loadstart")),this.url=this.layer.getURL(this.bounds),this.layer.useJSONP){var b=new OpenLayers.Protocol.Script({url:this.url,
callback:function(a){this.isLoading=!1;this.events.triggerEvent("loadend");this.json=a.data},scope:this});b.read();this.request=b}else this.request=OpenLayers.Request.GET({url:this.url,callback:function(a){this.isLoading=!1;this.events.triggerEvent("loadend");200===a.status&&this.parseData(a.responseText)},scope:this});else this.unload();return a},abortLoading:function(){this.request&&(this.request.abort(),delete this.request);this.isLoading=!1},getFeatureInfo:function(a,b){var c=null;if(this.json){var d=
this.getFeatureId(a,b);null!==d&&(c={id:d,data:this.json.data[d]})}return c},getFeatureId:function(a,b){var c=null;if(this.json){var d=this.utfgridResolution,d=this.json.grid[Math.floor(b/d)].charCodeAt(Math.floor(a/d)),d=this.indexFromCharCode(d),e=this.json.keys;!isNaN(d)&&d in e&&(c=e[d])}return c},indexFromCharCode:function(a){93<=a&&a--;35<=a&&a--;return a-32},parseData:function(a){this.format||(this.format=new OpenLayers.Format.JSON);this.json=this.format.read(a)},clear:function(){this.json=
null},CLASS_NAME:"OpenLayers.Tile.UTFGrid"});OpenLayers.Protocol.HTTP=OpenLayers.Class(OpenLayers.Protocol,{url:null,headers:null,params:null,callback:null,scope:null,readWithPOST:!1,updateWithPOST:!1,deleteWithPOST:!1,wildcarded:!1,srsInBBOX:!1,initialize:function(a){a=a||{};this.params={};this.headers={};OpenLayers.Protocol.prototype.initialize.apply(this,arguments);if(!this.filterToParams&&OpenLayers.Format.QueryStringFilter){var b=new OpenLayers.Format.QueryStringFilter({wildcarded:this.wildcarded,srsInBBOX:this.srsInBBOX});this.filterToParams=
function(a,d){return b.write(a,d)}}},destroy:function(){this.headers=this.params=null;OpenLayers.Protocol.prototype.destroy.apply(this)},read:function(a){OpenLayers.Protocol.prototype.read.apply(this,arguments);a=a||{};a.params=OpenLayers.Util.applyDefaults(a.params,this.options.params);a=OpenLayers.Util.applyDefaults(a,this.options);a.filter&&this.filterToParams&&(a.params=this.filterToParams(a.filter,a.params));var b=void 0!==a.readWithPOST?a.readWithPOST:this.readWithPOST,c=new OpenLayers.Protocol.Response({requestType:"read"});
b?(b=a.headers||{},b["Content-Type"]="application/x-www-form-urlencoded",c.priv=OpenLayers.Request.POST({url:a.url,callback:this.createCallback(this.handleRead,c,a),data:OpenLayers.Util.getParameterString(a.params),headers:b})):c.priv=OpenLayers.Request.GET({url:a.url,callback:this.createCallback(this.handleRead,c,a),params:a.params,headers:a.headers});return c},handleRead:function(a,b){this.handleResponse(a,b)},create:function(a,b){b=OpenLayers.Util.applyDefaults(b,this.options);var c=new OpenLayers.Protocol.Response({reqFeatures:a,
requestType:"create"});c.priv=OpenLayers.Request.POST({url:b.url,callback:this.createCallback(this.handleCreate,c,b),headers:b.headers,data:this.format.write(a)});return c},handleCreate:function(a,b){this.handleResponse(a,b)},update:function(a,b){b=b||{};var c=b.url||a.url||this.options.url+"/"+a.fid;b=OpenLayers.Util.applyDefaults(b,this.options);var d=new OpenLayers.Protocol.Response({reqFeatures:a,requestType:"update"});d.priv=OpenLayers.Request[this.updateWithPOST?"POST":"PUT"]({url:c,callback:this.createCallback(this.handleUpdate,
d,b),headers:b.headers,data:this.format.write(a)});return d},handleUpdate:function(a,b){this.handleResponse(a,b)},"delete":function(a,b){b=b||{};var c=b.url||a.url||this.options.url+"/"+a.fid;b=OpenLayers.Util.applyDefaults(b,this.options);var d=new OpenLayers.Protocol.Response({reqFeatures:a,requestType:"delete"}),e=this.deleteWithPOST?"POST":"DELETE",c={url:c,callback:this.createCallback(this.handleDelete,d,b),headers:b.headers};this.deleteWithPOST&&(c.data=this.format.write(a));d.priv=OpenLayers.Request[e](c);
return d},handleDelete:function(a,b){this.handleResponse(a,b)},handleResponse:function(a,b){var c=a.priv;b.callback&&(200<=c.status&&300>c.status?("delete"!=a.requestType&&(a.features=this.parseFeatures(c)),a.code=OpenLayers.Protocol.Response.SUCCESS):a.code=OpenLayers.Protocol.Response.FAILURE,b.callback.call(b.scope,a))},parseFeatures:function(a){var b=a.responseXML;b&&b.documentElement||(b=a.responseText);return!b||0>=b.length?null:this.format.read(b)},commit:function(a,b){function c(a){for(var b=
a.features?a.features.length:0,c=Array(b),e=0;e<b;++e)c[e]=a.features[e].fid;r.insertIds=c;d.apply(this,[a])}function d(a){this.callUserCallback(a,b);q=q&&a.success();f++;f>=p&&b.callback&&(r.code=q?OpenLayers.Protocol.Response.SUCCESS:OpenLayers.Protocol.Response.FAILURE,b.callback.apply(b.scope,[r]))}b=OpenLayers.Util.applyDefaults(b,this.options);var e=[],f=0,g={};g[OpenLayers.State.INSERT]=[];g[OpenLayers.State.UPDATE]=[];g[OpenLayers.State.DELETE]=[];for(var h,k,l=[],m=0,n=a.length;m<n;++m)if(h=
a[m],k=g[h.state])k.push(h),l.push(h);var p=(0<g[OpenLayers.State.INSERT].length?1:0)+g[OpenLayers.State.UPDATE].length+g[OpenLayers.State.DELETE].length,q=!0,r=new OpenLayers.Protocol.Response({reqFeatures:l});h=g[OpenLayers.State.INSERT];0<h.length&&e.push(this.create(h,OpenLayers.Util.applyDefaults({callback:c,scope:this},b.create)));h=g[OpenLayers.State.UPDATE];for(m=h.length-1;0<=m;--m)e.push(this.update(h[m],OpenLayers.Util.applyDefaults({callback:d,scope:this},b.update)));h=g[OpenLayers.State.DELETE];
for(m=h.length-1;0<=m;--m)e.push(this["delete"](h[m],OpenLayers.Util.applyDefaults({callback:d,scope:this},b["delete"])));return e},abort:function(a){a&&a.priv.abort()},callUserCallback:function(a,b){var c=b[a.requestType];c&&c.callback&&c.callback.call(c.scope,a)},CLASS_NAME:"OpenLayers.Protocol.HTTP"});OpenLayers.Strategy.Cluster=OpenLayers.Class(OpenLayers.Strategy,{distance:20,threshold:null,features:null,clusters:null,clustering:!1,resolution:null,activate:function(){var a=OpenLayers.Strategy.prototype.activate.call(this);if(a)this.layer.events.on({beforefeaturesadded:this.cacheFeatures,featuresremoved:this.clearCache,moveend:this.cluster,scope:this});return a},deactivate:function(){var a=OpenLayers.Strategy.prototype.deactivate.call(this);a&&(this.clearCache(),this.layer.events.un({beforefeaturesadded:this.cacheFeatures,
featuresremoved:this.clearCache,moveend:this.cluster,scope:this}));return a},cacheFeatures:function(a){var b=!0;this.clustering||(this.clearCache(),this.features=a.features,this.cluster(),b=!1);return b},clearCache:function(){this.clustering||(this.features=null)},cluster:function(a){if((!a||a.zoomChanged)&&this.features&&(a=this.layer.map.getResolution(),a!=this.resolution||!this.clustersExist())){this.resolution=a;a=[];for(var b,c,d,e=0;e<this.features.length;++e)if(b=this.features[e],b.geometry){c=
!1;for(var f=a.length-1;0<=f;--f)if(d=a[f],this.shouldCluster(d,b)){this.addToCluster(d,b);c=!0;break}c||a.push(this.createCluster(this.features[e]))}this.clustering=!0;this.layer.removeAllFeatures();this.clustering=!1;if(0<a.length){if(1<this.threshold)for(b=a.slice(),a=[],e=0,d=b.length;e<d;++e)c=b[e],c.attributes.count<this.threshold?Array.prototype.push.apply(a,c.cluster):a.push(c);this.clustering=!0;this.layer.addFeatures(a);this.clustering=!1}this.clusters=a}},clustersExist:function(){var a=
!1;if(this.clusters&&0<this.clusters.length&&this.clusters.length==this.layer.features.length)for(var a=!0,b=0;b<this.clusters.length;++b)if(this.clusters[b]!=this.layer.features[b]){a=!1;break}return a},shouldCluster:function(a,b){var c=a.geometry.getBounds().getCenterLonLat(),d=b.geometry.getBounds().getCenterLonLat();return Math.sqrt(Math.pow(c.lon-d.lon,2)+Math.pow(c.lat-d.lat,2))/this.resolution<=this.distance},addToCluster:function(a,b){a.cluster.push(b);a.attributes.count+=1},createCluster:function(a){var b=
a.geometry.getBounds().getCenterLonLat(),b=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(b.lon,b.lat),{count:1});b.cluster=[a];return b},CLASS_NAME:"OpenLayers.Strategy.Cluster"});OpenLayers.Strategy.Filter=OpenLayers.Class(OpenLayers.Strategy,{filter:null,cache:null,caching:!1,activate:function(){var a=OpenLayers.Strategy.prototype.activate.apply(this,arguments);a&&(this.cache=[],this.layer.events.on({beforefeaturesadded:this.handleAdd,beforefeaturesremoved:this.handleRemove,scope:this}));return a},deactivate:function(){this.cache=null;this.layer&&this.layer.events&&this.layer.events.un({beforefeaturesadded:this.handleAdd,beforefeaturesremoved:this.handleRemove,scope:this});
return OpenLayers.Strategy.prototype.deactivate.apply(this,arguments)},handleAdd:function(a){if(!this.caching&&this.filter){var b=a.features;a.features=[];for(var c,d=0,e=b.length;d<e;++d)c=b[d],this.filter.evaluate(c)?a.features.push(c):this.cache.push(c)}},handleRemove:function(a){this.caching||(this.cache=[])},setFilter:function(a){this.filter=a;a=this.cache;this.cache=[];this.handleAdd({features:this.layer.features});0<this.cache.length&&(this.caching=!0,this.layer.removeFeatures(this.cache.slice()),
this.caching=!1);0<a.length&&(a={features:a},this.handleAdd(a),0<a.features.length&&(this.caching=!0,this.layer.addFeatures(a.features),this.caching=!1))},CLASS_NAME:"OpenLayers.Strategy.Filter"});OpenLayers.Protocol.SOS=function(a){a=OpenLayers.Util.applyDefaults(a,OpenLayers.Protocol.SOS.DEFAULTS);var b=OpenLayers.Protocol.SOS["v"+a.version.replace(/\./g,"_")];if(!b)throw"Unsupported SOS version: "+a.version;return new b(a)};OpenLayers.Protocol.SOS.DEFAULTS={version:"1.0.0"};OpenLayers.Format.WFSDescribeFeatureType=OpenLayers.Class(OpenLayers.Format.XML,{regExes:{trimSpace:/^\s*|\s*$/g},namespaces:{xsd:"http://www.w3.org/2001/XMLSchema"},readers:{xsd:{schema:function(a,b){var c=[],d={},e,f;this.readChildNodes(a,{complexTypes:c,customTypes:d});var g=a.attributes,h,k;e=0;for(f=g.length;e<f;++e)h=g[e],k=h.name,0===k.indexOf("xmlns")?this.setNamespace(k.split(":")[1]||"",h.value):b[k]=h.value;b.featureTypes=c;b.targetPrefix=this.namespaceAlias[b.targetNamespace];e=0;for(f=
c.length;e<f;++e)g=c[e],h=d[g.typeName],d[g.typeName]&&(g.typeName=h.name)},complexType:function(a,b){var c={typeName:a.getAttribute("name")};this.readChildNodes(a,c);b.complexTypes.push(c)},complexContent:function(a,b){this.readChildNodes(a,b)},extension:function(a,b){this.readChildNodes(a,b)},sequence:function(a,b){var c={elements:[]};this.readChildNodes(a,c);b.properties=c.elements},element:function(a,b){var c;if(b.elements){var d={};c=a.attributes;for(var e,f=0,g=c.length;f<g;++f)e=c[f],d[e.name]=
e.value;c=d.type;c||(c={},this.readChildNodes(a,c),d.restriction=c,d.type=c.base);d.localType=(c.base||c).split(":").pop();b.elements.push(d);this.readChildNodes(a,d)}b.complexTypes&&(c=a.getAttribute("type"),d=c.split(":").pop(),b.customTypes[d]={name:a.getAttribute("name"),type:c})},annotation:function(a,b){b.annotation={};this.readChildNodes(a,b.annotation)},appinfo:function(a,b){b.appinfo||(b.appinfo=[]);b.appinfo.push(this.getChildValue(a))},documentation:function(a,b){b.documentation||(b.documentation=
[]);var c=this.getChildValue(a);b.documentation.push({lang:a.getAttribute("xml:lang"),textContent:c.replace(this.regExes.trimSpace,"")})},simpleType:function(a,b){this.readChildNodes(a,b)},restriction:function(a,b){b.base=a.getAttribute("base");this.readRestriction(a,b)}}},readRestriction:function(a,b){for(var c=a.childNodes,d,e,f=0,g=c.length;f<g;++f)d=c[f],1==d.nodeType&&(e=d.nodeName.split(":").pop(),d=d.getAttribute("value"),b[e]?("string"==typeof b[e]&&(b[e]=[b[e]]),b[e].push(d)):b[e]=d)},read:function(a){"string"==
typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={};if("ExceptionReport"===a.nodeName.split(":").pop()){var c=new OpenLayers.Format.OGCExceptionReport;b.error=c.read(a)}else this.readNode(a,b);return b},CLASS_NAME:"OpenLayers.Format.WFSDescribeFeatureType"});OpenLayers.Format.GeoRSS=OpenLayers.Class(OpenLayers.Format.XML,{rssns:"http://backend.userland.com/rss2",featureNS:"http://mapserver.gis.umn.edu/mapserver",georssns:"http://www.georss.org/georss",geons:"http://www.w3.org/2003/01/geo/wgs84_pos#",featureTitle:"Untitled",featureDescription:"No Description",gmlParser:null,xy:!1,createGeometryFromItem:function(a){var b=this.getElementsByTagNameNS(a,this.georssns,"point"),c=this.getElementsByTagNameNS(a,this.geons,"lat"),d=this.getElementsByTagNameNS(a,
this.geons,"long"),e=this.getElementsByTagNameNS(a,this.georssns,"line"),f=this.getElementsByTagNameNS(a,this.georssns,"polygon"),g=this.getElementsByTagNameNS(a,this.georssns,"where");a=this.getElementsByTagNameNS(a,this.georssns,"box");if(0<b.length||0<c.length&&0<d.length){0<b.length?(c=OpenLayers.String.trim(b[0].firstChild.nodeValue).split(/\s+/),2!=c.length&&(c=OpenLayers.String.trim(b[0].firstChild.nodeValue).split(/\s*,\s*/))):c=[parseFloat(c[0].firstChild.nodeValue),parseFloat(d[0].firstChild.nodeValue)];
var h=new OpenLayers.Geometry.Point(c[1],c[0])}else if(0<e.length){c=OpenLayers.String.trim(this.getChildValue(e[0])).split(/\s+/);d=[];e=0;for(f=c.length;e<f;e+=2)b=new OpenLayers.Geometry.Point(c[e+1],c[e]),d.push(b);h=new OpenLayers.Geometry.LineString(d)}else if(0<f.length){c=OpenLayers.String.trim(this.getChildValue(f[0])).split(/\s+/);d=[];e=0;for(f=c.length;e<f;e+=2)b=new OpenLayers.Geometry.Point(c[e+1],c[e]),d.push(b);h=new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(d)])}else 0<
g.length?(this.gmlParser||(this.gmlParser=new OpenLayers.Format.GML({xy:this.xy})),h=this.gmlParser.parseFeature(g[0]).geometry):0<a.length&&(c=OpenLayers.String.trim(a[0].firstChild.nodeValue).split(/\s+/),d=[],3<c.length&&(b=new OpenLayers.Geometry.Point(c[1],c[0]),d.push(b),b=new OpenLayers.Geometry.Point(c[1],c[2]),d.push(b),b=new OpenLayers.Geometry.Point(c[3],c[2]),d.push(b),b=new OpenLayers.Geometry.Point(c[3],c[0]),d.push(b),b=new OpenLayers.Geometry.Point(c[1],c[0]),d.push(b)),h=new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(d)]));
h&&(this.internalProjection&&this.externalProjection)&&h.transform(this.externalProjection,this.internalProjection);return h},createFeatureFromItem:function(a){var b=this.createGeometryFromItem(a),c=this._getChildValue(a,"*","title",this.featureTitle),d=this._getChildValue(a,"*","description",this._getChildValue(a,"*","content",this._getChildValue(a,"*","summary",this.featureDescription))),e=this._getChildValue(a,"*","link");if(!e)try{e=this.getElementsByTagNameNS(a,"*","link")[0].getAttribute("href")}catch(f){e=
null}a=this._getChildValue(a,"*","id",null);b=new OpenLayers.Feature.Vector(b,{title:c,description:d,link:e});b.fid=a;return b},_getChildValue:function(a,b,c,d){return(a=this.getElementsByTagNameNS(a,b,c))&&a[0]&&a[0].firstChild&&a[0].firstChild.nodeValue?this.getChildValue(a[0]):void 0==d?"":d},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));var b=null,b=this.getElementsByTagNameNS(a,"*","item");0==b.length&&(b=this.getElementsByTagNameNS(a,"*","entry"));
a=b.length;for(var c=Array(a),d=0;d<a;d++)c[d]=this.createFeatureFromItem(b[d]);return c},write:function(a){var b;if(OpenLayers.Util.isArray(a)){b=this.createElementNS(this.rssns,"rss");for(var c=0,d=a.length;c<d;c++)b.appendChild(this.createFeatureXML(a[c]))}else b=this.createFeatureXML(a);return OpenLayers.Format.XML.prototype.write.apply(this,[b])},createFeatureXML:function(a){var b=this.buildGeometryNode(a.geometry),c=this.createElementNS(this.rssns,"item"),d=this.createElementNS(this.rssns,"title");
d.appendChild(this.createTextNode(a.attributes.title?a.attributes.title:""));var e=this.createElementNS(this.rssns,"description");e.appendChild(this.createTextNode(a.attributes.description?a.attributes.description:""));c.appendChild(d);c.appendChild(e);a.attributes.link&&(d=this.createElementNS(this.rssns,"link"),d.appendChild(this.createTextNode(a.attributes.link)),c.appendChild(d));for(var f in a.attributes)"link"!=f&&("title"!=f&&"description"!=f)&&(d=this.createTextNode(a.attributes[f]),e=f,-1!=
f.search(":")&&(e=f.split(":")[1]),e=this.createElementNS(this.featureNS,"feature:"+e),e.appendChild(d),c.appendChild(e));c.appendChild(b);return c},buildGeometryNode:function(a){this.internalProjection&&this.externalProjection&&(a=a.clone(),a.transform(this.internalProjection,this.externalProjection));var b;if("OpenLayers.Geometry.Polygon"==a.CLASS_NAME)b=this.createElementNS(this.georssns,"georss:polygon"),b.appendChild(this.buildCoordinatesNode(a.components[0]));else if("OpenLayers.Geometry.LineString"==
a.CLASS_NAME)b=this.createElementNS(this.georssns,"georss:line"),b.appendChild(this.buildCoordinatesNode(a));else if("OpenLayers.Geometry.Point"==a.CLASS_NAME)b=this.createElementNS(this.georssns,"georss:point"),b.appendChild(this.buildCoordinatesNode(a));else throw"Couldn't parse "+a.CLASS_NAME;return b},buildCoordinatesNode:function(a){var b=null;a.components&&(b=a.components);if(b){a=b.length;for(var c=Array(a),d=0;d<a;d++)c[d]=b[d].y+" "+b[d].x;b=c.join(" ")}else b=a.y+" "+a.x;return this.createTextNode(b)},
CLASS_NAME:"OpenLayers.Format.GeoRSS"});OpenLayers.Format.WPSCapabilities=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.0.0",CLASS_NAME:"OpenLayers.Format.WPSCapabilities"});OpenLayers.Format.WPSCapabilities.v1_0_0=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{ows:"http://www.opengis.net/ows/1.1",wps:"http://www.opengis.net/wps/1.0.0",xlink:"http://www.w3.org/1999/xlink"},regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);
var b={};this.readNode(a,b);return b},readers:{wps:{Capabilities:function(a,b){this.readChildNodes(a,b)},ProcessOfferings:function(a,b){b.processOfferings={};this.readChildNodes(a,b.processOfferings)},Process:function(a,b){var c={processVersion:this.getAttributeNS(a,this.namespaces.wps,"processVersion")};this.readChildNodes(a,c);b[c.identifier]=c},Languages:function(a,b){b.languages=[];this.readChildNodes(a,b.languages)},Default:function(a,b){var c={isDefault:!0};this.readChildNodes(a,c);b.push(c)},
Supported:function(a,b){var c={};this.readChildNodes(a,c);b.push(c)}},ows:OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers.ows},CLASS_NAME:"OpenLayers.Format.WPSCapabilities.v1_0_0"});OpenLayers.Control.PinchZoom=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,pinchOrigin:null,currentCenter:null,autoActivate:!0,preserveCenter:!1,initialize:function(a){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.handler=new OpenLayers.Handler.Pinch(this,{start:this.pinchStart,move:this.pinchMove,done:this.pinchDone},this.handlerOptions)},pinchStart:function(a,b){var c=this.preserveCenter?this.map.getPixelFromLonLat(this.map.getCenter()):a.xy;this.currentCenter=
this.pinchOrigin=c},pinchMove:function(a,b){var c=b.scale,d=this.map.layerContainerOriginPx,e=this.pinchOrigin,f=this.preserveCenter?this.map.getPixelFromLonLat(this.map.getCenter()):a.xy,g=Math.round(d.x+f.x-e.x+(c-1)*(d.x-e.x)),d=Math.round(d.y+f.y-e.y+(c-1)*(d.y-e.y));this.map.applyTransform(g,d,c);this.currentCenter=f},pinchDone:function(a,b,c){this.map.applyTransform();a=this.map.getZoomForResolution(this.map.getResolution()/c.scale,!0);if(a!==this.map.getZoom()||!this.currentCenter.equals(this.pinchOrigin)){b=
this.map.getResolutionForZoom(a);c=this.map.getLonLatFromPixel(this.pinchOrigin);var d=this.currentCenter,e=this.map.getSize();c.lon+=b*(e.w/2-d.x);c.lat-=b*(e.h/2-d.y);this.map.div.clientWidth=this.map.div.clientWidth;this.map.setCenter(c,a)}},CLASS_NAME:"OpenLayers.Control.PinchZoom"});OpenLayers.Control.TouchNavigation=OpenLayers.Class(OpenLayers.Control,{dragPan:null,dragPanOptions:null,pinchZoom:null,pinchZoomOptions:null,clickHandlerOptions:null,documentDrag:!1,autoActivate:!0,initialize:function(a){this.handlers={};OpenLayers.Control.prototype.initialize.apply(this,arguments)},destroy:function(){this.deactivate();this.dragPan&&this.dragPan.destroy();this.dragPan=null;this.pinchZoom&&(this.pinchZoom.destroy(),delete this.pinchZoom);OpenLayers.Control.prototype.destroy.apply(this,
arguments)},activate:function(){return OpenLayers.Control.prototype.activate.apply(this,arguments)?(this.dragPan.activate(),this.handlers.click.activate(),this.pinchZoom.activate(),!0):!1},deactivate:function(){return OpenLayers.Control.prototype.deactivate.apply(this,arguments)?(this.dragPan.deactivate(),this.handlers.click.deactivate(),this.pinchZoom.deactivate(),!0):!1},draw:function(){var a={click:this.defaultClick,dblclick:this.defaultDblClick},b=OpenLayers.Util.extend({"double":!0,stopDouble:!0,
pixelTolerance:2},this.clickHandlerOptions);this.handlers.click=new OpenLayers.Handler.Click(this,a,b);this.dragPan=new OpenLayers.Control.DragPan(OpenLayers.Util.extend({map:this.map,documentDrag:this.documentDrag},this.dragPanOptions));this.dragPan.draw();this.pinchZoom=new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({map:this.map},this.pinchZoomOptions))},defaultClick:function(a){a.lastTouches&&2==a.lastTouches.length&&this.map.zoomOut()},defaultDblClick:function(a){this.map.zoomTo(this.map.zoom+
1,a.xy)},CLASS_NAME:"OpenLayers.Control.TouchNavigation"});OpenLayers.Console.warn("OpenLayers.Rico is deprecated");OpenLayers.Rico=OpenLayers.Rico||{};
OpenLayers.Rico.Color=OpenLayers.Class({initialize:function(a,b,c){this.rgb={r:a,g:b,b:c}},setRed:function(a){this.rgb.r=a},setGreen:function(a){this.rgb.g=a},setBlue:function(a){this.rgb.b=a},setHue:function(a){var b=this.asHSB();b.h=a;this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,b.b)},setSaturation:function(a){var b=this.asHSB();b.s=a;this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,b.b)},setBrightness:function(a){var b=this.asHSB();b.b=a;this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,b.b)},
darken:function(a){var b=this.asHSB();this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,Math.max(b.b-a,0))},brighten:function(a){var b=this.asHSB();this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,Math.min(b.b+a,1))},blend:function(a){this.rgb.r=Math.floor((this.rgb.r+a.rgb.r)/2);this.rgb.g=Math.floor((this.rgb.g+a.rgb.g)/2);this.rgb.b=Math.floor((this.rgb.b+a.rgb.b)/2)},isBright:function(){this.asHSB();return 0.5<this.asHSB().b},isDark:function(){return!this.isBright()},asRGB:function(){return"rgb("+
this.rgb.r+","+this.rgb.g+","+this.rgb.b+")"},asHex:function(){return"#"+this.rgb.r.toColorPart()+this.rgb.g.toColorPart()+this.rgb.b.toColorPart()},asHSB:function(){return OpenLayers.Rico.Color.RGBtoHSB(this.rgb.r,this.rgb.g,this.rgb.b)},toString:function(){return this.asHex()}});
OpenLayers.Rico.Color.createFromHex=function(a){if(4==a.length){var b=a;a="#";for(var c=1;4>c;c++)a+=b.charAt(c)+b.charAt(c)}0==a.indexOf("#")&&(a=a.substring(1));b=a.substring(0,2);c=a.substring(2,4);a=a.substring(4,6);return new OpenLayers.Rico.Color(parseInt(b,16),parseInt(c,16),parseInt(a,16))};
OpenLayers.Rico.Color.createColorFromBackground=function(a){var b=OpenLayers.Element.getStyle(OpenLayers.Util.getElement(a),"backgroundColor");return"transparent"==b&&a.parentNode?OpenLayers.Rico.Color.createColorFromBackground(a.parentNode):null==b?new OpenLayers.Rico.Color(255,255,255):0==b.indexOf("rgb(")?(a=b.substring(4,b.length-1).split(","),new OpenLayers.Rico.Color(parseInt(a[0]),parseInt(a[1]),parseInt(a[2]))):0==b.indexOf("#")?OpenLayers.Rico.Color.createFromHex(b):new OpenLayers.Rico.Color(255,
255,255)};
OpenLayers.Rico.Color.HSBtoRGB=function(a,b,c){var d=0,e=0,f=0;if(0==b)f=e=d=parseInt(255*c+0.5);else{a=6*(a-Math.floor(a));var g=a-Math.floor(a),h=c*(1-b),k=c*(1-b*g);b=c*(1-b*(1-g));switch(parseInt(a)){case 0:d=255*c+0.5;e=255*b+0.5;f=255*h+0.5;break;case 1:d=255*k+0.5;e=255*c+0.5;f=255*h+0.5;break;case 2:d=255*h+0.5;e=255*c+0.5;f=255*b+0.5;break;case 3:d=255*h+0.5;e=255*k+0.5;f=255*c+0.5;break;case 4:d=255*b+0.5;e=255*h+0.5;f=255*c+0.5;break;case 5:d=255*c+0.5,e=255*h+0.5,f=255*k+0.5}}return{r:parseInt(d),g:parseInt(e),
b:parseInt(f)}};OpenLayers.Rico.Color.RGBtoHSB=function(a,b,c){var d,e=a>b?a:b;c>e&&(e=c);var f=a<b?a:b;c<f&&(f=c);d=0!=e?(e-f)/e:0;if(0==d)a=0;else{var g=(e-a)/(e-f),h=(e-b)/(e-f);c=(e-c)/(e-f);a=(a==e?c-h:b==e?2+g-c:4+h-g)/6;0>a&&(a+=1)}return{h:a,s:d,b:e/255}};OpenLayers.Style2=OpenLayers.Class({id:null,name:null,title:null,description:null,layerName:null,isDefault:!1,rules:null,initialize:function(a){OpenLayers.Util.extend(this,a);this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},destroy:function(){for(var a=0,b=this.rules.length;a<b;a++)this.rules[a].destroy();delete this.rules},clone:function(){var a=OpenLayers.Util.extend({},this);if(this.rules){a.rules=[];for(var b=0,c=this.rules.length;b<c;++b)a.rules.push(this.rules[b].clone())}return new OpenLayers.Style2(a)},
CLASS_NAME:"OpenLayers.Style2"});OpenLayers.Format.WFS=OpenLayers.Class(OpenLayers.Format.GML,{layer:null,wfsns:"http://www.opengis.net/wfs",ogcns:"http://www.opengis.net/ogc",initialize:function(a,b){OpenLayers.Format.GML.prototype.initialize.apply(this,[a]);this.layer=b;this.layer.featureNS&&(this.featureNS=this.layer.featureNS);this.layer.options.geometry_column&&(this.geometryName=this.layer.options.geometry_column);this.layer.options.typename&&(this.featureName=this.layer.options.typename)},write:function(a){var b=this.createElementNS(this.wfsns,
"wfs:Transaction");b.setAttribute("version","1.0.0");b.setAttribute("service","WFS");for(var c=0;c<a.length;c++)switch(a[c].state){case OpenLayers.State.INSERT:b.appendChild(this.insert(a[c]));break;case OpenLayers.State.UPDATE:b.appendChild(this.update(a[c]));break;case OpenLayers.State.DELETE:b.appendChild(this.remove(a[c]))}return OpenLayers.Format.XML.prototype.write.apply(this,[b])},createFeatureXML:function(a){var b=this.buildGeometryNode(a.geometry),c=this.createElementNS(this.featureNS,"feature:"+
this.geometryName);c.appendChild(b);b=this.createElementNS(this.featureNS,"feature:"+this.featureName);b.appendChild(c);for(var d in a.attributes){var c=this.createTextNode(a.attributes[d]),e=d;-1!=d.search(":")&&(e=d.split(":")[1]);e=this.createElementNS(this.featureNS,"feature:"+e);e.appendChild(c);b.appendChild(e)}return b},insert:function(a){var b=this.createElementNS(this.wfsns,"wfs:Insert");b.appendChild(this.createFeatureXML(a));return b},update:function(a){a.fid||OpenLayers.Console.userError(OpenLayers.i18n("noFID"));
var b=this.createElementNS(this.wfsns,"wfs:Update");b.setAttribute("typeName",this.featurePrefix+":"+this.featureName);b.setAttribute("xmlns:"+this.featurePrefix,this.featureNS);var c=this.createElementNS(this.wfsns,"wfs:Property"),d=this.createElementNS(this.wfsns,"wfs:Name"),e=this.createTextNode(this.geometryName);d.appendChild(e);c.appendChild(d);d=this.createElementNS(this.wfsns,"wfs:Value");e=this.buildGeometryNode(a.geometry);a.layer&&e.setAttribute("srsName",a.layer.projection.getCode());
d.appendChild(e);c.appendChild(d);b.appendChild(c);for(var f in a.attributes)c=this.createElementNS(this.wfsns,"wfs:Property"),d=this.createElementNS(this.wfsns,"wfs:Name"),d.appendChild(this.createTextNode(f)),c.appendChild(d),d=this.createElementNS(this.wfsns,"wfs:Value"),d.appendChild(this.createTextNode(a.attributes[f])),c.appendChild(d),b.appendChild(c);c=this.createElementNS(this.ogcns,"ogc:Filter");f=this.createElementNS(this.ogcns,"ogc:FeatureId");f.setAttribute("fid",a.fid);c.appendChild(f);
b.appendChild(c);return b},remove:function(a){if(!a.fid)return OpenLayers.Console.userError(OpenLayers.i18n("noFID")),!1;var b=this.createElementNS(this.wfsns,"wfs:Delete");b.setAttribute("typeName",this.featurePrefix+":"+this.featureName);b.setAttribute("xmlns:"+this.featurePrefix,this.featureNS);var c=this.createElementNS(this.ogcns,"ogc:Filter"),d=this.createElementNS(this.ogcns,"ogc:FeatureId");d.setAttribute("fid",a.fid);c.appendChild(d);b.appendChild(c);return b},destroy:function(){this.layer=
null},CLASS_NAME:"OpenLayers.Format.WFS"});OpenLayers.Format.SLD.v1_0_0_GeoServer=OpenLayers.Class(OpenLayers.Format.SLD.v1_0_0,{version:"1.0.0",profile:"GeoServer",readers:OpenLayers.Util.applyDefaults({sld:OpenLayers.Util.applyDefaults({Priority:function(a,b){var c=this.readers.ogc._expression.call(this,a);c&&(b.priority=c)},VendorOption:function(a,b){b.vendorOptions||(b.vendorOptions={});b.vendorOptions[a.getAttribute("name")]=this.getChildValue(a)},TextSymbolizer:function(a,b){OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this,
arguments);var c=this.multipleSymbolizers?b.symbolizers[b.symbolizers.length-1]:b.symbolizer.Text;void 0===c.graphic&&(c.graphic=!1)}},OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld)},OpenLayers.Format.SLD.v1_0_0.prototype.readers),writers:OpenLayers.Util.applyDefaults({sld:OpenLayers.Util.applyDefaults({Priority:function(a){return this.writers.sld._OGCExpression.call(this,"sld:Priority",a)},VendorOption:function(a){return this.createElementNSPlus("sld:VendorOption",{attributes:{name:a.name},
value:a.value})},TextSymbolizer:function(a){var b=OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld.TextSymbolizer.apply(this,arguments);!1!==a.graphic&&(a.externalGraphic||a.graphicName)&&this.writeNode("Graphic",a,b);"priority"in a&&this.writeNode("Priority",a.priority,b);return this.addVendorOptions(b,a)},PointSymbolizer:function(a){var b=OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld.PointSymbolizer.apply(this,arguments);return this.addVendorOptions(b,a)},LineSymbolizer:function(a){var b=
OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld.LineSymbolizer.apply(this,arguments);return this.addVendorOptions(b,a)},PolygonSymbolizer:function(a){var b=OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld.PolygonSymbolizer.apply(this,arguments);return this.addVendorOptions(b,a)}},OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld)},OpenLayers.Format.SLD.v1_0_0.prototype.writers),addVendorOptions:function(a,b){if(b.vendorOptions)for(var c in b.vendorOptions)this.writeNode("VendorOption",{name:c,
value:b.vendorOptions[c]},a);return a},CLASS_NAME:"OpenLayers.Format.SLD.v1_0_0_GeoServer"});OpenLayers.Layer.Boxes=OpenLayers.Class(OpenLayers.Layer.Markers,{drawMarker:function(a){var b=this.map.getLayerPxFromLonLat({lon:a.bounds.left,lat:a.bounds.top}),c=this.map.getLayerPxFromLonLat({lon:a.bounds.right,lat:a.bounds.bottom});null==c||null==b?a.display(!1):(b=a.draw(b,{w:Math.max(1,c.x-b.x),h:Math.max(1,c.y-b.y)}),a.drawn||(this.div.appendChild(b),a.drawn=!0))},removeMarker:function(a){OpenLayers.Util.removeItem(this.markers,a);null!=a.div&&a.div.parentNode==this.div&&this.div.removeChild(a.div)},
CLASS_NAME:"OpenLayers.Layer.Boxes"});OpenLayers.Format.WFSCapabilities.v1_0_0=OpenLayers.Class(OpenLayers.Format.WFSCapabilities.v1,{readers:{wfs:OpenLayers.Util.applyDefaults({Service:function(a,b){b.service={};this.readChildNodes(a,b.service)},Fees:function(a,b){var c=this.getChildValue(a);c&&"none"!=c.toLowerCase()&&(b.fees=c)},AccessConstraints:function(a,b){var c=this.getChildValue(a);c&&"none"!=c.toLowerCase()&&(b.accessConstraints=c)},OnlineResource:function(a,b){var c=this.getChildValue(a);c&&"none"!=c.toLowerCase()&&(b.onlineResource=
c)},Keywords:function(a,b){var c=this.getChildValue(a);c&&"none"!=c.toLowerCase()&&(b.keywords=c.split(", "))},Capability:function(a,b){b.capability={};this.readChildNodes(a,b.capability)},Request:function(a,b){b.request={};this.readChildNodes(a,b.request)},GetFeature:function(a,b){b.getfeature={href:{},formats:[]};this.readChildNodes(a,b.getfeature)},ResultFormat:function(a,b){for(var c=a.childNodes,d,e=0;e<c.length;e++)d=c[e],1==d.nodeType&&b.formats.push(d.nodeName)},DCPType:function(a,b){this.readChildNodes(a,
b)},HTTP:function(a,b){this.readChildNodes(a,b.href)},Get:function(a,b){b.get=a.getAttribute("onlineResource")},Post:function(a,b){b.post=a.getAttribute("onlineResource")},SRS:function(a,b){var c=this.getChildValue(a);c&&(b.srs=c)}},OpenLayers.Format.WFSCapabilities.v1.prototype.readers.wfs)},CLASS_NAME:"OpenLayers.Format.WFSCapabilities.v1_0_0"});OpenLayers.Format.WMSCapabilities.v1_3=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1,{readers:{wms:OpenLayers.Util.applyDefaults({WMS_Capabilities:function(a,b){this.readChildNodes(a,b)},LayerLimit:function(a,b){b.layerLimit=parseInt(this.getChildValue(a))},MaxWidth:function(a,b){b.maxWidth=parseInt(this.getChildValue(a))},MaxHeight:function(a,b){b.maxHeight=parseInt(this.getChildValue(a))},BoundingBox:function(a,b){var c=OpenLayers.Format.WMSCapabilities.v1.prototype.readers.wms.BoundingBox.apply(this,
[a,b]);c.srs=a.getAttribute("CRS");b.bbox[c.srs]=c},CRS:function(a,b){this.readers.wms.SRS.apply(this,[a,b])},EX_GeographicBoundingBox:function(a,b){b.llbbox=[];this.readChildNodes(a,b.llbbox)},westBoundLongitude:function(a,b){b[0]=this.getChildValue(a)},eastBoundLongitude:function(a,b){b[2]=this.getChildValue(a)},southBoundLatitude:function(a,b){b[1]=this.getChildValue(a)},northBoundLatitude:function(a,b){b[3]=this.getChildValue(a)},MinScaleDenominator:function(a,b){b.maxScale=parseFloat(this.getChildValue(a)).toPrecision(16)},
MaxScaleDenominator:function(a,b){b.minScale=parseFloat(this.getChildValue(a)).toPrecision(16)},Dimension:function(a,b){var c={name:a.getAttribute("name").toLowerCase(),units:a.getAttribute("units"),unitsymbol:a.getAttribute("unitSymbol"),nearestVal:"1"===a.getAttribute("nearestValue"),multipleVal:"1"===a.getAttribute("multipleValues"),"default":a.getAttribute("default")||"",current:"1"===a.getAttribute("current"),values:this.getChildValue(a).split(",")};b.dimensions[c.name]=c},Keyword:function(a,
b){var c={value:this.getChildValue(a),vocabulary:a.getAttribute("vocabulary")};b.keywords&&b.keywords.push(c)}},OpenLayers.Format.WMSCapabilities.v1.prototype.readers.wms),sld:{UserDefinedSymbolization:function(a,b){this.readers.wms.UserDefinedSymbolization.apply(this,[a,b]);b.userSymbols.inlineFeature=1==parseInt(a.getAttribute("InlineFeature"));b.userSymbols.remoteWCS=1==parseInt(a.getAttribute("RemoteWCS"))},DescribeLayer:function(a,b){this.readers.wms.DescribeLayer.apply(this,[a,b])},GetLegendGraphic:function(a,
b){this.readers.wms.GetLegendGraphic.apply(this,[a,b])}}},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_3"});OpenLayers.Layer.Zoomify=OpenLayers.Class(OpenLayers.Layer.Grid,{size:null,isBaseLayer:!0,standardTileSize:256,tileOriginCorner:"tl",numberOfTiers:0,tileCountUpToTier:null,tierSizeInTiles:null,tierImageSize:null,initialize:function(a,b,c,d){this.initializeZoomify(c);OpenLayers.Layer.Grid.prototype.initialize.apply(this,[a,b,c,{},d])},initializeZoomify:function(a){var b=a.clone();this.size=a.clone();a=new OpenLayers.Size(Math.ceil(b.w/this.standardTileSize),Math.ceil(b.h/this.standardTileSize));this.tierSizeInTiles=
[a];for(this.tierImageSize=[b];b.w>this.standardTileSize||b.h>this.standardTileSize;)b=new OpenLayers.Size(Math.floor(b.w/2),Math.floor(b.h/2)),a=new OpenLayers.Size(Math.ceil(b.w/this.standardTileSize),Math.ceil(b.h/this.standardTileSize)),this.tierSizeInTiles.push(a),this.tierImageSize.push(b);this.tierSizeInTiles.reverse();this.tierImageSize.reverse();this.numberOfTiers=this.tierSizeInTiles.length;b=[1];this.tileCountUpToTier=[0];for(a=1;a<this.numberOfTiers;a++)b.unshift(Math.pow(2,a)),this.tileCountUpToTier.push(this.tierSizeInTiles[a-
1].w*this.tierSizeInTiles[a-1].h+this.tileCountUpToTier[a-1]);this.serverResolutions||(this.serverResolutions=b)},destroy:function(){OpenLayers.Layer.Grid.prototype.destroy.apply(this,arguments);this.tileCountUpToTier.length=0;this.tierSizeInTiles.length=0;this.tierImageSize.length=0},clone:function(a){null==a&&(a=new OpenLayers.Layer.Zoomify(this.name,this.url,this.size,this.options));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,[a])},getURL:function(a){a=this.adjustBounds(a);var b=
this.getServerResolution(),c=Math.round((a.left-this.tileOrigin.lon)/(b*this.tileSize.w));a=Math.round((this.tileOrigin.lat-a.top)/(b*this.tileSize.h));b=this.getZoomForResolution(b);c="TileGroup"+Math.floor((c+a*this.tierSizeInTiles[b].w+this.tileCountUpToTier[b])/256)+"/"+b+"-"+c+"-"+a+".jpg";b=this.url;OpenLayers.Util.isArray(b)&&(b=this.selectUrl(c,b));return b+c},getImageSize:function(){if(0<arguments.length){var a=this.adjustBounds(arguments[0]),b=this.getServerResolution(),c=Math.round((a.left-
this.tileOrigin.lon)/(b*this.tileSize.w)),a=Math.round((this.tileOrigin.lat-a.top)/(b*this.tileSize.h)),b=this.getZoomForResolution(b),d=this.standardTileSize,e=this.standardTileSize;c==this.tierSizeInTiles[b].w-1&&(d=this.tierImageSize[b].w%this.standardTileSize);a==this.tierSizeInTiles[b].h-1&&(e=this.tierImageSize[b].h%this.standardTileSize);return new OpenLayers.Size(d,e)}return this.tileSize},setMap:function(a){OpenLayers.Layer.Grid.prototype.setMap.apply(this,arguments);this.tileOrigin=new OpenLayers.LonLat(this.map.maxExtent.left,
this.map.maxExtent.top)},CLASS_NAME:"OpenLayers.Layer.Zoomify"});OpenLayers.Layer.MapServer=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{mode:"map",map_imagetype:"png"},initialize:function(a,b,c,d){OpenLayers.Layer.Grid.prototype.initialize.apply(this,arguments);this.params=OpenLayers.Util.applyDefaults(this.params,this.DEFAULT_PARAMS);if(null==d||null==d.isBaseLayer)this.isBaseLayer="true"!=this.params.transparent&&!0!=this.params.transparent},clone:function(a){null==a&&(a=new OpenLayers.Layer.MapServer(this.name,this.url,this.params,this.getOptions()));
return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,[a])},getURL:function(a){a=this.adjustBounds(a);a=[a.left,a.bottom,a.right,a.top];var b=this.getImageSize();return this.getFullRequestString({mapext:a,imgext:a,map_size:[b.w,b.h],imgx:b.w/2,imgy:b.h/2,imgxy:[b.w,b.h]})},getFullRequestString:function(a,b){var c=null==b?this.url:b,d=OpenLayers.Util.extend({},this.params),d=OpenLayers.Util.extend(d,a),e=OpenLayers.Util.getParameterString(d);OpenLayers.Util.isArray(c)&&(c=this.selectUrl(e,c));
var e=OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(c)),f;for(f in d)f.toUpperCase()in e&&delete d[f];e=OpenLayers.Util.getParameterString(d);d=c;e=e.replace(/,/g,"+");""!=e&&(f=c.charAt(c.length-1),d="&"==f||"?"==f?d+e:-1==c.indexOf("?")?d+("?"+e):d+("&"+e));return d},CLASS_NAME:"OpenLayers.Layer.MapServer"});OpenLayers.Renderer.VML=OpenLayers.Class(OpenLayers.Renderer.Elements,{xmlns:"urn:schemas-microsoft-com:vml",symbolCache:{},offset:null,initialize:function(a){if(this.supported()){if(!document.namespaces.olv){document.namespaces.add("olv",this.xmlns);for(var b=document.createStyleSheet(),c="shape rect oval fill stroke imagedata group textbox".split(" "),d=0,e=c.length;d<e;d++)b.addRule("olv\\:"+c[d],"behavior: url(#default#VML); position: absolute; display: inline-block;")}OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
arguments)}},supported:function(){return!!document.namespaces},setExtent:function(a,b){var c=OpenLayers.Renderer.Elements.prototype.setExtent.apply(this,arguments),d=this.getResolution(),e=a.left/d|0,d=a.top/d-this.size.h|0;b||!this.offset?(this.offset={x:e,y:d},d=e=0):(e-=this.offset.x,d-=this.offset.y);this.root.coordorigin=e-this.xOffset+" "+d;for(var e=[this.root,this.vectorRoot,this.textRoot],f=0,g=e.length;f<g;++f)d=e[f],d.coordsize=this.size.w+" "+this.size.h;this.root.style.flip="y";return c},
setSize:function(a){OpenLayers.Renderer.prototype.setSize.apply(this,arguments);for(var b=[this.rendererRoot,this.root,this.vectorRoot,this.textRoot],c=this.size.w+"px",d=this.size.h+"px",e,f=0,g=b.length;f<g;++f)e=b[f],e.style.width=c,e.style.height=d},getNodeType:function(a,b){var c=null;switch(a.CLASS_NAME){case "OpenLayers.Geometry.Point":c=b.externalGraphic?"olv:rect":this.isComplexSymbol(b.graphicName)?"olv:shape":"olv:oval";break;case "OpenLayers.Geometry.Rectangle":c="olv:rect";break;case "OpenLayers.Geometry.LineString":case "OpenLayers.Geometry.LinearRing":case "OpenLayers.Geometry.Polygon":case "OpenLayers.Geometry.Curve":c=
"olv:shape"}return c},setStyle:function(a,b,c,d){b=b||a._style;c=c||a._options;var e=b.fillColor,f=b.title||b.graphicTitle;f&&(a.title=f);if("OpenLayers.Geometry.Point"===a._geometryClass)if(b.externalGraphic){c.isFilled=!0;var e=b.graphicWidth||b.graphicHeight,f=b.graphicHeight||b.graphicWidth,e=e?e:2*b.pointRadius,f=f?f:2*b.pointRadius,g=this.getResolution(),h=void 0!=b.graphicXOffset?b.graphicXOffset:-(0.5*e),k=void 0!=b.graphicYOffset?b.graphicYOffset:-(0.5*f);a.style.left=((d.x-this.featureDx)/
g-this.offset.x+h|0)+"px";a.style.top=(d.y/g-this.offset.y-(k+f)|0)+"px";a.style.width=e+"px";a.style.height=f+"px";a.style.flip="y";e="none";c.isStroked=!1}else this.isComplexSymbol(b.graphicName)?(f=this.importSymbol(b.graphicName),a.path=f.path,a.coordorigin=f.left+","+f.bottom,f=f.size,a.coordsize=f+","+f,this.drawCircle(a,d,b.pointRadius),a.style.flip="y"):this.drawCircle(a,d,b.pointRadius);c.isFilled?a.fillcolor=e:a.filled="false";d=a.getElementsByTagName("fill");d=0==d.length?null:d[0];c.isFilled?
(d||(d=this.createNode("olv:fill",a.id+"_fill")),d.opacity=b.fillOpacity,"OpenLayers.Geometry.Point"===a._geometryClass&&b.externalGraphic&&(b.graphicOpacity&&(d.opacity=b.graphicOpacity),d.src=b.externalGraphic,d.type="frame",b.graphicWidth&&b.graphicHeight||(d.aspect="atmost")),d.parentNode!=a&&a.appendChild(d)):d&&a.removeChild(d);e=b.rotation;if(void 0!==e||void 0!==a._rotation)a._rotation=e,b.externalGraphic?(this.graphicRotate(a,h,k,b),d.opacity=0):"OpenLayers.Geometry.Point"===a._geometryClass&&
(a.style.rotation=e||0);h=a.getElementsByTagName("stroke");h=0==h.length?null:h[0];c.isStroked?(h||(h=this.createNode("olv:stroke",a.id+"_stroke"),a.appendChild(h)),h.on=!0,h.color=b.strokeColor,h.weight=b.strokeWidth+"px",h.opacity=b.strokeOpacity,h.endcap="butt"==b.strokeLinecap?"flat":b.strokeLinecap||"round",b.strokeDashstyle&&(h.dashstyle=this.dashStyle(b))):(a.stroked=!1,h&&(h.on=!1));"inherit"!=b.cursor&&null!=b.cursor&&(a.style.cursor=b.cursor);return a},graphicRotate:function(a,b,c,d){d=
d||a._style;var e=d.rotation||0,f,g;if(d.graphicWidth&&d.graphicHeight){g=Math.max(d.graphicWidth,d.graphicHeight);f=d.graphicWidth/d.graphicHeight;var h=Math.round(d.graphicWidth||g*f),k=Math.round(d.graphicHeight||g);a.style.width=h+"px";a.style.height=k+"px";var l=document.getElementById(a.id+"_image");l||(l=this.createNode("olv:imagedata",a.id+"_image"),a.appendChild(l));l.style.width=h+"px";l.style.height=k+"px";l.src=d.externalGraphic;l.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='', sizingMethod='scale')";
l=e*Math.PI/180;e=Math.sin(l);l=Math.cos(l);e="progid:DXImageTransform.Microsoft.Matrix(M11="+l+",M12="+-e+",M21="+e+",M22="+l+",SizingMethod='auto expand')\n";(l=d.graphicOpacity||d.fillOpacity)&&1!=l&&(e+="progid:DXImageTransform.Microsoft.BasicImage(opacity="+l+")\n");a.style.filter=e;e=new OpenLayers.Geometry.Point(-b,-c);h=(new OpenLayers.Bounds(0,0,h,k)).toGeometry();h.rotate(d.rotation,e);h=h.getBounds();a.style.left=Math.round(parseInt(a.style.left)+h.left)+"px";a.style.top=Math.round(parseInt(a.style.top)-
h.bottom)+"px"}else{var m=new Image;m.onreadystatechange=OpenLayers.Function.bind(function(){if("complete"==m.readyState||"interactive"==m.readyState)f=m.width/m.height,g=Math.max(2*d.pointRadius,d.graphicWidth||0,d.graphicHeight||0),b*=f,d.graphicWidth=g*f,d.graphicHeight=g,this.graphicRotate(a,b,c,d)},this);m.src=d.externalGraphic}},postDraw:function(a){a.style.visibility="visible";var b=a._style.fillColor,c=a._style.strokeColor;"none"==b&&a.fillcolor!=b&&(a.fillcolor=b);"none"==c&&a.strokecolor!=
c&&(a.strokecolor=c)},setNodeDimension:function(a,b){var c=b.getBounds();if(c){var d=this.getResolution(),c=new OpenLayers.Bounds((c.left-this.featureDx)/d-this.offset.x|0,c.bottom/d-this.offset.y|0,(c.right-this.featureDx)/d-this.offset.x|0,c.top/d-this.offset.y|0);a.style.left=c.left+"px";a.style.top=c.top+"px";a.style.width=c.getWidth()+"px";a.style.height=c.getHeight()+"px";a.coordorigin=c.left+" "+c.top;a.coordsize=c.getWidth()+" "+c.getHeight()}},dashStyle:function(a){a=a.strokeDashstyle;switch(a){case "solid":case "dot":case "dash":case "dashdot":case "longdash":case "longdashdot":return a;
default:return a=a.split(/[ ,]/),2==a.length?1*a[0]>=2*a[1]?"longdash":1==a[0]||1==a[1]?"dot":"dash":4==a.length?1*a[0]>=2*a[1]?"longdashdot":"dashdot":"solid"}},createNode:function(a,b){var c=document.createElement(a);b&&(c.id=b);c.unselectable="on";c.onselectstart=OpenLayers.Function.False;return c},nodeTypeCompare:function(a,b){var c=b,d=c.indexOf(":");-1!=d&&(c=c.substr(d+1));var e=a.nodeName,d=e.indexOf(":");-1!=d&&(e=e.substr(d+1));return c==e},createRenderRoot:function(){return this.nodeFactory(this.container.id+
"_vmlRoot","div")},createRoot:function(a){return this.nodeFactory(this.container.id+a,"olv:group")},drawPoint:function(a,b){return this.drawCircle(a,b,1)},drawCircle:function(a,b,c){if(!isNaN(b.x)&&!isNaN(b.y)){var d=this.getResolution();a.style.left=((b.x-this.featureDx)/d-this.offset.x|0)-c+"px";a.style.top=(b.y/d-this.offset.y|0)-c+"px";b=2*c;a.style.width=b+"px";a.style.height=b+"px";return a}return!1},drawLineString:function(a,b){return this.drawLine(a,b,!1)},drawLinearRing:function(a,b){return this.drawLine(a,
b,!0)},drawLine:function(a,b,c){this.setNodeDimension(a,b);for(var d=this.getResolution(),e=b.components.length,f=Array(e),g,h,k=0;k<e;k++)g=b.components[k],h=(g.x-this.featureDx)/d-this.offset.x|0,g=g.y/d-this.offset.y|0,f[k]=" "+h+","+g+" l ";b=c?" x e":" e";a.path="m"+f.join("")+b;return a},drawPolygon:function(a,b){this.setNodeDimension(a,b);var c=this.getResolution(),d=[],e,f,g,h,k,l,m,n,p,q;e=0;for(f=b.components.length;e<f;e++){d.push("m");g=b.components[e].components;h=0===e;l=k=null;m=0;
for(n=g.length;m<n;m++)p=g[m],q=(p.x-this.featureDx)/c-this.offset.x|0,p=p.y/c-this.offset.y|0,q=" "+q+","+p,d.push(q),0==m&&d.push(" l"),h||(k?k!=q&&(l?l!=q&&(h=!0):l=q):k=q);d.push(h?" x ":" ")}d.push("e");a.path=d.join("");return a},drawRectangle:function(a,b){var c=this.getResolution();a.style.left=((b.x-this.featureDx)/c-this.offset.x|0)+"px";a.style.top=(b.y/c-this.offset.y|0)+"px";a.style.width=(b.width/c|0)+"px";a.style.height=(b.height/c|0)+"px";return a},drawText:function(a,b,c){var d=this.nodeFactory(a+
this.LABEL_ID_SUFFIX,"olv:rect"),e=this.nodeFactory(a+this.LABEL_ID_SUFFIX+"_textbox","olv:textbox"),f=this.getResolution();d.style.left=((c.x-this.featureDx)/f-this.offset.x|0)+"px";d.style.top=(c.y/f-this.offset.y|0)+"px";d.style.flip="y";e.innerText=b.label;"inherit"!=b.cursor&&null!=b.cursor&&(e.style.cursor=b.cursor);b.fontColor&&(e.style.color=b.fontColor);b.fontOpacity&&(e.style.filter="alpha(opacity="+100*b.fontOpacity+")");b.fontFamily&&(e.style.fontFamily=b.fontFamily);b.fontSize&&(e.style.fontSize=
b.fontSize);b.fontWeight&&(e.style.fontWeight=b.fontWeight);b.fontStyle&&(e.style.fontStyle=b.fontStyle);!0===b.labelSelect&&(d._featureId=a,e._featureId=a,e._geometry=c,e._geometryClass=c.CLASS_NAME);e.style.whiteSpace="nowrap";e.inset="1px,0px,0px,0px";d.parentNode||(d.appendChild(e),this.textRoot.appendChild(d));b=b.labelAlign||"cm";1==b.length&&(b+="m");a=e.clientWidth*OpenLayers.Renderer.VML.LABEL_SHIFT[b.substr(0,1)];e=e.clientHeight*OpenLayers.Renderer.VML.LABEL_SHIFT[b.substr(1,1)];d.style.left=
parseInt(d.style.left)-a-1+"px";d.style.top=parseInt(d.style.top)+e+"px"},moveRoot:function(a){var b=this.map.getLayer(a.container.id);b instanceof OpenLayers.Layer.Vector.RootContainer&&(b=this.map.getLayer(this.container.id));b&&b.renderer.clear();OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this,arguments);b&&b.redraw()},importSymbol:function(a){var b=this.container.id+"-"+a,c=this.symbolCache[b];if(c)return c;c=OpenLayers.Renderer.symbol[a];if(!c)throw Error(a+" is not a valid symbol name");
a=new OpenLayers.Bounds(Number.MAX_VALUE,Number.MAX_VALUE,0,0);for(var d=["m"],e=0;e<c.length;e+=2){var f=c[e],g=c[e+1];a.left=Math.min(a.left,f);a.bottom=Math.min(a.bottom,g);a.right=Math.max(a.right,f);a.top=Math.max(a.top,g);d.push(f);d.push(g);0==e&&d.push("l")}d.push("x e");c=d.join(" ");d=(a.getWidth()-a.getHeight())/2;0<d?(a.bottom-=d,a.top+=d):(a.left+=d,a.right-=d);c={path:c,size:a.getWidth(),left:a.left,bottom:a.bottom};return this.symbolCache[b]=c},CLASS_NAME:"OpenLayers.Renderer.VML"});
OpenLayers.Renderer.VML.LABEL_SHIFT={l:0,c:0.5,r:1,t:0,m:0.5,b:1};OpenLayers.Control.CacheRead=OpenLayers.Class(OpenLayers.Control,{fetchEvent:"tileloadstart",layers:null,autoActivate:!0,setMap:function(a){OpenLayers.Control.prototype.setMap.apply(this,arguments);var b,c=this.layers||a.layers;for(b=c.length-1;0<=b;--b)this.addLayer({layer:c[b]});if(!this.layers)a.events.on({addlayer:this.addLayer,removeLayer:this.removeLayer,scope:this})},addLayer:function(a){a.layer.events.register(this.fetchEvent,this,this.fetch)},removeLayer:function(a){a.layer.events.unregister(this.fetchEvent,
this,this.fetch)},fetch:function(a){if(this.active&&window.localStorage&&a.tile instanceof OpenLayers.Tile.Image){var b=a.tile,c=b.url;!b.layer.crossOriginKeyword&&(OpenLayers.ProxyHost&&0===c.indexOf(OpenLayers.ProxyHost))&&(c=OpenLayers.Control.CacheWrite.urlMap[c]);if(c=window.localStorage.getItem("olCache_"+c))b.url=c,"tileerror"===a.type&&b.setImgSrc(c)}},destroy:function(){if(this.layers||this.map){var a,b=this.layers||this.map.layers;for(a=b.length-1;0<=a;--a)this.removeLayer({layer:b[a]})}this.map&&
this.map.events.un({addlayer:this.addLayer,removeLayer:this.removeLayer,scope:this});OpenLayers.Control.prototype.destroy.apply(this,arguments)},CLASS_NAME:"OpenLayers.Control.CacheRead"});OpenLayers.Protocol.WFS.v1_0_0=OpenLayers.Class(OpenLayers.Protocol.WFS.v1,{version:"1.0.0",CLASS_NAME:"OpenLayers.Protocol.WFS.v1_0_0"});OpenLayers.Format.WMSGetFeatureInfo=OpenLayers.Class(OpenLayers.Format.XML,{layerIdentifier:"_layer",featureIdentifier:"_feature",regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},gmlFormat:null,read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));var b=a.documentElement;if(b){var c=this["read_"+b.nodeName];a=c?c.call(this,b):(new OpenLayers.Format.GML(this.options?this.options:{})).read(a)}return a},read_msGMLOutput:function(a){var b=
[];if(a=this.getSiblingNodesByTagCriteria(a,this.layerIdentifier))for(var c=0,d=a.length;c<d;++c){var e=a[c],f=e.nodeName;e.prefix&&(f=f.split(":")[1]);f=f.replace(this.layerIdentifier,"");if(e=this.getSiblingNodesByTagCriteria(e,this.featureIdentifier))for(var g=0;g<e.length;g++){var h=e[g],k=this.parseGeometry(h),h=this.parseAttributes(h),h=new OpenLayers.Feature.Vector(k.geometry,h,null);h.bounds=k.bounds;h.type=f;b.push(h)}}return b},read_FeatureInfoResponse:function(a){var b=[];a=this.getElementsByTagNameNS(a,
"*","FIELDS");for(var c=0,d=a.length;c<d;c++){var e=a[c],f={},g,h=e.attributes.length;if(0<h)for(g=0;g<h;g++){var k=e.attributes[g];f[k.nodeName]=k.nodeValue}else for(e=e.childNodes,g=0,h=e.length;g<h;++g)k=e[g],3!=k.nodeType&&(f[k.getAttribute("name")]=k.getAttribute("value"));b.push(new OpenLayers.Feature.Vector(null,f,null))}return b},getSiblingNodesByTagCriteria:function(a,b){var c=[],d,e,f,g;if(a&&a.hasChildNodes()){d=a.childNodes;f=d.length;for(var h=0;h<f;h++){for(g=d[h];g&&1!=g.nodeType;)g=
g.nextSibling,h++;e=g?g.nodeName:"";0<e.length&&-1<e.indexOf(b)?c.push(g):(e=this.getSiblingNodesByTagCriteria(g,b),0<e.length&&(0==c.length?c=e:c.push(e)))}}return c},parseAttributes:function(a){var b={};if(1==a.nodeType){a=a.childNodes;for(var c=a.length,d=0;d<c;++d){var e=a[d];if(1==e.nodeType){var f=e.childNodes,e=e.prefix?e.nodeName.split(":")[1]:e.nodeName;0==f.length?b[e]=null:1==f.length&&(f=f[0],3==f.nodeType||4==f.nodeType)&&(f=f.nodeValue.replace(this.regExes.trimSpace,""),b[e]=f)}}}return b},
parseGeometry:function(a){this.gmlFormat||(this.gmlFormat=new OpenLayers.Format.GML);a=this.gmlFormat.parseFeature(a);var b,c=null;a&&(b=a.geometry&&a.geometry.clone(),c=a.bounds&&a.bounds.clone(),a.destroy());return{geometry:b,bounds:c}},CLASS_NAME:"OpenLayers.Format.WMSGetFeatureInfo"});OpenLayers.Control.WMTSGetFeatureInfo=OpenLayers.Class(OpenLayers.Control,{hover:!1,requestEncoding:"KVP",drillDown:!1,maxFeatures:10,clickCallback:"click",layers:null,queryVisible:!0,infoFormat:"text/html",vendorParams:{},format:null,formatOptions:null,handler:null,hoverRequest:null,pending:0,initialize:function(a){a=a||{};a.handlerOptions=a.handlerOptions||{};OpenLayers.Control.prototype.initialize.apply(this,[a]);this.format||(this.format=new OpenLayers.Format.WMSGetFeatureInfo(a.formatOptions));
!0===this.drillDown&&(this.hover=!1);this.hover?this.handler=new OpenLayers.Handler.Hover(this,{move:this.cancelHover,pause:this.getInfoForHover},OpenLayers.Util.extend(this.handlerOptions.hover||{},{delay:250})):(a={},a[this.clickCallback]=this.getInfoForClick,this.handler=new OpenLayers.Handler.Click(this,a,this.handlerOptions.click||{}))},getInfoForClick:function(a){this.request(a.xy,{})},getInfoForHover:function(a){this.request(a.xy,{hover:!0})},cancelHover:function(){this.hoverRequest&&(--this.pending,
0>=this.pending&&(OpenLayers.Element.removeClass(this.map.viewPortDiv,"olCursorWait"),this.pending=0),this.hoverRequest.abort(),this.hoverRequest=null)},findLayers:function(){for(var a=this.layers||this.map.layers,b=[],c,d=a.length-1;0<=d&&(c=a[d],!(c instanceof OpenLayers.Layer.WMTS)||(c.requestEncoding!==this.requestEncoding||this.queryVisible&&!c.getVisibility())||(b.push(c),this.drillDown&&!this.hover));--d);return b},buildRequestOptions:function(a,b){var c=this.map.getLonLatFromPixel(b),d=a.getURL(new OpenLayers.Bounds(c.lon,
c.lat,c.lon,c.lat)),d=OpenLayers.Util.getParameters(d),c=a.getTileInfo(c);OpenLayers.Util.extend(d,{service:"WMTS",version:a.version,request:"GetFeatureInfo",infoFormat:this.infoFormat,i:c.i,j:c.j});OpenLayers.Util.applyDefaults(d,this.vendorParams);return{url:OpenLayers.Util.isArray(a.url)?a.url[0]:a.url,params:OpenLayers.Util.upperCaseObject(d),callback:function(c){this.handleResponse(b,c,a)},scope:this}},request:function(a,b){b=b||{};var c=this.findLayers();if(0<c.length){for(var d,e,f=0,g=c.length;f<
g;f++)e=c[f],d=this.events.triggerEvent("beforegetfeatureinfo",{xy:a,layer:e}),!1!==d&&(++this.pending,d=this.buildRequestOptions(e,a),d=OpenLayers.Request.GET(d),!0===b.hover&&(this.hoverRequest=d));0<this.pending&&OpenLayers.Element.addClass(this.map.viewPortDiv,"olCursorWait")}},handleResponse:function(a,b,c){--this.pending;0>=this.pending&&(OpenLayers.Element.removeClass(this.map.viewPortDiv,"olCursorWait"),this.pending=0);if(b.status&&(200>b.status||300<=b.status))this.events.triggerEvent("exception",
{xy:a,request:b,layer:c});else{var d=b.responseXML;d&&d.documentElement||(d=b.responseText);var e,f;try{e=this.format.read(d)}catch(g){f=!0,this.events.triggerEvent("exception",{xy:a,request:b,error:g,layer:c})}f||this.events.triggerEvent("getfeatureinfo",{text:b.responseText,features:e,request:b,xy:a,layer:c})}},CLASS_NAME:"OpenLayers.Control.WMTSGetFeatureInfo"});OpenLayers.Protocol.CSW.v2_0_2=OpenLayers.Class(OpenLayers.Protocol,{formatOptions:null,initialize:function(a){OpenLayers.Protocol.prototype.initialize.apply(this,[a]);a.format||(this.format=new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({},this.formatOptions)))},destroy:function(){this.options&&!this.options.format&&this.format.destroy();this.format=null;OpenLayers.Protocol.prototype.destroy.apply(this)},read:function(a){a=OpenLayers.Util.extend({},a);OpenLayers.Util.applyDefaults(a,
this.options||{});var b=new OpenLayers.Protocol.Response({requestType:"read"}),c=this.format.write(a.params||a);b.priv=OpenLayers.Request.POST({url:a.url,callback:this.createCallback(this.handleRead,b,a),params:a.params,headers:a.headers,data:c});return b},handleRead:function(a,b){if(b.callback){var c=a.priv;200<=c.status&&300>c.status?(a.data=this.parseData(c),a.code=OpenLayers.Protocol.Response.SUCCESS):a.code=OpenLayers.Protocol.Response.FAILURE;b.callback.call(b.scope,a)}},parseData:function(a){var b=
a.responseXML;b&&b.documentElement||(b=a.responseText);return!b||0>=b.length?null:this.format.read(b)},CLASS_NAME:"OpenLayers.Protocol.CSW.v2_0_2"});OpenLayers.Format.WCSCapabilities.v1_1_0=OpenLayers.Class(OpenLayers.Format.WCSCapabilities.v1,{namespaces:{wcs:"http://www.opengis.net/wcs/1.1",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance",ows:"http://www.opengis.net/ows/1.1"},errorProperty:"operationsMetadata",readers:{wcs:OpenLayers.Util.applyDefaults({Capabilities:function(a,b){this.readChildNodes(a,b)},Contents:function(a,b){b.contentMetadata=[];this.readChildNodes(a,b.contentMetadata)},CoverageSummary:function(a,
b){var c={};this.readChildNodes(a,c);b.push(c)},Identifier:function(a,b){b.identifier=this.getChildValue(a)},Title:function(a,b){b.title=this.getChildValue(a)},Abstract:function(a,b){b["abstract"]=this.getChildValue(a)},SupportedCRS:function(a,b){var c=this.getChildValue(a);c&&(b.supportedCRS||(b.supportedCRS=[]),b.supportedCRS.push(c))},SupportedFormat:function(a,b){var c=this.getChildValue(a);c&&(b.supportedFormat||(b.supportedFormat=[]),b.supportedFormat.push(c))}},OpenLayers.Format.WCSCapabilities.v1.prototype.readers.wcs),
ows:OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers.ows},CLASS_NAME:"OpenLayers.Format.WCSCapabilities.v1_1_0"});OpenLayers.Control.Graticule=OpenLayers.Class(OpenLayers.Control,{autoActivate:!0,intervals:[45,30,20,10,5,2,1,0.5,0.2,0.1,0.05,0.01,0.005,0.002,0.001],displayInLayerSwitcher:!0,visible:!0,numPoints:50,targetSize:200,layerName:null,labelled:!0,labelFormat:"dm",lineSymbolizer:{strokeColor:"#333",strokeWidth:1,strokeOpacity:0.5},labelSymbolizer:{},gratLayer:null,initialize:function(a){a=a||{};a.layerName=a.layerName||OpenLayers.i18n("Graticule");OpenLayers.Control.prototype.initialize.apply(this,[a]);
this.labelSymbolizer.stroke=!1;this.labelSymbolizer.fill=!1;this.labelSymbolizer.label="${label}";this.labelSymbolizer.labelAlign="${labelAlign}";this.labelSymbolizer.labelXOffset="${xOffset}";this.labelSymbolizer.labelYOffset="${yOffset}"},destroy:function(){this.deactivate();OpenLayers.Control.prototype.destroy.apply(this,arguments);this.gratLayer&&(this.gratLayer.destroy(),this.gratLayer=null)},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);if(!this.gratLayer){var a=new OpenLayers.Style({},
{rules:[new OpenLayers.Rule({symbolizer:{Point:this.labelSymbolizer,Line:this.lineSymbolizer}})]});this.gratLayer=new OpenLayers.Layer.Vector(this.layerName,{styleMap:new OpenLayers.StyleMap({"default":a}),visibility:this.visible,displayInLayerSwitcher:this.displayInLayerSwitcher})}return this.div},activate:function(){return OpenLayers.Control.prototype.activate.apply(this,arguments)?(this.map.addLayer(this.gratLayer),this.map.events.register("moveend",this,this.update),this.update(),!0):!1},deactivate:function(){return OpenLayers.Control.prototype.deactivate.apply(this,
arguments)?(this.map.events.unregister("moveend",this,this.update),this.map.removeLayer(this.gratLayer),!0):!1},update:function(){var a=this.map.getExtent();if(a){this.gratLayer.destroyFeatures();var b=new OpenLayers.Projection("EPSG:4326"),c=this.map.getProjectionObject(),d=this.map.getResolution();c.proj&&"longlat"==c.proj.projName&&(this.numPoints=1);var e=this.map.getCenter(),f=new OpenLayers.Pixel(e.lon,e.lat);OpenLayers.Projection.transform(f,c,b);for(var e=this.targetSize*d,e=e*e,g,d=0;d<this.intervals.length;++d){g=
this.intervals[d];var h=g/2,k=f.offset({x:-h,y:-h}),h=f.offset({x:h,y:h});OpenLayers.Projection.transform(k,b,c);OpenLayers.Projection.transform(h,b,c);if((k.x-h.x)*(k.x-h.x)+(k.y-h.y)*(k.y-h.y)<=e)break}f.x=Math.floor(f.x/g)*g;f.y=Math.floor(f.y/g)*g;var d=0,e=[f.clone()],h=f.clone(),l;do h=h.offset({x:0,y:g}),l=OpenLayers.Projection.transform(h.clone(),b,c),e.unshift(h);while(a.containsPixel(l)&&1E3>++d);h=f.clone();do h=h.offset({x:0,y:-g}),l=OpenLayers.Projection.transform(h.clone(),b,c),e.push(h);
while(a.containsPixel(l)&&1E3>++d);d=0;k=[f.clone()];h=f.clone();do h=h.offset({x:-g,y:0}),l=OpenLayers.Projection.transform(h.clone(),b,c),k.unshift(h);while(a.containsPixel(l)&&1E3>++d);h=f.clone();do h=h.offset({x:g,y:0}),l=OpenLayers.Projection.transform(h.clone(),b,c),k.push(h);while(a.containsPixel(l)&&1E3>++d);g=[];for(d=0;d<k.length;++d){l=k[d].x;for(var f=[],m=null,n=Math.min(e[0].y,90),h=Math.max(e[e.length-1].y,-90),p=(n-h)/this.numPoints,n=h,h=0;h<=this.numPoints;++h){var q=new OpenLayers.Geometry.Point(l,
n);q.transform(b,c);f.push(q);n+=p;q.y>=a.bottom&&!m&&(m=q)}this.labelled&&(m=new OpenLayers.Geometry.Point(m.x,a.bottom),l={value:l,label:this.labelled?OpenLayers.Util.getFormattedLonLat(l,"lon",this.labelFormat):"",labelAlign:"cb",xOffset:0,yOffset:2},this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(m,l)));f=new OpenLayers.Geometry.LineString(f);g.push(new OpenLayers.Feature.Vector(f))}for(h=0;h<e.length;++h)if(n=e[h].y,!(-90>n||90<n)){f=[];d=k[0].x;p=(k[k.length-1].x-d)/this.numPoints;
l=d;m=null;for(d=0;d<=this.numPoints;++d)q=new OpenLayers.Geometry.Point(l,n),q.transform(b,c),f.push(q),l+=p,q.x<a.right&&(m=q);this.labelled&&(m=new OpenLayers.Geometry.Point(a.right,m.y),l={value:n,label:this.labelled?OpenLayers.Util.getFormattedLonLat(n,"lat",this.labelFormat):"",labelAlign:"rb",xOffset:-2,yOffset:2},this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(m,l)));f=new OpenLayers.Geometry.LineString(f);g.push(new OpenLayers.Feature.Vector(f))}this.gratLayer.addFeatures(g)}},CLASS_NAME:"OpenLayers.Control.Graticule"});OpenLayers.Console.warn("OpenLayers.Rico is deprecated");OpenLayers.Rico=OpenLayers.Rico||{};
OpenLayers.Rico.Corner={round:function(a,b){a=OpenLayers.Util.getElement(a);this._setOptions(b);var c=this.options.color;"fromElement"==this.options.color&&(c=this._background(a));var d=this.options.bgColor;"fromParent"==this.options.bgColor&&(d=this._background(a.offsetParent));this._roundCornersImpl(a,c,d)},changeColor:function(a,b){a.style.backgroundColor=b;for(var c=a.parentNode.getElementsByTagName("span"),d=0;d<c.length;d++)c[d].style.backgroundColor=b},changeOpacity:function(a,b){var c="alpha(opacity="+
100*b+")";a.style.opacity=b;a.style.filter=c;for(var d=a.parentNode.getElementsByTagName("span"),e=0;e<d.length;e++)d[e].style.opacity=b,d[e].style.filter=c},reRound:function(a,b){var c=a.parentNode.childNodes[2];a.parentNode.removeChild(a.parentNode.childNodes[0]);a.parentNode.removeChild(c);this.round(a.parentNode,b)},_roundCornersImpl:function(a,b,c){this.options.border&&this._renderBorder(a,c);this._isTopRounded()&&this._roundTopCorners(a,b,c);this._isBottomRounded()&&this._roundBottomCorners(a,
b,c)},_renderBorder:function(a,b){var c="1px solid "+this._borderColor(b);a.innerHTML="<div "+("style='border-left: "+c+";"+("border-right: "+c)+"'")+">"+a.innerHTML+"</div>"},_roundTopCorners:function(a,b,c){for(var d=this._createCorner(c),e=0;e<this.options.numSlices;e++)d.appendChild(this._createCornerSlice(b,c,e,"top"));a.style.paddingTop=0;a.insertBefore(d,a.firstChild)},_roundBottomCorners:function(a,b,c){for(var d=this._createCorner(c),e=this.options.numSlices-1;0<=e;e--)d.appendChild(this._createCornerSlice(b,
c,e,"bottom"));a.style.paddingBottom=0;a.appendChild(d)},_createCorner:function(a){var b=document.createElement("div");b.style.backgroundColor=this._isTransparent()?"transparent":a;return b},_createCornerSlice:function(a,b,c,d){var e=document.createElement("span"),f=e.style;f.backgroundColor=a;f.display="block";f.height="1px";f.overflow="hidden";f.fontSize="1px";a=this._borderColor(a,b);this.options.border&&0==c?(f.borderTopStyle="solid",f.borderTopWidth="1px",f.borderLeftWidth="0px",f.borderRightWidth=
"0px",f.borderBottomWidth="0px",f.height="0px",f.borderColor=a):a&&(f.borderColor=a,f.borderStyle="solid",f.borderWidth="0px 1px");this.options.compact||c!=this.options.numSlices-1||(f.height="2px");this._setMargin(e,c,d);this._setBorder(e,c,d);return e},_setOptions:function(a){this.options={corners:"all",color:"fromElement",bgColor:"fromParent",blend:!0,border:!1,compact:!1};OpenLayers.Util.extend(this.options,a||{});this.options.numSlices=this.options.compact?2:4;this._isTransparent()&&(this.options.blend=
!1)},_whichSideTop:function(){return this._hasString(this.options.corners,"all","top")||0<=this.options.corners.indexOf("tl")&&0<=this.options.corners.indexOf("tr")?"":0<=this.options.corners.indexOf("tl")?"left":0<=this.options.corners.indexOf("tr")?"right":""},_whichSideBottom:function(){return this._hasString(this.options.corners,"all","bottom")||0<=this.options.corners.indexOf("bl")&&0<=this.options.corners.indexOf("br")?"":0<=this.options.corners.indexOf("bl")?"left":0<=this.options.corners.indexOf("br")?
"right":""},_borderColor:function(a,b){return"transparent"==a?b:this.options.border?this.options.border:this.options.blend?this._blend(b,a):""},_setMargin:function(a,b,c){b=this._marginSize(b);c="top"==c?this._whichSideTop():this._whichSideBottom();"left"==c?(a.style.marginLeft=b+"px",a.style.marginRight="0px"):"right"==c?(a.style.marginRight=b+"px",a.style.marginLeft="0px"):(a.style.marginLeft=b+"px",a.style.marginRight=b+"px")},_setBorder:function(a,b,c){b=this._borderSize(b);c="top"==c?this._whichSideTop():
this._whichSideBottom();"left"==c?(a.style.borderLeftWidth=b+"px",a.style.borderRightWidth="0px"):"right"==c?(a.style.borderRightWidth=b+"px",a.style.borderLeftWidth="0px"):(a.style.borderLeftWidth=b+"px",a.style.borderRightWidth=b+"px");!1!=this.options.border&&(a.style.borderLeftWidth=b+"px",a.style.borderRightWidth=b+"px")},_marginSize:function(a){if(this._isTransparent())return 0;var b=[5,3,2,1],c=[3,2,1,0],d=[2,1],e=[1,0];return this.options.compact&&this.options.blend?e[a]:this.options.compact?
d[a]:this.options.blend?c[a]:b[a]},_borderSize:function(a){var b=[5,3,2,1],c=[2,1,1,1],d=[1,0],e=[0,2,0,0];return this.options.compact&&(this.options.blend||this._isTransparent())?1:this.options.compact?d[a]:this.options.blend?c[a]:this.options.border?e[a]:this._isTransparent()?b[a]:0},_hasString:function(a){for(var b=1;b<arguments.length;b++)if(0<=a.indexOf(arguments[b]))return!0;return!1},_blend:function(a,b){var c=OpenLayers.Rico.Color.createFromHex(a);c.blend(OpenLayers.Rico.Color.createFromHex(b));
return c},_background:function(a){try{return OpenLayers.Rico.Color.createColorFromBackground(a).asHex()}catch(b){return"#ffffff"}},_isTransparent:function(){return"transparent"==this.options.color},_isTopRounded:function(){return this._hasString(this.options.corners,"all","top","tl","tr")},_isBottomRounded:function(){return this._hasString(this.options.corners,"all","bottom","bl","br")},_hasSingleTextChild:function(a){return 1==a.childNodes.length&&3==a.childNodes[0].nodeType}};OpenLayers.Control.NavigationHistory=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOGGLE,previous:null,previousOptions:null,next:null,nextOptions:null,limit:50,autoActivate:!0,clearOnDeactivate:!1,registry:null,nextStack:null,previousStack:null,listeners:null,restoring:!1,initialize:function(a){OpenLayers.Control.prototype.initialize.apply(this,[a]);this.registry=OpenLayers.Util.extend({moveend:this.getState},this.registry);a={trigger:OpenLayers.Function.bind(this.previousTrigger,
this),displayClass:this.displayClass+" "+this.displayClass+"Previous"};OpenLayers.Util.extend(a,this.previousOptions);this.previous=new OpenLayers.Control.Button(a);a={trigger:OpenLayers.Function.bind(this.nextTrigger,this),displayClass:this.displayClass+" "+this.displayClass+"Next"};OpenLayers.Util.extend(a,this.nextOptions);this.next=new OpenLayers.Control.Button(a);this.clear()},onPreviousChange:function(a,b){a&&!this.previous.active?this.previous.activate():!a&&this.previous.active&&this.previous.deactivate()},
onNextChange:function(a,b){a&&!this.next.active?this.next.activate():!a&&this.next.active&&this.next.deactivate()},destroy:function(){OpenLayers.Control.prototype.destroy.apply(this);this.previous.destroy();this.next.destroy();this.deactivate();for(var a in this)this[a]=null},setMap:function(a){this.map=a;this.next.setMap(a);this.previous.setMap(a)},draw:function(){OpenLayers.Control.prototype.draw.apply(this,arguments);this.next.draw();this.previous.draw()},previousTrigger:function(){var a=this.previousStack.shift(),
b=this.previousStack.shift();void 0!=b?(this.nextStack.unshift(a),this.previousStack.unshift(b),this.restoring=!0,this.restore(b),this.restoring=!1,this.onNextChange(this.nextStack[0],this.nextStack.length),this.onPreviousChange(this.previousStack[1],this.previousStack.length-1)):this.previousStack.unshift(a);return b},nextTrigger:function(){var a=this.nextStack.shift();void 0!=a&&(this.previousStack.unshift(a),this.restoring=!0,this.restore(a),this.restoring=!1,this.onNextChange(this.nextStack[0],
this.nextStack.length),this.onPreviousChange(this.previousStack[1],this.previousStack.length-1));return a},clear:function(){this.previousStack=[];this.previous.deactivate();this.nextStack=[];this.next.deactivate()},getState:function(){return{center:this.map.getCenter(),resolution:this.map.getResolution(),projection:this.map.getProjectionObject(),units:this.map.getProjectionObject().getUnits()||this.map.units||this.map.baseLayer.units}},restore:function(a){var b,c;if(this.map.getProjectionObject()==
a.projection)c=this.map.getZoomForResolution(a.resolution),b=a.center;else{b=a.center.clone();b.transform(a.projection,this.map.getProjectionObject());c=a.units;var d=this.map.getProjectionObject().getUnits()||this.map.units||this.map.baseLayer.units;c=this.map.getZoomForResolution((c&&d?OpenLayers.INCHES_PER_UNIT[c]/OpenLayers.INCHES_PER_UNIT[d]:1)*a.resolution)}this.map.setCenter(b,c)},setListeners:function(){this.listeners={};for(var a in this.registry)this.listeners[a]=OpenLayers.Function.bind(function(){if(!this.restoring){var b=
this.registry[a].apply(this,arguments);this.previousStack.unshift(b);if(1<this.previousStack.length)this.onPreviousChange(this.previousStack[1],this.previousStack.length-1);this.previousStack.length>this.limit+1&&this.previousStack.pop();0<this.nextStack.length&&(this.nextStack=[],this.onNextChange(null,0))}return!0},this)},activate:function(){var a=!1;if(this.map&&OpenLayers.Control.prototype.activate.apply(this)){null==this.listeners&&this.setListeners();for(var b in this.listeners)this.map.events.register(b,
this,this.listeners[b]);a=!0;0==this.previousStack.length&&this.initStack()}return a},initStack:function(){this.map.getCenter()&&this.listeners.moveend()},deactivate:function(){var a=!1;if(this.map&&OpenLayers.Control.prototype.deactivate.apply(this)){for(var b in this.listeners)this.map.events.unregister(b,this,this.listeners[b]);this.clearOnDeactivate&&this.clear();a=!0}return a},CLASS_NAME:"OpenLayers.Control.NavigationHistory"});OpenLayers.Layer.UTFGrid=OpenLayers.Class(OpenLayers.Layer.XYZ,{isBaseLayer:!1,projection:new OpenLayers.Projection("EPSG:900913"),useJSONP:!1,tileClass:OpenLayers.Tile.UTFGrid,initialize:function(a){OpenLayers.Layer.Grid.prototype.initialize.apply(this,[a.name,a.url,{},a]);this.tileOptions=OpenLayers.Util.extend({utfgridResolution:this.utfgridResolution},this.tileOptions)},createBackBuffer:function(){},clone:function(a){null==a&&(a=new OpenLayers.Layer.UTFGrid(this.getOptions()));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,
[a])},getFeatureInfo:function(a){var b=null;(a=this.getTileData(a))&&a.tile&&(b=a.tile.getFeatureInfo(a.i,a.j));return b},getFeatureId:function(a){var b=null;a=this.getTileData(a);a.tile&&(b=a.tile.getFeatureId(a.i,a.j));return b},CLASS_NAME:"OpenLayers.Layer.UTFGrid"});OpenLayers.TileManager=OpenLayers.Class({cacheSize:256,tilesPerFrame:2,frameDelay:16,moveDelay:100,zoomDelay:200,maps:null,tileQueueId:null,tileQueue:null,tileCache:null,tileCacheIndex:null,initialize:function(a){OpenLayers.Util.extend(this,a);this.maps=[];this.tileQueueId={};this.tileQueue={};this.tileCache={};this.tileCacheIndex=[]},addMap:function(a){if(!this._destroyed&&OpenLayers.Layer.Grid){this.maps.push(a);this.tileQueue[a.id]=[];for(var b=0,c=a.layers.length;b<c;++b)this.addLayer({layer:a.layers[b]});
a.events.on({move:this.move,zoomend:this.zoomEnd,changelayer:this.changeLayer,addlayer:this.addLayer,preremovelayer:this.removeLayer,scope:this})}},removeMap:function(a){if(!this._destroyed&&OpenLayers.Layer.Grid){window.clearTimeout(this.tileQueueId[a.id]);if(a.layers)for(var b=0,c=a.layers.length;b<c;++b)this.removeLayer({layer:a.layers[b]});a.events&&a.events.un({move:this.move,zoomend:this.zoomEnd,changelayer:this.changeLayer,addlayer:this.addLayer,preremovelayer:this.removeLayer,scope:this});
delete this.tileQueue[a.id];delete this.tileQueueId[a.id];OpenLayers.Util.removeItem(this.maps,a)}},move:function(a){this.updateTimeout(a.object,this.moveDelay,!0)},zoomEnd:function(a){this.updateTimeout(a.object,this.zoomDelay)},changeLayer:function(a){"visibility"!==a.property&&"params"!==a.property||this.updateTimeout(a.object,0)},addLayer:function(a){a=a.layer;if(a instanceof OpenLayers.Layer.Grid){a.events.on({addtile:this.addTile,retile:this.clearTileQueue,scope:this});var b,c,d;for(b=a.grid.length-
1;0<=b;--b)for(c=a.grid[b].length-1;0<=c;--c)d=a.grid[b][c],this.addTile({tile:d}),d.url&&this.manageTileCache({object:d})}},removeLayer:function(a){a=a.layer;if(a instanceof OpenLayers.Layer.Grid&&(this.clearTileQueue({object:a}),a.events&&a.events.un({addtile:this.addTile,retile:this.clearTileQueue,scope:this}),a.grid)){var b,c,d;for(b=a.grid.length-1;0<=b;--b)for(c=a.grid[b].length-1;0<=c;--c)d=a.grid[b][c],this.unloadTile({object:d}),d.url&&this.manageTileCache({object:d})}},updateTimeout:function(a,
b,c){window.clearTimeout(this.tileQueueId[a.id]);var d=this.tileQueue[a.id];if(!c||d.length)this.tileQueueId[a.id]=window.setTimeout(OpenLayers.Function.bind(function(){this.drawTilesFromQueue(a);d.length&&this.updateTimeout(a,this.frameDelay)},this),b)},addTile:function(a){a.tile.events.on({beforedraw:this.queueTileDraw,beforeload:this.manageTileCache,loadend:this.addToCache,unload:this.unloadTile,scope:this})},unloadTile:function(a){a=a.object;a.events.un({beforedraw:this.queueTileDraw,beforeload:this.manageTileCache,
loadend:this.addToCache,unload:this.unloadTile,scope:this});OpenLayers.Util.removeItem(this.tileQueue[a.layer.map.id],a)},queueTileDraw:function(a){a=a.object;var b=!1,c=a.layer,d=c.getURL(a.bounds),e=this.tileCache[d];e&&"olTileImage"!==e.className&&(delete this.tileCache[d],OpenLayers.Util.removeItem(this.tileCacheIndex,d),e=null);!c.url||!c.async&&e||(b=this.tileQueue[c.map.id],~OpenLayers.Util.indexOf(b,a)||b.push(a),b=!0);return!b},drawTilesFromQueue:function(a){var b=this.tileQueue[a.id],c=
this.tilesPerFrame;for(a=a.zoomTween&&a.zoomTween.playing;!a&&b.length&&c;)b.shift().draw(!0),--c},manageTileCache:function(a){a=a.object;var b=this.tileCache[a.url];if(b&&(!b.parentNode||OpenLayers.Element.hasClass(b.parentNode,"olBackBuffer"))){if(a.layer.backBuffer){if(a.layer.backBuffer===b.parentNode)return;b.style.opacity=0;b.style.visibility="hidden"}b.id=null;a.setImage(b);OpenLayers.Util.removeItem(this.tileCacheIndex,a.url);this.tileCacheIndex.push(a.url)}},addToCache:function(a){a=a.object;
this.tileCache[a.url]||OpenLayers.Element.hasClass(a.imgDiv,"olImageLoadError")||(this.tileCacheIndex.length>=this.cacheSize&&(delete this.tileCache[this.tileCacheIndex[0]],this.tileCacheIndex.shift()),this.tileCache[a.url]=a.imgDiv,this.tileCacheIndex.push(a.url))},clearTileQueue:function(a){a=a.object;for(var b=this.tileQueue[a.map.id],c=b.length-1;0<=c;--c)b[c].layer===a&&b.splice(c,1)},destroy:function(){for(var a=this.maps.length-1;0<=a;--a)this.removeMap(this.maps[a]);this.tileCacheIndex=this.tileCache=
this.tileQueueId=this.tileQueue=this.maps=null;this._destroyed=!0}});OpenLayers.Layer.ArcGISCache=OpenLayers.Class(OpenLayers.Layer.XYZ,{url:null,tileOrigin:null,tileSize:new OpenLayers.Size(256,256),useArcGISServer:!0,type:"png",useScales:!1,overrideDPI:!1,initialize:function(a,b,c){OpenLayers.Layer.XYZ.prototype.initialize.apply(this,arguments);this.resolutions&&(this.serverResolutions=this.resolutions,this.maxExtent=this.getMaxExtentForResolution(this.resolutions[0]));if(this.layerInfo){var d=this.layerInfo,e=new OpenLayers.Bounds(d.fullExtent.xmin,d.fullExtent.ymin,
d.fullExtent.xmax,d.fullExtent.ymax);this.projection="EPSG:"+d.spatialReference.wkid;this.sphericalMercator=102100==d.spatialReference.wkid;this.units="esriFeet"==d.units?"ft":"m";if(d.tileInfo){this.tileSize=new OpenLayers.Size(d.tileInfo.width||d.tileInfo.cols,d.tileInfo.height||d.tileInfo.rows);this.tileOrigin=new OpenLayers.LonLat(d.tileInfo.origin.x,d.tileInfo.origin.y);var f=new OpenLayers.Geometry.Point(e.left,e.top),e=new OpenLayers.Geometry.Point(e.right,e.bottom);this.useScales?this.scales=
[]:this.resolutions=[];this.lods=[];for(var g in d.tileInfo.lods)if(d.tileInfo.lods.hasOwnProperty(g)){var h=d.tileInfo.lods[g];this.useScales?this.scales.push(h.scale):this.resolutions.push(h.resolution);var k=this.getContainingTileCoords(f,h.resolution);h.startTileCol=k.x;h.startTileRow=k.y;k=this.getContainingTileCoords(e,h.resolution);h.endTileCol=k.x;h.endTileRow=k.y;this.lods.push(h)}this.maxExtent=this.calculateMaxExtentWithLOD(this.lods[0]);this.serverResolutions=this.resolutions;this.overrideDPI&&
d.tileInfo.dpi&&(OpenLayers.DOTS_PER_INCH=d.tileInfo.dpi)}}},getContainingTileCoords:function(a,b){return new OpenLayers.Pixel(Math.max(Math.floor((a.x-this.tileOrigin.lon)/(this.tileSize.w*b)),0),Math.max(Math.floor((this.tileOrigin.lat-a.y)/(this.tileSize.h*b)),0))},calculateMaxExtentWithLOD:function(a){var b=this.tileOrigin.lon+a.startTileCol*this.tileSize.w*a.resolution,c=this.tileOrigin.lat-a.startTileRow*this.tileSize.h*a.resolution;return new OpenLayers.Bounds(b,c-(a.endTileRow-a.startTileRow+
1)*this.tileSize.h*a.resolution,b+(a.endTileCol-a.startTileCol+1)*this.tileSize.w*a.resolution,c)},calculateMaxExtentWithExtent:function(a,b){var c=new OpenLayers.Geometry.Point(a.left,a.top),d=new OpenLayers.Geometry.Point(a.right,a.bottom),c=this.getContainingTileCoords(c,b),d=this.getContainingTileCoords(d,b);return this.calculateMaxExtentWithLOD({resolution:b,startTileCol:c.x,startTileRow:c.y,endTileCol:d.x,endTileRow:d.y})},getUpperLeftTileCoord:function(a){var b=new OpenLayers.Geometry.Point(this.maxExtent.left,
this.maxExtent.top);return this.getContainingTileCoords(b,a)},getLowerRightTileCoord:function(a){var b=new OpenLayers.Geometry.Point(this.maxExtent.right,this.maxExtent.bottom);return this.getContainingTileCoords(b,a)},getMaxExtentForResolution:function(a){var b=this.getUpperLeftTileCoord(a),c=this.getLowerRightTileCoord(a),d=this.tileOrigin.lon+b.x*this.tileSize.w*a,e=this.tileOrigin.lat-b.y*this.tileSize.h*a;return new OpenLayers.Bounds(d,e-(c.y-b.y+1)*this.tileSize.h*a,d+(c.x-b.x+1)*this.tileSize.w*
a,e)},clone:function(a){null==a&&(a=new OpenLayers.Layer.ArcGISCache(this.name,this.url,this.options));return OpenLayers.Layer.XYZ.prototype.clone.apply(this,[a])},initGriddedTiles:function(a){delete this._tileOrigin;OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this,arguments)},getMaxExtent:function(){var a=this.map.getResolution();return this.maxExtent=this.getMaxExtentForResolution(a)},getTileOrigin:function(){if(!this._tileOrigin){var a=this.getMaxExtent();this._tileOrigin=new OpenLayers.LonLat(a.left,
a.bottom)}return this._tileOrigin},getURL:function(a){var b=this.getResolution(),c=this.tileOrigin.lon+b*this.tileSize.w/2,d=this.tileOrigin.lat-b*this.tileSize.h/2;a=a.getCenterLonLat();c=Math.round(Math.abs((a.lon-c)/(b*this.tileSize.w)));d=Math.round(Math.abs((d-a.lat)/(b*this.tileSize.h)));a=this.map.getZoom();if(this.lods){if(b=this.lods[this.map.getZoom()],c<b.startTileCol||c>b.endTileCol||d<b.startTileRow||d>b.endTileRow)return null}else{var e=this.getUpperLeftTileCoord(b),b=this.getLowerRightTileCoord(b);
if(c<e.x||c>=b.x||d<e.y||d>=b.y)return null}b=this.url;e=""+c+d+a;OpenLayers.Util.isArray(b)&&(b=this.selectUrl(e,b));this.useArcGISServer?b+="/tile/${z}/${y}/${x}":(c="C"+OpenLayers.Number.zeroPad(c,8,16),d="R"+OpenLayers.Number.zeroPad(d,8,16),a="L"+OpenLayers.Number.zeroPad(a,2,10),b=b+"/${z}/${y}/${x}."+this.type);b=OpenLayers.String.format(b,{x:c,y:d,z:a});return OpenLayers.Util.urlAppend(b,OpenLayers.Util.getParameterString(this.params))},CLASS_NAME:"OpenLayers.Layer.ArcGISCache"});OpenLayers.Control.WMSGetFeatureInfo=OpenLayers.Class(OpenLayers.Control,{hover:!1,drillDown:!1,maxFeatures:10,clickCallback:"click",output:"features",layers:null,queryVisible:!1,url:null,layerUrls:null,infoFormat:"text/html",vendorParams:{},format:null,formatOptions:null,handler:null,hoverRequest:null,initialize:function(a){a=a||{};a.handlerOptions=a.handlerOptions||{};OpenLayers.Control.prototype.initialize.apply(this,[a]);this.format||(this.format=new OpenLayers.Format.WMSGetFeatureInfo(a.formatOptions));
!0===this.drillDown&&(this.hover=!1);this.hover?this.handler=new OpenLayers.Handler.Hover(this,{move:this.cancelHover,pause:this.getInfoForHover},OpenLayers.Util.extend(this.handlerOptions.hover||{},{delay:250})):(a={},a[this.clickCallback]=this.getInfoForClick,this.handler=new OpenLayers.Handler.Click(this,a,this.handlerOptions.click||{}))},getInfoForClick:function(a){this.events.triggerEvent("beforegetfeatureinfo",{xy:a.xy});OpenLayers.Element.addClass(this.map.viewPortDiv,"olCursorWait");this.request(a.xy,
{})},getInfoForHover:function(a){this.events.triggerEvent("beforegetfeatureinfo",{xy:a.xy});this.request(a.xy,{hover:!0})},cancelHover:function(){this.hoverRequest&&(this.hoverRequest.abort(),this.hoverRequest=null)},findLayers:function(){for(var a=this.layers||this.map.layers,b=[],c,d,e=a.length-1;0<=e;--e)c=a[e],c instanceof OpenLayers.Layer.WMS&&(!this.queryVisible||c.getVisibility())&&(d=OpenLayers.Util.isArray(c.url)?c.url[0]:c.url,!1!==this.drillDown||this.url||(this.url=d),(!0===this.drillDown||
this.urlMatches(d))&&b.push(c));return b},urlMatches:function(a){var b=OpenLayers.Util.isEquivalentUrl(this.url,a);if(!b&&this.layerUrls)for(var c=0,d=this.layerUrls.length;c<d;++c)if(OpenLayers.Util.isEquivalentUrl(this.layerUrls[c],a)){b=!0;break}return b},buildWMSOptions:function(a,b,c,d){for(var e=[],f=[],g=0,h=b.length;g<h;g++)null!=b[g].params.LAYERS&&(e=e.concat(b[g].params.LAYERS),f=f.concat(this.getStyleNames(b[g])));b=b[0];g=this.map.getProjection();(h=b.projection)&&h.equals(this.map.getProjectionObject())&&
(g=h.getCode());d=OpenLayers.Util.extend({service:"WMS",version:b.params.VERSION,request:"GetFeatureInfo",exceptions:b.params.EXCEPTIONS,bbox:this.map.getExtent().toBBOX(null,b.reverseAxisOrder()),feature_count:this.maxFeatures,height:this.map.getSize().h,width:this.map.getSize().w,format:d,info_format:b.params.INFO_FORMAT||this.infoFormat},1.3<=parseFloat(b.params.VERSION)?{crs:g,i:parseInt(c.x),j:parseInt(c.y)}:{srs:g,x:parseInt(c.x),y:parseInt(c.y)});0!=e.length&&(d=OpenLayers.Util.extend({layers:e,
query_layers:e,styles:f},d));OpenLayers.Util.applyDefaults(d,this.vendorParams);return{url:a,params:OpenLayers.Util.upperCaseObject(d),callback:function(b){this.handleResponse(c,b,a)},scope:this}},getStyleNames:function(a){return a.params.STYLES?a.params.STYLES:OpenLayers.Util.isArray(a.params.LAYERS)?Array(a.params.LAYERS.length):a.params.LAYERS.replace(/[^,]/g,"")},request:function(a,b){var c=this.findLayers();if(0==c.length)this.events.triggerEvent("nogetfeatureinfo"),OpenLayers.Element.removeClass(this.map.viewPortDiv,
"olCursorWait");else if(b=b||{},!1===this.drillDown){var c=this.buildWMSOptions(this.url,c,a,c[0].params.FORMAT),d=OpenLayers.Request.GET(c);!0===b.hover&&(this.hoverRequest=d)}else{this._numRequests=this._requestCount=0;this.features=[];for(var d={},e,f=0,g=c.length;f<g;f++){var h=c[f];e=OpenLayers.Util.isArray(h.url)?h.url[0]:h.url;e in d?d[e].push(h):(this._numRequests++,d[e]=[h])}for(e in d)c=d[e],c=this.buildWMSOptions(e,c,a,c[0].params.FORMAT),OpenLayers.Request.GET(c)}},triggerGetFeatureInfo:function(a,
b,c){this.events.triggerEvent("getfeatureinfo",{text:a.responseText,features:c,request:a,xy:b});OpenLayers.Element.removeClass(this.map.viewPortDiv,"olCursorWait")},handleResponse:function(a,b,c){var d=b.responseXML;d&&d.documentElement||(d=b.responseText);d=this.format.read(d);!1===this.drillDown?this.triggerGetFeatureInfo(b,a,d):(this._requestCount++,this._features="object"===this.output?(this._features||[]).concat({url:c,features:d}):(this._features||[]).concat(d),this._requestCount===this._numRequests&&
(this.triggerGetFeatureInfo(b,a,this._features.concat()),delete this._features,delete this._requestCount,delete this._numRequests))},CLASS_NAME:"OpenLayers.Control.WMSGetFeatureInfo"});OpenLayers.Format.WMSCapabilities.v1_3_0=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_3,{version:"1.3.0",CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_3_0"});OpenLayers.Format.SOSGetFeatureOfInterest=OpenLayers.Class(OpenLayers.Format.XML,{VERSION:"1.0.0",namespaces:{sos:"http://www.opengis.net/sos/1.0",gml:"http://www.opengis.net/gml",sa:"http://www.opengis.net/sampling/1.0",xsi:"http://www.w3.org/2001/XMLSchema-instance"},schemaLocation:"http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd",defaultPrefix:"sos",regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},read:function(a){"string"==
typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={features:[]};this.readNode(a,b);a=[];for(var c=0,d=b.features.length;c<d;c++){var e=b.features[c];this.internalProjection&&(this.externalProjection&&e.components[0])&&e.components[0].transform(this.externalProjection,this.internalProjection);e=new OpenLayers.Feature.Vector(e.components[0],e.attributes);a.push(e)}return a},readers:{sa:{SamplingPoint:function(a,b){if(!b.attributes){var c=
{attributes:{}};b.features.push(c);b=c}b.attributes.id=this.getAttributeNS(a,this.namespaces.gml,"id");this.readChildNodes(a,b)},position:function(a,b){this.readChildNodes(a,b)}},gml:OpenLayers.Util.applyDefaults({FeatureCollection:function(a,b){this.readChildNodes(a,b)},featureMember:function(a,b){var c={attributes:{}};b.features.push(c);this.readChildNodes(a,c)},name:function(a,b){b.attributes.name=this.getChildValue(a)},pos:function(a,b){this.externalProjection||(this.externalProjection=new OpenLayers.Projection(a.getAttribute("srsName")));
OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply(this,[a,b])}},OpenLayers.Format.GML.v3.prototype.readers.gml)},writers:{sos:{GetFeatureOfInterest:function(a){for(var b=this.createElementNSPlus("GetFeatureOfInterest",{attributes:{version:this.VERSION,service:"SOS","xsi:schemaLocation":this.schemaLocation}}),c=0,d=a.fois.length;c<d;c++)this.writeNode("FeatureOfInterestId",{foi:a.fois[c]},b);return b},FeatureOfInterestId:function(a){return this.createElementNSPlus("FeatureOfInterestId",{value:a.foi})}}},
CLASS_NAME:"OpenLayers.Format.SOSGetFeatureOfInterest"});OpenLayers.Format.SOSGetObservation=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{ows:"http://www.opengis.net/ows",gml:"http://www.opengis.net/gml",sos:"http://www.opengis.net/sos/1.0",ogc:"http://www.opengis.net/ogc",om:"http://www.opengis.net/om/1.0",sa:"http://www.opengis.net/sampling/1.0",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},regExes:{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g},VERSION:"1.0.0",schemaLocation:"http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd",
defaultPrefix:"sos",read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={measurements:[],observations:[]};this.readNode(a,b);return b},write:function(a){a=this.writeNode("sos:GetObservation",a);a.setAttribute("xmlns:om",this.namespaces.om);a.setAttribute("xmlns:ogc",this.namespaces.ogc);this.setAttributeNS(a,this.namespaces.xsi,"xsi:schemaLocation",this.schemaLocation);return OpenLayers.Format.XML.prototype.write.apply(this,
[a])},readers:{om:{ObservationCollection:function(a,b){b.id=this.getAttributeNS(a,this.namespaces.gml,"id");this.readChildNodes(a,b)},member:function(a,b){this.readChildNodes(a,b)},Measurement:function(a,b){var c={};b.measurements.push(c);this.readChildNodes(a,c)},Observation:function(a,b){var c={};b.observations.push(c);this.readChildNodes(a,c)},samplingTime:function(a,b){var c={};b.samplingTime=c;this.readChildNodes(a,c)},observedProperty:function(a,b){b.observedProperty=this.getAttributeNS(a,this.namespaces.xlink,
"href");this.readChildNodes(a,b)},procedure:function(a,b){b.procedure=this.getAttributeNS(a,this.namespaces.xlink,"href");this.readChildNodes(a,b)},featureOfInterest:function(a,b){var c={features:[]};b.fois=[];b.fois.push(c);this.readChildNodes(a,c);for(var d=[],e=0,f=c.features.length;e<f;e++){var g=c.features[e];d.push(new OpenLayers.Feature.Vector(g.components[0],g.attributes))}c.features=d},result:function(a,b){var c={};b.result=c;""!==this.getChildValue(a)?(c.value=this.getChildValue(a),c.uom=
a.getAttribute("uom")):this.readChildNodes(a,c)}},sa:OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.sa,gml:OpenLayers.Util.applyDefaults({TimeInstant:function(a,b){var c={};b.timeInstant=c;this.readChildNodes(a,c)},timePosition:function(a,b){b.timePosition=this.getChildValue(a)}},OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.gml)},writers:{sos:{GetObservation:function(a){var b=this.createElementNSPlus("GetObservation",{attributes:{version:this.VERSION,service:"SOS"}});this.writeNode("offering",
a,b);a.eventTime&&this.writeNode("eventTime",a,b);for(var c in a.procedures)this.writeNode("procedure",a.procedures[c],b);for(var d in a.observedProperties)this.writeNode("observedProperty",a.observedProperties[d],b);a.foi&&this.writeNode("featureOfInterest",a.foi,b);this.writeNode("responseFormat",a,b);a.resultModel&&this.writeNode("resultModel",a,b);a.responseMode&&this.writeNode("responseMode",a,b);return b},featureOfInterest:function(a){var b=this.createElementNSPlus("featureOfInterest");this.writeNode("ObjectID",
a.objectId,b);return b},ObjectID:function(a){return this.createElementNSPlus("ObjectID",{value:a})},responseFormat:function(a){return this.createElementNSPlus("responseFormat",{value:a.responseFormat})},procedure:function(a){return this.createElementNSPlus("procedure",{value:a})},offering:function(a){return this.createElementNSPlus("offering",{value:a.offering})},observedProperty:function(a){return this.createElementNSPlus("observedProperty",{value:a})},eventTime:function(a){var b=this.createElementNSPlus("eventTime");
"latest"===a.eventTime&&this.writeNode("ogc:TM_Equals",a,b);return b},resultModel:function(a){return this.createElementNSPlus("resultModel",{value:a.resultModel})},responseMode:function(a){return this.createElementNSPlus("responseMode",{value:a.responseMode})}},ogc:{TM_Equals:function(a){var b=this.createElementNSPlus("ogc:TM_Equals");this.writeNode("ogc:PropertyName",{property:"urn:ogc:data:time:iso8601"},b);"latest"===a.eventTime&&this.writeNode("gml:TimeInstant",{value:"latest"},b);return b},PropertyName:function(a){return this.createElementNSPlus("ogc:PropertyName",
{value:a.property})}},gml:{TimeInstant:function(a){var b=this.createElementNSPlus("gml:TimeInstant");this.writeNode("gml:timePosition",a,b);return b},timePosition:function(a){return this.createElementNSPlus("gml:timePosition",{value:a.value})}}},CLASS_NAME:"OpenLayers.Format.SOSGetObservation"});OpenLayers.Control.UTFGrid=OpenLayers.Class(OpenLayers.Control,{autoActivate:!0,layers:null,defaultHandlerOptions:{delay:300,pixelTolerance:4,stopMove:!1,single:!0,"double":!1,stopSingle:!1,stopDouble:!1},handlerMode:"click",setHandler:function(a){this.handlerMode=a;this.resetHandler()},resetHandler:function(){this.handler&&(this.handler.deactivate(),this.handler.destroy(),this.handler=null);"hover"==this.handlerMode?this.handler=new OpenLayers.Handler.Hover(this,{pause:this.handleEvent,move:this.reset},
this.handlerOptions):"click"==this.handlerMode?this.handler=new OpenLayers.Handler.Click(this,{click:this.handleEvent},this.handlerOptions):"move"==this.handlerMode&&(this.handler=new OpenLayers.Handler.Hover(this,{pause:this.handleEvent,move:this.handleEvent},this.handlerOptions));return this.handler?!0:!1},initialize:function(a){a=a||{};a.handlerOptions=a.handlerOptions||this.defaultHandlerOptions;OpenLayers.Control.prototype.initialize.apply(this,[a]);this.resetHandler()},handleEvent:function(a){if(null==
a)this.reset();else{var b=this.map.getLonLatFromPixel(a.xy);if(b){var c=this.findLayers();if(0<c.length){for(var d={},e,f,g=0,h=c.length;g<h;g++)e=c[g],f=OpenLayers.Util.indexOf(this.map.layers,e),d[f]=e.getFeatureInfo(b);this.callback(d,b,a.xy)}}}},callback:function(a){},reset:function(a){this.callback(null)},findLayers:function(){for(var a=this.layers||this.map.layers,b=[],c,d=a.length-1;0<=d;--d)c=a[d],c instanceof OpenLayers.Layer.UTFGrid&&b.push(c);return b},CLASS_NAME:"OpenLayers.Control.UTFGrid"});OpenLayers.Format.CQL=function(){function a(a){function b(){var a=e.pop();switch(a.type){case "LOGICAL":var c=b(),g=b();return new OpenLayers.Filter.Logical({filters:[g,c],type:f[a.text.toUpperCase()]});case "NOT":return a=b(),new OpenLayers.Filter.Logical({filters:[a],type:OpenLayers.Filter.Logical.NOT});case "BETWEEN":return e.pop(),g=b(),a=b(),c=b(),new OpenLayers.Filter.Comparison({property:c,lowerBoundary:a,upperBoundary:g,type:OpenLayers.Filter.Comparison.BETWEEN});case "COMPARISON":return g=
b(),c=b(),new OpenLayers.Filter.Comparison({property:c,value:g,type:d[a.text.toUpperCase()]});case "IS_NULL":return c=b(),new OpenLayers.Filter.Comparison({property:c,type:d[a.text.toUpperCase()]});case "VALUE":return(c=a.text.match(/^'(.*)'$/))?c[1].replace(/''/g,"'"):Number(a.text);case "SPATIAL":switch(a.text.toUpperCase()){case "BBOX":var a=b(),c=b(),g=b(),h=b(),k=b();return new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.BBOX,property:k,value:OpenLayers.Bounds.fromArray([h,g,c,
a])});case "INTERSECTS":return g=b(),c=b(),new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.INTERSECTS,property:c,value:g});case "WITHIN":return g=b(),c=b(),new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.WITHIN,property:c,value:g});case "CONTAINS":return g=b(),c=b(),new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.CONTAINS,property:c,value:g});case "DWITHIN":return a=b(),g=b(),c=b(),new OpenLayers.Filter.Spatial({type:OpenLayers.Filter.Spatial.DWITHIN,value:g,
property:c,distance:Number(a)})}case "GEOMETRY":return OpenLayers.Geometry.fromWKT(a.text);default:return a.text}}for(var c=[],e=[];a.length;){var g=a.shift();switch(g.type){case "PROPERTY":case "GEOMETRY":case "VALUE":e.push(g);break;case "COMPARISON":case "BETWEEN":case "IS_NULL":case "LOGICAL":for(var k=h[g.type];0<c.length&&h[c[c.length-1].type]<=k;)e.push(c.pop());c.push(g);break;case "SPATIAL":case "NOT":case "LPAREN":c.push(g);break;case "RPAREN":for(;0<c.length&&"LPAREN"!=c[c.length-1].type;)e.push(c.pop());
c.pop();0<c.length&&"SPATIAL"==c[c.length-1].type&&e.push(c.pop());case "COMMA":case "END":break;default:throw Error("Unknown token type "+g.type);}}for(;0<c.length;)e.push(c.pop());a=b();if(0<e.length){a="Remaining tokens after building AST: \n";for(c=e.length-1;0<=c;c--)a+=e[c].type+": "+e[c].text+"\n";throw Error(a);}return a}var b={PROPERTY:/^[_a-zA-Z]\w*/,COMPARISON:/^(=|<>|<=|<|>=|>|LIKE)/i,IS_NULL:/^IS NULL/i,COMMA:/^,/,LOGICAL:/^(AND|OR)/i,VALUE:/^('([^']|'')*'|\d+(\.\d*)?|\.\d+)/,LPAREN:/^\(/,
RPAREN:/^\)/,SPATIAL:/^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i,NOT:/^NOT/i,BETWEEN:/^BETWEEN/i,GEOMETRY:function(a){var b=/^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(a);if(b){var c=a.length,b=a.indexOf("(",b[0].length);if(-1<b)for(var d=1;b<c&&0<d;)switch(b++,a.charAt(b)){case "(":d++;break;case ")":d--}return[a.substr(0,b+1)]}},END:/^$/},c={LPAREN:["GEOMETRY","SPATIAL","PROPERTY","VALUE","LPAREN"],RPAREN:["NOT","LOGICAL","END","RPAREN"],PROPERTY:["COMPARISON",
"BETWEEN","COMMA","IS_NULL"],BETWEEN:["VALUE"],IS_NULL:["END"],COMPARISON:["VALUE"],COMMA:["GEOMETRY","VALUE","PROPERTY"],VALUE:["LOGICAL","COMMA","RPAREN","END"],SPATIAL:["LPAREN"],LOGICAL:["NOT","VALUE","SPATIAL","PROPERTY","LPAREN"],NOT:["PROPERTY","LPAREN"],GEOMETRY:["COMMA","RPAREN"]},d={"=":OpenLayers.Filter.Comparison.EQUAL_TO,"<>":OpenLayers.Filter.Comparison.NOT_EQUAL_TO,"<":OpenLayers.Filter.Comparison.LESS_THAN,"<=":OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,">":OpenLayers.Filter.Comparison.GREATER_THAN,
">=":OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,LIKE:OpenLayers.Filter.Comparison.LIKE,BETWEEN:OpenLayers.Filter.Comparison.BETWEEN,"IS NULL":OpenLayers.Filter.Comparison.IS_NULL},e={},f={AND:OpenLayers.Filter.Logical.AND,OR:OpenLayers.Filter.Logical.OR},g={},h={RPAREN:3,LOGICAL:2,COMPARISON:1},k;for(k in d)d.hasOwnProperty(k)&&(e[d[k]]=k);for(k in f)f.hasOwnProperty(k)&&(g[f[k]]=k);return OpenLayers.Class(OpenLayers.Format,{read:function(d){var e=d;d=[];var f,g=["NOT","GEOMETRY","SPATIAL",
"PROPERTY","LPAREN"];do{a:{f=g;for(var h=void 0,g=void 0,k=f.length,h=0;h<k;h++){var g=f[h],s=b[g]instanceof RegExp?b[g].exec(e):(0,b[g])(e);if(s){f=s[0];e=e.substr(f.length).replace(/^\s*/,"");f={type:g,text:f,remainder:e};break a}}d="ERROR: In parsing: ["+e+"], expected one of: ";for(h=0;h<k;h++)g=f[h],d+="\n    "+g+": "+b[g];throw Error(d);}e=f.remainder;g=c[f.type];if("END"!=f.type&&!g)throw Error("No follows list for "+f.type);d.push(f)}while("END"!=f.type);d=a(d);this.keepData&&(this.data=d);
return d},write:function(a){if(a instanceof OpenLayers.Geometry)return a.toString();switch(a.CLASS_NAME){case "OpenLayers.Filter.Spatial":switch(a.type){case OpenLayers.Filter.Spatial.BBOX:return"BBOX("+a.property+","+a.value.toBBOX()+")";case OpenLayers.Filter.Spatial.DWITHIN:return"DWITHIN("+a.property+", "+this.write(a.value)+", "+a.distance+")";case OpenLayers.Filter.Spatial.WITHIN:return"WITHIN("+a.property+", "+this.write(a.value)+")";case OpenLayers.Filter.Spatial.INTERSECTS:return"INTERSECTS("+
a.property+", "+this.write(a.value)+")";case OpenLayers.Filter.Spatial.CONTAINS:return"CONTAINS("+a.property+", "+this.write(a.value)+")";default:throw Error("Unknown spatial filter type: "+a.type);}case "OpenLayers.Filter.Logical":if(a.type==OpenLayers.Filter.Logical.NOT)return"NOT ("+this.write(a.filters[0])+")";for(var b="(",c=!0,d=0;d<a.filters.length;d++)c?c=!1:b+=") "+g[a.type]+" (",b+=this.write(a.filters[d]);return b+")";case "OpenLayers.Filter.Comparison":return a.type==OpenLayers.Filter.Comparison.BETWEEN?
a.property+" BETWEEN "+this.write(a.lowerBoundary)+" AND "+this.write(a.upperBoundary):null!==a.value?a.property+" "+e[a.type]+" "+this.write(a.value):a.property+" "+e[a.type];case void 0:if("string"===typeof a)return"'"+a.replace(/'/g,"''")+"'";if("number"===typeof a)return String(a);default:throw Error("Can't encode: "+a.CLASS_NAME+" "+a);}},CLASS_NAME:"OpenLayers.Format.CQL"})}();OpenLayers.Control.Split=OpenLayers.Class(OpenLayers.Control,{layer:null,source:null,sourceOptions:null,tolerance:null,edge:!0,deferDelete:!1,mutual:!0,targetFilter:null,sourceFilter:null,handler:null,initialize:function(a){OpenLayers.Control.prototype.initialize.apply(this,[a]);this.options=a||{};this.options.source&&this.setSource(this.options.source)},setSource:function(a){this.active?(this.deactivate(),this.handler&&(this.handler.destroy(),delete this.handler),this.source=a,this.activate()):this.source=
a},activate:function(){var a=OpenLayers.Control.prototype.activate.call(this);if(a)if(!this.source)this.handler||(this.handler=new OpenLayers.Handler.Path(this,{done:function(a){this.onSketchComplete({feature:new OpenLayers.Feature.Vector(a)})}},{layerOptions:this.sourceOptions})),this.handler.activate();else if(this.source.events)this.source.events.on({sketchcomplete:this.onSketchComplete,afterfeaturemodified:this.afterFeatureModified,scope:this});return a},deactivate:function(){var a=OpenLayers.Control.prototype.deactivate.call(this);
a&&this.source&&this.source.events&&this.source.events.un({sketchcomplete:this.onSketchComplete,afterfeaturemodified:this.afterFeatureModified,scope:this});return a},onSketchComplete:function(a){this.feature=null;return!this.considerSplit(a.feature)},afterFeatureModified:function(a){a.modified&&"function"===typeof a.feature.geometry.split&&(this.feature=a.feature,this.considerSplit(a.feature))},removeByGeometry:function(a,b){for(var c=0,d=a.length;c<d;++c)if(a[c].geometry===b){a.splice(c,1);break}},
isEligible:function(a){return a.geometry?a.state!==OpenLayers.State.DELETE&&"function"===typeof a.geometry.split&&this.feature!==a&&(!this.targetFilter||this.targetFilter.evaluate(a.attributes)):!1},considerSplit:function(a){var b=!1,c=!1;if(!this.sourceFilter||this.sourceFilter.evaluate(a.attributes)){for(var d=this.layer&&this.layer.features||[],e,f,g=[],h=[],k=this.layer===this.source&&this.mutual,l={edge:this.edge,tolerance:this.tolerance,mutual:k},m=[a.geometry],n,p,q,r=0,s=d.length;r<s;++r)if(n=
d[r],this.isEligible(n)){p=[n.geometry];for(var t=0;t<m.length;++t){q=m[t];for(var u=0;u<p.length;++u)if(e=p[u],q.getBounds().intersectsBounds(e.getBounds())&&(e=q.split(e,l)))f=this.events.triggerEvent("beforesplit",{source:a,target:n}),!1!==f&&(k&&(f=e[0],1<f.length&&(f.unshift(t,1),Array.prototype.splice.apply(m,f),t+=f.length-3),e=e[1]),1<e.length&&(e.unshift(u,1),Array.prototype.splice.apply(p,e),u+=e.length-3))}p&&1<p.length&&(this.geomsToFeatures(n,p),this.events.triggerEvent("split",{original:n,
features:p}),Array.prototype.push.apply(g,p),h.push(n),c=!0)}m&&1<m.length&&(this.geomsToFeatures(a,m),this.events.triggerEvent("split",{original:a,features:m}),Array.prototype.push.apply(g,m),h.push(a),b=!0);if(b||c){if(this.deferDelete){d=[];r=0;for(s=h.length;r<s;++r)c=h[r],c.state===OpenLayers.State.INSERT?d.push(c):(c.state=OpenLayers.State.DELETE,this.layer.drawFeature(c));this.layer.destroyFeatures(d,{silent:!0});r=0;for(s=g.length;r<s;++r)g[r].state=OpenLayers.State.INSERT}else this.layer.destroyFeatures(h,
{silent:!0});this.layer.addFeatures(g,{silent:!0});this.events.triggerEvent("aftersplit",{source:a,features:g})}}return b},geomsToFeatures:function(a,b){var c=a.clone();delete c.geometry;for(var d,e=0,f=b.length;e<f;++e)d=c.clone(),d.geometry=b[e],d.state=OpenLayers.State.INSERT,b[e]=d},destroy:function(){this.active&&this.deactivate();OpenLayers.Control.prototype.destroy.call(this)},CLASS_NAME:"OpenLayers.Control.Split"});OpenLayers.Layer.WMTS=OpenLayers.Class(OpenLayers.Layer.Grid,{isBaseLayer:!0,version:"1.0.0",requestEncoding:"KVP",url:null,layer:null,matrixSet:null,style:null,format:"image/jpeg",tileOrigin:null,tileFullExtent:null,formatSuffix:null,matrixIds:null,dimensions:null,params:null,zoomOffset:0,serverResolutions:null,formatSuffixMap:{"image/png":"png","image/png8":"png","image/png24":"png","image/png32":"png",png:"png","image/jpeg":"jpg","image/jpg":"jpg",jpeg:"jpg",jpg:"jpg"},matrix:null,initialize:function(a){var b=
{url:!0,layer:!0,style:!0,matrixSet:!0},c;for(c in b)if(!(c in a))throw Error("Missing property '"+c+"' in layer configuration.");a.params=OpenLayers.Util.upperCaseObject(a.params);OpenLayers.Layer.Grid.prototype.initialize.apply(this,[a.name,a.url,a.params,a]);this.formatSuffix||(this.formatSuffix=this.formatSuffixMap[this.format]||this.format.split("/").pop());if(this.matrixIds&&(a=this.matrixIds.length)&&"string"===typeof this.matrixIds[0])for(b=this.matrixIds,this.matrixIds=Array(a),c=0;c<a;++c)this.matrixIds[c]=
{identifier:b[c]}},setMap:function(){OpenLayers.Layer.Grid.prototype.setMap.apply(this,arguments)},updateMatrixProperties:function(){if(this.matrix=this.getMatrix())this.matrix.topLeftCorner&&(this.tileOrigin=this.matrix.topLeftCorner),this.matrix.tileWidth&&this.matrix.tileHeight&&(this.tileSize=new OpenLayers.Size(this.matrix.tileWidth,this.matrix.tileHeight)),this.tileOrigin||(this.tileOrigin=new OpenLayers.LonLat(this.maxExtent.left,this.maxExtent.top)),this.tileFullExtent||(this.tileFullExtent=
this.maxExtent)},moveTo:function(a,b,c){!b&&this.matrix||this.updateMatrixProperties();return OpenLayers.Layer.Grid.prototype.moveTo.apply(this,arguments)},clone:function(a){null==a&&(a=new OpenLayers.Layer.WMTS(this.options));return a=OpenLayers.Layer.Grid.prototype.clone.apply(this,[a])},getIdentifier:function(){return this.getServerZoom()},getMatrix:function(){var a;if(this.matrixIds&&0!==this.matrixIds.length)if("scaleDenominator"in this.matrixIds[0])for(var b=OpenLayers.METERS_PER_INCH*OpenLayers.INCHES_PER_UNIT[this.units]*
this.getServerResolution()/2.8E-4,c=Number.POSITIVE_INFINITY,d,e=0,f=this.matrixIds.length;e<f;++e)d=Math.abs(1-this.matrixIds[e].scaleDenominator/b),d<c&&(c=d,a=this.matrixIds[e]);else a=this.matrixIds[this.getIdentifier()];else a={identifier:this.getIdentifier()};return a},getTileInfo:function(a){var b=this.getServerResolution(),c=(a.lon-this.tileOrigin.lon)/(b*this.tileSize.w);a=(this.tileOrigin.lat-a.lat)/(b*this.tileSize.h);var b=Math.floor(c),d=Math.floor(a);return{col:b,row:d,i:Math.floor((c-
b)*this.tileSize.w),j:Math.floor((a-d)*this.tileSize.h)}},getURL:function(a){a=this.adjustBounds(a);var b="";if(!this.tileFullExtent||this.tileFullExtent.intersectsBounds(a)){a=a.getCenterLonLat();var c=this.getTileInfo(a);a=this.dimensions;var d,b=OpenLayers.Util.isArray(this.url)?this.selectUrl([this.version,this.style,this.matrixSet,this.matrix.identifier,c.row,c.col].join(),this.url):this.url;if("REST"===this.requestEncoding.toUpperCase())if(d=this.params,-1!==b.indexOf("{")){b=b.replace(/\{/g,
"${");c={style:this.style,Style:this.style,TileMatrixSet:this.matrixSet,TileMatrix:this.matrix.identifier,TileRow:c.row,TileCol:c.col};if(a){var e,f;for(f=a.length-1;0<=f;--f)e=a[f],c[e]=d[e.toUpperCase()]}b=OpenLayers.String.format(b,c)}else{e=this.version+"/"+this.layer+"/"+this.style+"/";if(a)for(f=0;f<a.length;f++)d[a[f]]&&(e=e+d[a[f]]+"/");e=e+this.matrixSet+"/"+this.matrix.identifier+"/"+c.row+"/"+c.col+"."+this.formatSuffix;b.match(/\/$/)||(b+="/");b+=e}else"KVP"===this.requestEncoding.toUpperCase()&&
(d={SERVICE:"WMTS",REQUEST:"GetTile",VERSION:this.version,LAYER:this.layer,STYLE:this.style,TILEMATRIXSET:this.matrixSet,TILEMATRIX:this.matrix.identifier,TILEROW:c.row,TILECOL:c.col,FORMAT:this.format},b=OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this,[d]))}return b},mergeNewParams:function(a){if("KVP"===this.requestEncoding.toUpperCase())return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,[OpenLayers.Util.upperCaseObject(a)])},CLASS_NAME:"OpenLayers.Layer.WMTS"});OpenLayers.Protocol.SOS.v1_0_0=OpenLayers.Class(OpenLayers.Protocol,{fois:null,formatOptions:null,initialize:function(a){OpenLayers.Protocol.prototype.initialize.apply(this,[a]);a.format||(this.format=new OpenLayers.Format.SOSGetFeatureOfInterest(this.formatOptions))},destroy:function(){this.options&&!this.options.format&&this.format.destroy();this.format=null;OpenLayers.Protocol.prototype.destroy.apply(this)},read:function(a){a=OpenLayers.Util.extend({},a);OpenLayers.Util.applyDefaults(a,this.options||
{});var b=new OpenLayers.Protocol.Response({requestType:"read"}),c=this.format,c=OpenLayers.Format.XML.prototype.write.apply(c,[c.writeNode("sos:GetFeatureOfInterest",{fois:this.fois})]);b.priv=OpenLayers.Request.POST({url:a.url,callback:this.createCallback(this.handleRead,b,a),data:c});return b},handleRead:function(a,b){if(b.callback){var c=a.priv;200<=c.status&&300>c.status?(a.features=this.parseFeatures(c),a.code=OpenLayers.Protocol.Response.SUCCESS):a.code=OpenLayers.Protocol.Response.FAILURE;
b.callback.call(b.scope,a)}},parseFeatures:function(a){var b=a.responseXML;b&&b.documentElement||(b=a.responseText);return!b||0>=b.length?null:this.format.read(b)},CLASS_NAME:"OpenLayers.Protocol.SOS.v1_0_0"});OpenLayers.Layer.KaMapCache=OpenLayers.Class(OpenLayers.Layer.KaMap,{IMAGE_EXTENSIONS:{jpeg:"jpg",gif:"gif",png:"png",png8:"png",png24:"png",dithered:"png"},DEFAULT_FORMAT:"jpeg",initialize:function(a,b,c,d){OpenLayers.Layer.KaMap.prototype.initialize.apply(this,arguments);this.extension=this.IMAGE_EXTENSIONS[this.params.i.toLowerCase()||this.DEFAULT_FORMAT]},getURL:function(a){a=this.adjustBounds(a);var b=this.map.getResolution(),c=Math.round(1E4*this.map.getScale())/1E4,d=Math.round(a.left/b);a=
-Math.round(a.top/b);var b=Math.floor(d/this.tileSize.w/this.params.metaTileSize.w)*this.tileSize.w*this.params.metaTileSize.w,e=Math.floor(a/this.tileSize.h/this.params.metaTileSize.h)*this.tileSize.h*this.params.metaTileSize.h,c=["/",this.params.map,"/",c,"/",this.params.g.replace(/\s/g,"_"),"/def/t",e,"/l",b,"/t",a,"l",d,".",this.extension],d=this.url;OpenLayers.Util.isArray(d)&&(d=this.selectUrl(c.join(""),d));return d+c.join("")},CLASS_NAME:"OpenLayers.Layer.KaMapCache"});OpenLayers.Protocol.WFS.v1_1_0=OpenLayers.Class(OpenLayers.Protocol.WFS.v1,{version:"1.1.0",initialize:function(a){OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this,arguments);this.outputFormat&&!this.readFormat&&("gml2"==this.outputFormat.toLowerCase()?this.readFormat=new OpenLayers.Format.GML.v2({featureType:this.featureType,featureNS:this.featureNS,geometryName:this.geometryName}):"json"==this.outputFormat.toLowerCase()&&(this.readFormat=new OpenLayers.Format.GeoJSON))},CLASS_NAME:"OpenLayers.Protocol.WFS.v1_1_0"});OpenLayers.Format.WMSCapabilities.v1_1_1=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1,{version:"1.1.1",readers:{wms:OpenLayers.Util.applyDefaults({SRS:function(a,b){b.srs[this.getChildValue(a)]=!0}},OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers.wms)},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_1_1"});OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC=OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1_1,{version:"1.1.1",profile:"WMSC",readers:{wms:OpenLayers.Util.applyDefaults({VendorSpecificCapabilities:function(a,b){b.vendorSpecific={tileSets:[]};this.readChildNodes(a,b.vendorSpecific)},TileSet:function(a,b){var c={srs:{},bbox:{},resolutions:[]};this.readChildNodes(a,c);b.tileSets.push(c)},Resolutions:function(a,b){for(var c=this.getChildValue(a).split(" "),d=0,e=c.length;d<e;d++)""!=c[d]&&b.resolutions.push(parseFloat(c[d]))},
Width:function(a,b){b.width=parseInt(this.getChildValue(a))},Height:function(a,b){b.height=parseInt(this.getChildValue(a))},Layers:function(a,b){b.layers=this.getChildValue(a)},Styles:function(a,b){b.styles=this.getChildValue(a)}},OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers.wms)},CLASS_NAME:"OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC"});OpenLayers.Control.LayerSwitcher=OpenLayers.Class(OpenLayers.Control,{layerStates:null,layersDiv:null,baseLayersDiv:null,baseLayers:null,dataLbl:null,dataLayersDiv:null,dataLayers:null,minimizeDiv:null,maximizeDiv:null,ascending:!0,initialize:function(a){OpenLayers.Control.prototype.initialize.apply(this,arguments);this.layerStates=[]},destroy:function(){this.clearLayersArray("base");this.clearLayersArray("data");this.map.events.un({buttonclick:this.onButtonClick,addlayer:this.redraw,changelayer:this.redraw,
removelayer:this.redraw,changebaselayer:this.redraw,scope:this});this.events.unregister("buttonclick",this,this.onButtonClick);OpenLayers.Control.prototype.destroy.apply(this,arguments)},setMap:function(a){OpenLayers.Control.prototype.setMap.apply(this,arguments);this.map.events.on({addlayer:this.redraw,changelayer:this.redraw,removelayer:this.redraw,changebaselayer:this.redraw,scope:this});this.outsideViewport?(this.events.attachToElement(this.div),this.events.register("buttonclick",this,this.onButtonClick)):
this.map.events.register("buttonclick",this,this.onButtonClick)},draw:function(){OpenLayers.Control.prototype.draw.apply(this);this.loadContents();this.outsideViewport||this.minimizeControl();this.redraw();return this.div},onButtonClick:function(a){a=a.buttonElement;a===this.minimizeDiv?this.minimizeControl():a===this.maximizeDiv?this.maximizeControl():a._layerSwitcher===this.id&&(a["for"]&&(a=document.getElementById(a["for"])),a.disabled||("radio"==a.type?(a.checked=!0,this.map.setBaseLayer(this.map.getLayer(a._layer))):
(a.checked=!a.checked,this.updateMap())))},clearLayersArray:function(a){this[a+"LayersDiv"].innerHTML="";this[a+"Layers"]=[]},checkRedraw:function(){if(!this.layerStates.length||this.map.layers.length!=this.layerStates.length)return!0;for(var a=0,b=this.layerStates.length;a<b;a++){var c=this.layerStates[a],d=this.map.layers[a];if(c.name!=d.name||c.inRange!=d.inRange||c.id!=d.id||c.visibility!=d.visibility)return!0}return!1},redraw:function(){if(!this.checkRedraw())return this.div;this.clearLayersArray("base");
this.clearLayersArray("data");var a=!1,b=!1,c=this.map.layers.length;this.layerStates=Array(c);for(var d=0;d<c;d++){var e=this.map.layers[d];this.layerStates[d]={name:e.name,visibility:e.visibility,inRange:e.inRange,id:e.id}}var f=this.map.layers.slice();this.ascending||f.reverse();d=0;for(c=f.length;d<c;d++){var e=f[d],g=e.isBaseLayer;if(e.displayInLayerSwitcher){g?b=!0:a=!0;var h=g?e==this.map.baseLayer:e.getVisibility(),k=document.createElement("input"),l=OpenLayers.Util.createUniqueID(this.id+
"_input_");k.id=l;k.name=g?this.id+"_baseLayers":e.name;k.type=g?"radio":"checkbox";k.value=e.name;k.checked=h;k.defaultChecked=h;k.className="olButton";k._layer=e.id;k._layerSwitcher=this.id;g||e.inRange||(k.disabled=!0);h=document.createElement("label");h["for"]=k.id;OpenLayers.Element.addClass(h,"labelSpan olButton");h._layer=e.id;h._layerSwitcher=this.id;g||e.inRange||(h.style.color="gray");h.innerHTML=e.name;h.style.verticalAlign=g?"bottom":"baseline";l=document.createElement("br");(g?this.baseLayers:
this.dataLayers).push({layer:e,inputElem:k,labelSpan:h});e=g?this.baseLayersDiv:this.dataLayersDiv;e.appendChild(k);e.appendChild(h);e.appendChild(l)}}this.dataLbl.style.display=a?"":"none";this.baseLbl.style.display=b?"":"none";return this.div},updateMap:function(){for(var a=0,b=this.baseLayers.length;a<b;a++){var c=this.baseLayers[a];c.inputElem.checked&&this.map.setBaseLayer(c.layer,!1)}a=0;for(b=this.dataLayers.length;a<b;a++)c=this.dataLayers[a],c.layer.setVisibility(c.inputElem.checked)},maximizeControl:function(a){this.div.style.width=
"";this.div.style.height="";this.showControls(!1);null!=a&&OpenLayers.Event.stop(a)},minimizeControl:function(a){this.div.style.width="0px";this.div.style.height="0px";this.showControls(!0);null!=a&&OpenLayers.Event.stop(a)},showControls:function(a){this.maximizeDiv.style.display=a?"":"none";this.minimizeDiv.style.display=a?"none":"";this.layersDiv.style.display=a?"none":""},loadContents:function(){this.layersDiv=document.createElement("div");this.layersDiv.id=this.id+"_layersDiv";OpenLayers.Element.addClass(this.layersDiv,
"layersDiv");this.baseLbl=document.createElement("div");this.baseLbl.innerHTML=OpenLayers.i18n("Base Layer");OpenLayers.Element.addClass(this.baseLbl,"baseLbl");this.baseLayersDiv=document.createElement("div");OpenLayers.Element.addClass(this.baseLayersDiv,"baseLayersDiv");this.dataLbl=document.createElement("div");this.dataLbl.innerHTML=OpenLayers.i18n("Overlays");OpenLayers.Element.addClass(this.dataLbl,"dataLbl");this.dataLayersDiv=document.createElement("div");OpenLayers.Element.addClass(this.dataLayersDiv,
"dataLayersDiv");this.ascending?(this.layersDiv.appendChild(this.baseLbl),this.layersDiv.appendChild(this.baseLayersDiv),this.layersDiv.appendChild(this.dataLbl),this.layersDiv.appendChild(this.dataLayersDiv)):(this.layersDiv.appendChild(this.dataLbl),this.layersDiv.appendChild(this.dataLayersDiv),this.layersDiv.appendChild(this.baseLbl),this.layersDiv.appendChild(this.baseLayersDiv));this.div.appendChild(this.layersDiv);var a=OpenLayers.Util.getImageLocation("layer-switcher-maximize.png");this.maximizeDiv=
OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MaximizeDiv",null,null,a,"absolute");OpenLayers.Element.addClass(this.maximizeDiv,"maximizeDiv olButton");this.maximizeDiv.style.display="none";this.div.appendChild(this.maximizeDiv);a=OpenLayers.Util.getImageLocation("layer-switcher-minimize.png");this.minimizeDiv=OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MinimizeDiv",null,null,a,"absolute");OpenLayers.Element.addClass(this.minimizeDiv,"minimizeDiv olButton");this.minimizeDiv.style.display=
"none";this.div.appendChild(this.minimizeDiv)},CLASS_NAME:"OpenLayers.Control.LayerSwitcher"});OpenLayers.Format.Atom=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{atom:"http://www.w3.org/2005/Atom",georss:"http://www.georss.org/georss"},feedTitle:"untitled",defaultEntryTitle:"untitled",gmlParser:null,xy:!1,read:function(a){"string"==typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));return this.parseFeatures(a)},write:function(a){var b;if(OpenLayers.Util.isArray(a)){b=this.createElementNSPlus("atom:feed");b.appendChild(this.createElementNSPlus("atom:title",{value:this.feedTitle}));
for(var c=0,d=a.length;c<d;c++)b.appendChild(this.buildEntryNode(a[c]))}else b=this.buildEntryNode(a);return OpenLayers.Format.XML.prototype.write.apply(this,[b])},buildContentNode:function(a){var b=this.createElementNSPlus("atom:content",{attributes:{type:a.type||null}});if(a.src)b.setAttribute("src",a.src);else if("text"==a.type||null==a.type)b.appendChild(this.createTextNode(a.value));else if("html"==a.type){if("string"!=typeof a.value)throw"HTML content must be in form of an escaped string";b.appendChild(this.createTextNode(a.value))}else"xhtml"==
a.type?b.appendChild(a.value):"xhtml"==a.type||a.type.match(/(\+|\/)xml$/)?b.appendChild(a.value):b.appendChild(this.createTextNode(a.value));return b},buildEntryNode:function(a){var b=a.attributes,c=b.atom||{},d=this.createElementNSPlus("atom:entry");if(c.authors)for(var e=OpenLayers.Util.isArray(c.authors)?c.authors:[c.authors],f=0,g=e.length;f<g;f++)d.appendChild(this.buildPersonConstructNode("author",e[f]));if(c.categories)for(var e=OpenLayers.Util.isArray(c.categories)?c.categories:[c.categories],
h,f=0,g=e.length;f<g;f++)h=e[f],d.appendChild(this.createElementNSPlus("atom:category",{attributes:{term:h.term,scheme:h.scheme||null,label:h.label||null}}));c.content&&d.appendChild(this.buildContentNode(c.content));if(c.contributors)for(e=OpenLayers.Util.isArray(c.contributors)?c.contributors:[c.contributors],f=0,g=e.length;f<g;f++)d.appendChild(this.buildPersonConstructNode("contributor",e[f]));a.fid&&d.appendChild(this.createElementNSPlus("atom:id",{value:a.fid}));if(c.links)for(e=OpenLayers.Util.isArray(c.links)?
c.links:[c.links],f=0,g=e.length;f<g;f++)h=e[f],d.appendChild(this.createElementNSPlus("atom:link",{attributes:{href:h.href,rel:h.rel||null,type:h.type||null,hreflang:h.hreflang||null,title:h.title||null,length:h.length||null}}));c.published&&d.appendChild(this.createElementNSPlus("atom:published",{value:c.published}));c.rights&&d.appendChild(this.createElementNSPlus("atom:rights",{value:c.rights}));(c.summary||b.description)&&d.appendChild(this.createElementNSPlus("atom:summary",{value:c.summary||
b.description}));d.appendChild(this.createElementNSPlus("atom:title",{value:c.title||b.title||this.defaultEntryTitle}));c.updated&&d.appendChild(this.createElementNSPlus("atom:updated",{value:c.updated}));a.geometry&&(b=this.createElementNSPlus("georss:where"),b.appendChild(this.buildGeometryNode(a.geometry)),d.appendChild(b));return d},initGmlParser:function(){this.gmlParser=new OpenLayers.Format.GML.v3({xy:this.xy,featureNS:"http://example.com#feature",internalProjection:this.internalProjection,
externalProjection:this.externalProjection})},buildGeometryNode:function(a){this.gmlParser||this.initGmlParser();return this.gmlParser.writeNode("feature:_geometry",a).firstChild},buildPersonConstructNode:function(a,b){var c=["uri","email"],d=this.createElementNSPlus("atom:"+a);d.appendChild(this.createElementNSPlus("atom:name",{value:b.name}));for(var e=0,f=c.length;e<f;e++)b[c[e]]&&d.appendChild(this.createElementNSPlus("atom:"+c[e],{value:b[c[e]]}));return d},getFirstChildValue:function(a,b,c,
d){return(a=this.getElementsByTagNameNS(a,b,c))&&0<a.length?this.getChildValue(a[0],d):d},parseFeature:function(a){var b={},c=null,d=null,e=null,f=this.namespaces.atom;this.parsePersonConstructs(a,"author",b);d=this.getElementsByTagNameNS(a,f,"category");0<d.length&&(b.categories=[]);for(var g=0,h=d.length;g<h;g++){c={};c.term=d[g].getAttribute("term");if(e=d[g].getAttribute("scheme"))c.scheme=e;if(e=d[g].getAttribute("label"))c.label=e;b.categories.push(c)}d=this.getElementsByTagNameNS(a,f,"content");
if(0<d.length){c={};if(e=d[0].getAttribute("type"))c.type=e;(e=d[0].getAttribute("src"))?c.src=e:("text"==c.type||"html"==c.type||null==c.type?c.value=this.getFirstChildValue(a,f,"content",null):"xhtml"==c.type||c.type.match(/(\+|\/)xml$/)?c.value=this.getChildEl(d[0]):c.value=this.getFirstChildValue(a,f,"content",null),b.content=c)}this.parsePersonConstructs(a,"contributor",b);b.id=this.getFirstChildValue(a,f,"id",null);d=this.getElementsByTagNameNS(a,f,"link");0<d.length&&(b.links=Array(d.length));
for(var k=["rel","type","hreflang","title","length"],g=0,h=d.length;g<h;g++){c={};c.href=d[g].getAttribute("href");for(var l=0,m=k.length;l<m;l++)(e=d[g].getAttribute(k[l]))&&(c[k[l]]=e);b.links[g]=c}if(c=this.getFirstChildValue(a,f,"published",null))b.published=c;if(c=this.getFirstChildValue(a,f,"rights",null))b.rights=c;if(c=this.getFirstChildValue(a,f,"summary",null))b.summary=c;b.title=this.getFirstChildValue(a,f,"title",null);b.updated=this.getFirstChildValue(a,f,"updated",null);c={title:b.title,
description:b.summary,atom:b};a=this.parseLocations(a)[0];a=new OpenLayers.Feature.Vector(a,c);a.fid=b.id;return a},parseFeatures:function(a){var b=[],c=this.getElementsByTagNameNS(a,this.namespaces.atom,"entry");0==c.length&&(c=[a]);a=0;for(var d=c.length;a<d;a++)b.push(this.parseFeature(c[a]));return b},parseLocations:function(a){var b=this.namespaces.georss,c={components:[]},d=this.getElementsByTagNameNS(a,b,"where");if(d&&0<d.length){this.gmlParser||this.initGmlParser();for(var e=0,f=d.length;e<
f;e++)this.gmlParser.readChildNodes(d[e],c)}c=c.components;if((d=this.getElementsByTagNameNS(a,b,"point"))&&0<d.length)for(e=0,f=d.length;e<f;e++){var g=OpenLayers.String.trim(d[e].firstChild.nodeValue).split(/\s+/);2!=g.length&&(g=OpenLayers.String.trim(d[e].firstChild.nodeValue).split(/\s*,\s*/));c.push(new OpenLayers.Geometry.Point(g[1],g[0]))}var h=this.getElementsByTagNameNS(a,b,"line");if(h&&0<h.length)for(var k,e=0,f=h.length;e<f;e++){d=OpenLayers.String.trim(h[e].firstChild.nodeValue).split(/\s+/);
k=[];for(var l=0,m=d.length;l<m;l+=2)g=new OpenLayers.Geometry.Point(d[l+1],d[l]),k.push(g);c.push(new OpenLayers.Geometry.LineString(k))}if((a=this.getElementsByTagNameNS(a,b,"polygon"))&&0<a.length)for(e=0,f=a.length;e<f;e++){d=OpenLayers.String.trim(a[e].firstChild.nodeValue).split(/\s+/);k=[];l=0;for(m=d.length;l<m;l+=2)g=new OpenLayers.Geometry.Point(d[l+1],d[l]),k.push(g);c.push(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(k)]))}if(this.internalProjection&&this.externalProjection)for(e=
0,f=c.length;e<f;e++)c[e]&&c[e].transform(this.externalProjection,this.internalProjection);return c},parsePersonConstructs:function(a,b,c){var d=[],e=this.namespaces.atom;a=this.getElementsByTagNameNS(a,e,b);for(var f=["uri","email"],g=0,h=a.length;g<h;g++){var k={};k.name=this.getFirstChildValue(a[g],e,"name",null);for(var l=0,m=f.length;l<m;l++){var n=this.getFirstChildValue(a[g],e,f[l],null);n&&(k[f[l]]=n)}d.push(k)}0<d.length&&(c[b+"s"]=d)},CLASS_NAME:"OpenLayers.Format.Atom"});OpenLayers.Control.KeyboardDefaults=OpenLayers.Class(OpenLayers.Control,{autoActivate:!0,slideFactor:75,observeElement:null,draw:function(){this.handler=new OpenLayers.Handler.Keyboard(this,{keydown:this.defaultKeyPress},{observeElement:this.observeElement||document})},defaultKeyPress:function(a){var b,c=!0;b=OpenLayers.Event.element(a);if(!b||"INPUT"!=b.tagName&&"TEXTAREA"!=b.tagName&&"SELECT"!=b.tagName){switch(a.keyCode){case OpenLayers.Event.KEY_LEFT:this.map.pan(-this.slideFactor,0);break;case OpenLayers.Event.KEY_RIGHT:this.map.pan(this.slideFactor,
0);break;case OpenLayers.Event.KEY_UP:this.map.pan(0,-this.slideFactor);break;case OpenLayers.Event.KEY_DOWN:this.map.pan(0,this.slideFactor);break;case 33:b=this.map.getSize();this.map.pan(0,-0.75*b.h);break;case 34:b=this.map.getSize();this.map.pan(0,0.75*b.h);break;case 35:b=this.map.getSize();this.map.pan(0.75*b.w,0);break;case 36:b=this.map.getSize();this.map.pan(-0.75*b.w,0);break;case 43:case 61:case 187:case 107:this.map.zoomIn();break;case 45:case 109:case 189:case 95:this.map.zoomOut();
break;default:c=!1}c&&OpenLayers.Event.stop(a)}},CLASS_NAME:"OpenLayers.Control.KeyboardDefaults"});OpenLayers.Format.WMTSCapabilities.v1_0_0=OpenLayers.Class(OpenLayers.Format.OWSCommon.v1_1_0,{version:"1.0.0",namespaces:{ows:"http://www.opengis.net/ows/1.1",wmts:"http://www.opengis.net/wmts/1.0",xlink:"http://www.w3.org/1999/xlink"},yx:null,defaultPrefix:"wmts",initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a]);this.options=a;a=OpenLayers.Util.extend({},OpenLayers.Format.WMTSCapabilities.prototype.yx);this.yx=OpenLayers.Util.extend(a,this.yx)},read:function(a){"string"==
typeof a&&(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));a&&9==a.nodeType&&(a=a.documentElement);var b={};this.readNode(a,b);b.version=this.version;return b},readers:{wmts:{Capabilities:function(a,b){this.readChildNodes(a,b)},Contents:function(a,b){b.contents={};b.contents.layers=[];b.contents.tileMatrixSets={};this.readChildNodes(a,b.contents)},Layer:function(a,b){var c={styles:[],formats:[],dimensions:[],tileMatrixSetLinks:[],layers:[]};this.readChildNodes(a,c);b.layers.push(c)},Style:function(a,
b){var c={};c.isDefault="true"===a.getAttribute("isDefault");this.readChildNodes(a,c);b.styles.push(c)},Format:function(a,b){b.formats.push(this.getChildValue(a))},TileMatrixSetLink:function(a,b){var c={};this.readChildNodes(a,c);b.tileMatrixSetLinks.push(c)},TileMatrixSet:function(a,b){if(b.layers){var c={matrixIds:[]};this.readChildNodes(a,c);b.tileMatrixSets[c.identifier]=c}else b.tileMatrixSet=this.getChildValue(a)},TileMatrix:function(a,b){var c={supportedCRS:b.supportedCRS};this.readChildNodes(a,
c);b.matrixIds.push(c)},ScaleDenominator:function(a,b){b.scaleDenominator=parseFloat(this.getChildValue(a))},TopLeftCorner:function(a,b){var c=this.getChildValue(a).split(" "),d;b.supportedCRS&&(d=b.supportedCRS.replace(/urn:ogc:def:crs:(\w+):.+:(\w+)$/,"urn:ogc:def:crs:$1::$2"),d=!!this.yx[d]);b.topLeftCorner=d?new OpenLayers.LonLat(c[1],c[0]):new OpenLayers.LonLat(c[0],c[1])},TileWidth:function(a,b){b.tileWidth=parseInt(this.getChildValue(a))},TileHeight:function(a,b){b.tileHeight=parseInt(this.getChildValue(a))},
MatrixWidth:function(a,b){b.matrixWidth=parseInt(this.getChildValue(a))},MatrixHeight:function(a,b){b.matrixHeight=parseInt(this.getChildValue(a))},ResourceURL:function(a,b){b.resourceUrl=b.resourceUrl||{};var c=a.getAttribute("resourceType");b.resourceUrls||(b.resourceUrls=[]);c=b.resourceUrl[c]={format:a.getAttribute("format"),template:a.getAttribute("template"),resourceType:c};b.resourceUrls.push(c)},WSDL:function(a,b){b.wsdl={};b.wsdl.href=a.getAttribute("xlink:href")},ServiceMetadataURL:function(a,
b){b.serviceMetadataUrl={};b.serviceMetadataUrl.href=a.getAttribute("xlink:href")},LegendURL:function(a,b){b.legend={};b.legend.href=a.getAttribute("xlink:href");b.legend.format=a.getAttribute("format")},Dimension:function(a,b){var c={values:[]};this.readChildNodes(a,c);b.dimensions.push(c)},Default:function(a,b){b["default"]=this.getChildValue(a)},Value:function(a,b){b.values.push(this.getChildValue(a))}},ows:OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers.ows},CLASS_NAME:"OpenLayers.Format.WMTSCapabilities.v1_0_0"});
/*
 * Patch che all'evento 'movestart' passa anche il valore dello zoom futuro
 */
 
OpenLayers.Map.prototype.moveTo = function(lonlat, zoom, options) {
	
        if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {
            lonlat = new OpenLayers.LonLat(lonlat);
        }
        if (!options) { 
            options = {};
        }
        if (zoom != null) {
            zoom = parseFloat(zoom);
            if (!this.fractionalZoom) {
                zoom = Math.round(zoom);
            }
        }
        var requestedZoom = zoom;
        zoom = this.adjustZoom(zoom);
        if (zoom !== requestedZoom) {
            // zoom was adjusted, so keep old lonlat to avoid panning
            lonlat = this.getCenter();
        }
        // dragging is false by default
        var dragging = options.dragging || this.dragging;
        // forceZoomChange is false by default
        var forceZoomChange = options.forceZoomChange;

        if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {
            lonlat = this.maxExtent.getCenterLonLat();
            this.center = lonlat.clone();
        }

        if(this.restrictedExtent != null) {
            // In 3.0, decide if we want to change interpretation of maxExtent.
            if(lonlat == null) { 
                lonlat = this.center; 
            }
            if(zoom == null) { 
                zoom = this.getZoom(); 
            }
            var resolution = this.getResolutionForZoom(zoom);
            var extent = this.calculateBounds(lonlat, resolution); 
            if(!this.restrictedExtent.containsBounds(extent)) {
                var maxCenter = this.restrictedExtent.getCenterLonLat(); 
                if(extent.getWidth() > this.restrictedExtent.getWidth()) { 
                    lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat); 
                } else if(extent.left < this.restrictedExtent.left) {
                    lonlat = lonlat.add(this.restrictedExtent.left -
                                        extent.left, 0); 
                } else if(extent.right > this.restrictedExtent.right) { 
                    lonlat = lonlat.add(this.restrictedExtent.right -
                                        extent.right, 0); 
                } 
                if(extent.getHeight() > this.restrictedExtent.getHeight()) { 
                    lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat); 
                } else if(extent.bottom < this.restrictedExtent.bottom) { 
                    lonlat = lonlat.add(0, this.restrictedExtent.bottom -
                                        extent.bottom); 
                } 
                else if(extent.top > this.restrictedExtent.top) { 
                    lonlat = lonlat.add(0, this.restrictedExtent.top -
                                        extent.top); 
                } 
            }
        }
        
        var zoomChanged = forceZoomChange || (
                            (this.isValidZoomLevel(zoom)) && 
                            (zoom != this.getZoom()) );

        var centerChanged = (this.isValidLonLat(lonlat)) && 
                            (!lonlat.equals(this.center));

        // if neither center nor zoom will change, no need to do anything
        if (zoomChanged || centerChanged || dragging) {
            dragging || this.events.triggerEvent("movestart", {
                zoomChanged: zoomChanged,
                zoom: zoom
                //,zoom2: zoom
            });

            if (centerChanged) {
                if (!zoomChanged && this.center) { 
                    // if zoom hasnt changed, just slide layerContainer
                    //  (must be done before setting this.center to new value)
                    this.centerLayerContainer(lonlat);
                }
                this.center = lonlat.clone();
            }

            var res = zoomChanged ?
                this.getResolutionForZoom(zoom) : this.getResolution();
            // (re)set the layerContainerDiv's location
            if (zoomChanged || this.layerContainerOrigin == null) {
                this.layerContainerOrigin = this.getCachedCenter();
                this.layerContainerOriginPx.x = 0;
                this.layerContainerOriginPx.y = 0;
                this.applyTransform();
                var maxExtent = this.getMaxExtent({restricted: true});
                var maxExtentCenter = maxExtent.getCenterLonLat();
                var lonDelta = this.center.lon - maxExtentCenter.lon;
                var latDelta = maxExtentCenter.lat - this.center.lat;
                var extentWidth = Math.round(maxExtent.getWidth() / res);
                var extentHeight = Math.round(maxExtent.getHeight() / res);
                this.minPx = {
                    x: (this.size.w - extentWidth) / 2 - lonDelta / res,
                    y: (this.size.h - extentHeight) / 2 - latDelta / res
                };
                this.maxPx = {
                    x: this.minPx.x + Math.round(maxExtent.getWidth() / res),
                    y: this.minPx.y + Math.round(maxExtent.getHeight() / res)
                };
            }

            if (zoomChanged) {
                this.zoom = zoom;
                this.resolution = res;
            }    
            
            var bounds = this.getExtent();
            
            //send the move call to the baselayer and all the overlays    

            if(this.baseLayer.visibility) {
                this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);
                options.dragging || this.baseLayer.events.triggerEvent(
                    "moveend", {zoomChanged: zoomChanged}
                );
            }
            
            bounds = this.baseLayer.getExtent();
            
            for (var i=this.layers.length-1; i>=0; --i) {
                var layer = this.layers[i];
                if (layer !== this.baseLayer && !layer.isBaseLayer) {
                    var inRange = layer.calculateInRange();
                    if (layer.inRange != inRange) {
                        // the inRange property has changed. If the layer is
                        // no longer in range, we turn it off right away. If
                        // the layer is no longer out of range, the moveTo
                        // call below will turn on the layer.
                        layer.inRange = inRange;
                        if (!inRange) {
                            layer.display(false);
                        }
						
						this.events.triggerEvent("changelayer", {
                            layer: layer, property: "visibility"
                        });
                    }
                    if (inRange && layer.visibility) {
                        layer.moveTo(bounds, zoomChanged, options.dragging);
                        options.dragging || layer.events.triggerEvent(
                            "moveend", {zoomChanged: zoomChanged}
                        );
                    }
                }                
            }
            
            this.events.triggerEvent("move");
            dragging || this.events.triggerEvent("moveend");

            if (zoomChanged) {
                //redraw popups
                for (var i=0, len=this.popups.length; i<len; i++) {
                    this.popups[i].updatePosition();
                }
                this.events.triggerEvent("zoomend");
            }
        }
    }
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */


/**
 * @requires OpenLayers/Handler/Point.js
 * @requires OpenLayers/Geometry/Point.js
 * @requires OpenLayers/Geometry/LineString.js
 */

/**
 * Class: OpenLayers.Handler.PointFromRef
 * Handler to draw a point that have a defined distance from another.  
 * Point of reference is set by mousedown. You can fix the distance by
 * the property defaultDistance, or set it by another mousedown.
 * After this step you can choose only the direction.
 *
 * Inherits from:
 *  - <OpenLayers.Handler.Point>
 */
OpenLayers.Handler.PointFromRef = OpenLayers.Class(OpenLayers.Handler.Point, {
    
    /**
     * Property: defaultDistance
     * {Number} This property fix the distance from th reference point.
     */
    defaultDistance: null,
        
    /**
     * Property: drawDistance
     * {Boolean} If true the Handler allows to set the distance from
     * reference point by a click of the mouse.
     */
    drawDistance: true,
    
    /**
     * Property: line
     * {<OpenLayers.Feature.Vector>}
     */
    line: null,
    
    infoOnMouse: true,
    
    /**
     * Method: setDistance
     * Set the distance from the point of reference.
     * This has no effect on defaultDistance
     *
     * Returns:
     * {Boolean}
     */
    setDistance: function (d){
    	this.distance = d;
    	if(this.drawing){
    		this.modifyFeature(); 
    		this.drawFeature();
    	}
    	this.callback("distance", [this.distance]);
    },

    /**
     * Constructor: OpenLayers.Handler.Path
     * Create a new path hander
     *
     * Parameters:
     * control - {<OpenLayers.Control>} 
     * callbacks - {Object} An object with a 'done' property whose value is a
     *     function to be called when the point drawing is finished. he callback should expect to recieve a single argument,
     *     the point geometry. 
     *     If the callbacks object contains a 'refPoint' property, 
     *     this function will be sent when the reference point is set.
     *     If the callbacks object contains a 'distance' property, 
     *     this function will be sent when the distance is set or when the distance
     *     is not set and the mousemove.
     *     If the callbacks object contains a
     *     'cancel' property, this function will be called when the
     *     handler is deactivated while drawing.   
     *     If the callbacks object contains a 'cancel' property, this function 
     *     will be called when the handler is deactivated while drawing. The 
     *     cancel should expect to receive a geometry.
     * options - {Object} An optional object with properties to be set on the
     *           handler
     */
    initialize: function(control, callbacks, options) {
    	    	
        OpenLayers.Handler.Point.prototype.initialize.apply(this, arguments);
        this.distance = null;    
        this.toFinalize = false;    
         // cache the bound event listener method so it can be unobserved later
        this.eventListener = OpenLayers.Function.bindAsEventListener(
            this.keydown, this
        );
        
    },
    
    /**
     * Metodo identico a quello del padre (Point)
     * L'unica modifica apportata � l'inversione delle operazioni 1 e 2 e l'aggiunta necessaria della 3
     * Potendo esserci il richiamo di una finestra modale sulla callback (alert, confirm, etc.)
     * non sparirebbero le linee di costruzione prima di aver risposto.
     */
    finalize: function(cancel) {
        var key = cancel ? "cancel" : "done";
        this.drawing = false;
        this.mouseDown = false;
        this.lastDown = null;
        this.lastUp = null;
        /* 3 */
        var clone = this.geometryClone();
        
        /* 2 */
        if(cancel || !this.persist) {
            this.destroyFeature();
        }
        /* 1 */
        this.callback(key, [clone]);
       
    },
    
    /**
     * APIMethod: activate
     * turn on the handler
     */
    activate: function() {
        if(OpenLayers.Handler.Point.prototype.activate.apply(this, arguments)) {
    		OpenLayers.Event.observe(
                    document, "keydown", this.eventListener);
                    /*
            if(this.defaultDistance){
            	this.distance = this.defaultDistance;
            }
            */
            return true;
        }else{
        	return false;
        }
    },
    
    /**
     * APIMethod: deactivate
     * turn off the handler
     */
    deactivate: function() {
    	var deactivated = false;
        if (OpenLayers.Handler.Point.prototype.deactivate.apply(this, arguments)) {
            OpenLayers.Event.stopObserving(document, "keydown", this.eventListener);                       
            deactivated = true;
        }
        return deactivated;        
    },
    
        
    /**
     * Method: createFeature
     * Add temporary geometries
     */   
    createFeature: function(pixel) { 
         var lonlat = this.control.map.getLonLatFromPixel(pixel); 
         this.point = new OpenLayers.Feature.Vector( 
             new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat) 
         ); 
         this.refPoint = new OpenLayers.Feature.Vector(
                                        new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat));
         this.finalPoint = new OpenLayers.Feature.Vector(
                                        new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat));
         this.line = new OpenLayers.Feature.Vector( 
             new OpenLayers.Geometry.LineString([this.refPoint.geometry]) 
         ); 
         
         if(this.infoOnMouse){
         	this.createInfoPopup(lonlat);
         }
         this.callback("create", [this.point.geometry, this.line]); 
         this.point.geometry.clearBounds(); 
         this.layer.addFeatures([this.line,this.refPoint,this.point,this.finalPoint], {silent: true}); 
     }, 
    
        
    /**
     * Method: destroyFeature
     * Destroy temporary geometries and reset distance
     */
    destroyFeature: function() {
    	 
        OpenLayers.Handler.Point.prototype.destroyFeature.apply(this);

        this.line = null;
        this.refPoint = null;
        this.finalPoint = null;     
        this.toFinalize = false;
        if(!this.defaultDistance){
        	this.distance = null;
        }else{
        	this.distance = this.defaultDistance;
        }
        
        if(this.popup){
        	this.destroyInfoPopup();
        }
    },
    
    /**
     * Method: addPoint
     * Add point to geometry.  Send the point index to override
     * the behavior of LinearRing that disregards adding duplicate points.
     */
    addPoint: function() {
        this.line.geometry.addComponent(this.point.geometry.clone(),
                                        this.line.geometry.components.length);
    },

    /**
     * Method: modifyFeature
     * Modify the existing geometry given the new point
     */
     
    modifyFeature: function(pixel) {
        var index = this.line.geometry.components.length - 1;
      
        // Creo sempre e comunque un clone, perch� Internet Explorer altrimenti fa cose strane
        // a basse scale
    	var tmpPoint = this.point.geometry.clone();
    	
    	
    	if(this.distance){   	
    		var scale = this.distance/this.point.geometry.distanceTo(this.refPoint.geometry);
    		tmpPoint.resize(scale,this.refPoint.geometry);  		    		
    	}
    	
    	this.finalPoint.geometry.x = tmpPoint.x;
    	this.finalPoint.geometry.y = tmpPoint.y;
    	this.line.geometry.components[index].x = tmpPoint.x;
    	this.line.geometry.components[index].y = tmpPoint.y; 
    	
    	if(this.infoOnMouse){
    		var d = this.distance?this.distance:(this.refPoint?this.refPoint.geometry.distanceTo(tmpPoint):0);
	    	// Per mettere il popup di informazione sul punto fissato e non sul mouse  
			if(pixel){
				var lonlat = {};
				lonlat.lon = tmpPoint.x;
				lonlat.lat = tmpPoint.y;	    		
	    		pixel = this.control.map.getPixelFromLonLat(lonlat);
			}   	
	    	this.updateInfoPopup(pixel,d);
    	}   	
      
        this.line.geometry.components[index].clearBounds();
    },
    
    /**
     * Method: drawFeature
     * Render geometries on the temporary layer.
     */
    drawFeature: function() {
        
        // se � stato impostato il punto di riferimento e la distanza
        // non mostro pi� il punto sotto il mouse       
        if(this.refPoint && this.distance){
        	this.layer.drawFeature(this.point, {
                strokeOpacity: 0,
                fillOpacity: 0
            });        	       
        }else{
        	this.layer.drawFeature(this.point, this.style);
        }
        this.layer.drawFeature(this.line, this.style);
        
        this.layer.drawFeature(this.refPoint, this.style);
        
        this.layer.drawFeature(this.finalPoint, this.style);
    },

    /**
     * Method: geometryClone
     * Return a clone of the relevant geometry.
     *
     * Returns:
     * {<OpenLayers.Geometry.LineString>}
     */    
    geometryClone: function() {
        return this.finalPoint.geometry.clone();
    },

    /**
     * Method: mousedown
     * Handle mouse down.  Add a new point to the geometry and
     * render it. Return determines whether to propagate the event on the map.
     * 
     * Parameters:
     * evt - {Event} The browser event
     *
     * Returns: 
     * {Boolean} Allow event propagation
     */
    mousedown: function(evt) {
        // ignore double-clicks
        if(!this.drawing) {
            this.createFeature(evt.xy);
        }
        this.mouseDown = true;
        this.lastDown = evt.xy;
        var lonlat = this.control.map.getLonLatFromPixel(evt.xy);

        if(!this.drawing){
        	this.point.geometry.x = lonlat.lon;
        	this.point.geometry.y = lonlat.lat;
        	//this.addPoint();
        	this.addPoint();
        	this.refPoint.geometry.x = lonlat.lon;
        	this.refPoint.geometry.y = lonlat.lat;
          	this.callback("refPoint", [this.point.geometry]);
          	// quando aggiungo il primo punto se c'� una distanza di default la setto
          	if(this.defaultDistance){
            	this.setDistance(this.defaultDistance);
            }
          	this.drawFeature();
        }else if(this.lastUp != null /*&& !this.lastUp.equals(evt.xy)*/){
        	// se la distanza � stata impostata
        	if(this.distance){
        		var scale = this.distance/this.point.geometry.distanceTo(this.refPoint.geometry);
        		this.finalPoint.geometry.x = lonlat.lon;
        		this.finalPoint.geometry.y = lonlat.lat;
        		this.finalPoint.geometry.resize(scale,this.refPoint.geometry);
        		this.toFinalize = true;
            	//this.finalize();
            	return false;            	
        	}
        	// se la distanza non � stata impostata ma � attivo il flag che mi permette di settarla con il click
        	else if(this.drawDistance){
        		//this.distance = this.point.geometry.distanceTo(this.refPoint.geometry);
        		this.setDistance(this.point.geometry.distanceTo(this.refPoint.geometry));             			
        	}
        }                
        this.drawing = true;
        return false;
    },

    /**
     * Method: mousemove
     * Handle mouse move.  Adjust the geometry and redraw.
     * Return determines whether to propagate the event on the map.
     * 
     * Parameters:
     * evt - {Event} The browser event
     *
     * Returns: 
     * {Boolean} Allow event propagation
     */
    mousemove: function (evt) {
        if(this.drawing && !this.mouseDown) { 
            var lonlat = this.map.getLonLatFromPixel(evt.xy);
            this.point.geometry.x = lonlat.lon;
            this.point.geometry.y = lonlat.lat;
            
            var d = 0;
            
            if(this.distance){            	
            	//this.callback("distance", [this.distance]);
            	d = this.distance;
            }else{
            	d = this.point.geometry.distanceTo(this.refPoint.geometry);
            	this.callback("distance", [d]);            	
            }            
            //this.updateInfoPopup(evt.xy,d);
            this.modifyFeature(evt.xy);            
            this.drawFeature();
        }
        return true;
    },
    
    /**
     * Method: mouseup
     * Handle mouse up.  Send the latest point in the geometry to
     * the control. Return determines whether to propagate the event on the map.
     * 
     * Parameters:
     * evt - {Event} The browser event
     *
     * Returns: 
     * {Boolean} Allow event propagation
     */
    mouseup: function (evt) {
        this.mouseDown = false;
        if(this.drawing) {
        	if(this.toFinalize){   
        		// finalizzo sul mouse up e poi comincio a ritrasmettere gli eventi     		
	        	this.finalize();        		
        	}else{
	            this.lastUp = evt.xy;            
        	}
        	return false;
        }
        return true;
    },
    
    keydown: function(evt){
    	if(evt.keyCode == 46 /* Canc */ || (evt.ctrlKey && evt.keyCode == 90) /* Ctrl + z */){
    		if(this.drawing){
    			if(!this.drawDistance || !this.distance){
    				this.cancel();
    			}else{
    				this.setDistance(null);
    				this.callback("distance", [this.point.geometry.distanceTo(this.refPoint.geometry)]);    				
    			}
    		}
    		return false;	
    	}
    	return true;
    },
    
    createInfoPopup: function(lonlat){
		if(this.popup){
			this.destroyInfoPopup();
		}
		this.popup = new OpenLayers.Popup.Anchored("infoOnMouse",lonlat,new OpenLayers.Size(100,30),"",{size : new OpenLayers.Size(0,0), offset : new OpenLayers.Pixel(0,0)});
        this.popup.setOpacity(0.6);
        this.popup.setBorder("solid black 1px");        
        this.control.map.addPopup(this.popup);
        this.updateInfoPopup(null,0);
	},
	
	destroyInfoPopup: function(){
		this.control.map.removePopup(this.popup);
		this.popup.destroy();
		this.popup = null;
	},
	
	updateInfoPopup: function(pixel,distance){
		
		this.popup.hide();      
				
		var html  = "<div>Distance = " + Math.round(distance*100)/100 + "</div>";
		    		    
        this.popup.setContentHTML(html);                
        this.popup.updateSize();
        
        if(pixel){
	        var popupPos = pixel.clone();
	
	        var relPos = this.popup.relativePosition;
	         
	        var relPosX = relPos.charAt(1);
	        var relPosY = relPos.charAt(0);
	
	        var offsetX = 0;
	        var offsetY = 0;
	        
	        if (relPosX == 'l') {
	            offsetX -= 10;
	        } else 
	        if (relPosX == 'r') {
	            offsetX += 10;
	        }        
	        if (relPosY == 't') {
	            offsetY -= 10;
	        } else 
	        if (relPosY == 'b') {
	            offsetY += 10;
	        }
	        
	        this.popup.anchor.offset = new OpenLayers.Pixel(offsetX,offsetY);
	        this.popup.lonlat = this.control.map.getLonLatFromPixel(popupPos);                
        }
        this.popup.show();        
        
	},

    CLASS_NAME: "OpenLayers.Handler.PointFromRef"
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */


/**
 * @requires OpenLayers/Handler/Point.js
 * @requires OpenLayers/Geometry/Point.js
 * @requires OpenLayers/Geometry/LineString.js
 */

/**
 * Class: OpenLayers.Handler.Path
 * Handler to draw a path on the map.  Path is displayed on mouse down,
 * moves on mouse move, and is finished on mouse up.
 *
 * Inherits from:
 *  - <OpenLayers.Handler.Point>
 */
OpenLayers.Handler.ConstrainedPath = OpenLayers.Class(OpenLayers.Handler.Point, {
    
    /**
     * Property: line
     * {<OpenLayers.Feature.Vector>}
     */
    line: null,
    
    /**
     * Property: ring
     * {Boolean} Set the line as closed ring. Default false
     */
    ring: false,
       
    /**
     * Property: infoOnMouse
     * {Boolean} Set the line information as length and angle on a mouse popup. 
     * Default true.
     */
    infoOnMouse: false,
    
    /**
     * Property: relativeAngle
     * {Boolean} Set angles as relative to previous side and not to Horizontal.
     * Default true.
     */
    relativeAngle: true,
        
    /*
     * Costante da non cambiare.
     */
    N_COINCIDING_FIRST_POINT: 2,    
    
    /**
     * Method: activate
     * Activate event observation for the keyboard
     * Returns:
     * {Boolean} true if ok.
     */
    activate: function(){
    	if(OpenLayers.Handler.Point.prototype.activate.apply(this, arguments)) {
    		OpenLayers.Event.observe(document, "keydown", this.eventListener);
            return true;
        }else{
        	return false;
        }
    },
    
    /**
     * Method: deactivate
     * Remove event observation for the keyboard
     * Returns:
     * {Boolean} true if ok.
     */
    deactivate: function() {
    	var deactivated = false;
        if (OpenLayers.Handler.Point.prototype.deactivate.apply(this, arguments)) {
            OpenLayers.Event.stopObserving(document, "keydown", this.eventListener);            
            deactivated = true;
        }
        return deactivated;        
    },
    
    /**
     * Method: setRelativeAngle
     * Set the angle as relative to the previous side. If the
     * parameter is false the angle is relative to horizontal
     * 
     * Parameters:
     * isRelative - {Boolean} 
     */
    setRelativeAngle: function(isRelative){
    	if(isRelative){
    		this.relativeAngle = true;
    	}else{
    		this.relativeAngle = false;
    	}
    },
    
    /**
     * Method: runCommand
     * Check if the command is relative to the previous point
     * and set the next point.
     * 
     * Parameters:
     * command - {String} CAD command
     */
    runCommand: function (command){    	
    	var isRelative = command.charAt(0) == '@'; 
    	if(isRelative){
    		command = command.substring(1);
    	}
    	//if((!command || command=="CH") && this.ring && this.line.geometry.components.length >= (this.N_COINCIDING_FIRST_POINT + 2)){    		
    	if((!command || command=="END")){
    		this.dblclick();
    	}else{
    		this.setNextPoint(command, isRelative);
    	}
    },
    
    setNextPoint: function (command, isRelative){
    	    	    	
    	// modulo ed angolo
    	if(command.indexOf('<') != -1){
    		
    		var modulo_angolo = command.split("<");    		    	
    		this.setNextMA(modulo_angolo[0],modulo_angolo[1]);    		
    		
    	// x e y
    	}else if (command.indexOf(',') != -1){
    		
    		var x_y = command.split(",");
    		this.setNextXY(x_y[0],x_y[1]);    		    		
    		    		
		// solo modulo    		
    	}else{
    		if(!isNaN(command)){
    			this.setNextMA(command,null); 
    		}
    	}
    	/*
    	if(this.drawing){
    		this.modifyFeature(); 
    		this.drawFeature();
    	}
    	this.callback("distance", [this.distance]);
    	*/
    },
    
    setNextMA: function (modulo,angolo){
    	
    	var m = null;
    	var a = null;
    			
		if(modulo){
			if(!isNaN(modulo)){
				m = modulo;
			}    				
		}
		if(angolo){
			if(!isNaN(angolo)){
				a = angolo;
			}    				
		}
		
		this.setConstraint(true,m,a);
    },
    
    setNextXY: function (dx,dy){
    	var x = null;
    	var y = null;
    			
		if(dx){
			if(!isNaN(dx)){
				x = dx;
			}    				
		}
		if(dy){
			if(!isNaN(dy)){
				y = dy;
			}    				
		}
		
		this.setConstraint(false,x,y);
    	
    },
    
    clearConstraint : function(){
    	this.constraint = null;
    },
    
    setConstraint : function(isPolar,firstCoord,secondCoord){
    	this.constraint = {x:null,y:null,modulo:null,angolo:null};
    	this.constraint.isPolar = isPolar;
    	if(isPolar){
    		this.constraint.modulo = firstCoord;
    		this.constraint.angolo = secondCoord;    		    	
    	}else{
    		this.constraint.x = firstCoord;
    		this.constraint.y = secondCoord;    		
    	}
    	
    	// se entrambe i vincoli sono impostati il punto � definito e quindi lo si inserisce
    	if( (this.constraint.modulo && this.constraint.angolo) || (this.constraint.x && this.constraint.y) ){
    			this.updatePoint();
    			//var g = this.point.geometry.clone();
    			var p = new OpenLayers.Pixel(this.point.geometry.x,this.point.geometry.y);
    			var evt = {xy:p};
    			this.mousedown(evt);
    			this.mouseup(evt);
    			/*
    			this.addPoint(p);
    			*/
    			this.clearConstraint();
    	} 
    },
    
	createInfoPopup: function(lonlat){
		if(this.popup){
			this.destroyInfoPopup();
		}
		this.popup = new OpenLayers.Popup.Anchored("infoOnMouse",lonlat,new OpenLayers.Size(100,30),"",{size : new OpenLayers.Size(0,0), offset : new OpenLayers.Pixel(0,0)});
        this.popup.setOpacity(0.6);
        this.popup.setBorder("solid black 1px");        
        this.control.map.addPopup(this.popup);
        this.popup.hide(); 
	},
	
	destroyInfoPopup: function(){
		this.control.map.removePopup(this.popup);
		this.popup.destroy();
		this.popup = null;
	},
	
	updateInfoPopup: function(pixel,distance,angoloRAD){
		
		this.popup.hide();      
		var gradiS = angoloRAD*180/Math.PI;
		var gradi = Math.floor(gradiS);
		var primi = Math.round((gradiS - gradi)*60);
		
		var html  = "<div>Distanza = " + Math.round(distance*100)/100;
		    html += "<br />";
		    html += "Angolo = " + gradi + "<sup>o</sup>" + primi + "<sup>'</sup></div>";
        this.popup.setContentHTML(html);                
        this.popup.updateSize();
        
        var popupPos = pixel.clone();

        var relPos = this.popup.relativePosition;
         
        var relPosX = relPos.charAt(1);
        var relPosY = relPos.charAt(0);

        var offsetX = 0;
        var offsetY = 0;
        
        if (relPosX == 'l') {
            offsetX -= 10;
        } else 
        if (relPosX == 'r') {
            offsetX += 10;
        }        
        if (relPosY == 't') {
            offsetY -= 10;
        } else 
        if (relPosY == 'b') {
            offsetY += 10;
        }
        
        this.popup.anchor.offset = new OpenLayers.Pixel(offsetX,offsetY);
        this.popup.lonlat = this.control.map.getLonLatFromPixel(popupPos);                
        this.popup.show();        
        
	},
	
	updatePoint: function(){
		
		var nPoints = this.line.geometry.components.length;
        var lastPoint = this.line.geometry.components[this.line.geometry.components.length-this.N_COINCIDING_FIRST_POINT];
        
		if(this.constraint != null){
        	if(this.constraint.isPolar){
        		        		
        		if(this.constraint.angolo){
        			
        			var rad = this.constraint.angolo * Math.PI / 180;
        			
        			var d = (this.constraint.modulo)?this.constraint.modulo:this.point.geometry.distanceTo(lastPoint);
        			        			
        			// angolo rispetto all'orizzontale
	        		if(nPoints == this.N_COINCIDING_FIRST_POINT){	        			
	        			this.point.geometry.x = lastPoint.x + ( d * Math.cos(rad));
	        			this.point.geometry.y = lastPoint.y + ( d * Math.sin(rad));
	        		// angolo rispetto a quello del lato precedente	
	        		}else if (nPoints > this.N_COINCIDING_FIRST_POINT){
	        			if(this.relativeAngle){
		        			var dx = lastPoint.x - this.line.geometry.components[this.line.geometry.components.length-(this.N_COINCIDING_FIRST_POINT+1)].x; 
		        			var dy = lastPoint.y - this.line.geometry.components[this.line.geometry.components.length-(this.N_COINCIDING_FIRST_POINT+1)].y;
		        			var a = Math.atan2(dy,dx);
		        			rad += a;
	        			}
	        			this.point.geometry.x = lastPoint.x + ( d * Math.cos(rad));
	        			this.point.geometry.y = lastPoint.y + ( d * Math.sin(rad));
	        		}
	        	// Il modulo � stato messo in else per evitare problemi nel caso in cui il punto sia nella stessa posizione del precedente.
        		}else if(this.constraint.modulo){
        			var scale = this.constraint.modulo/this.point.geometry.distanceTo(lastPoint);        			
    				this.point.geometry.resize(scale,this.line.geometry.components[this.line.geometry.components.length-this.N_COINCIDING_FIRST_POINT]);
        		}
        		
        		        		
        	}else{
        		if (nPoints >= this.N_COINCIDING_FIRST_POINT){
	        		if(this.constraint.x){	  		
	        			this.point.geometry.x = lastPoint.x + Number(this.constraint.x);
	        		}
	        		if(this.constraint.y){
	        			this.point.geometry.y = lastPoint.y + Number(this.constraint.y);
	        		}
        		}
        	}
        }
	},
	
	fromAbsoluteToRelativeAngle: function(rad){
		var nPoints = this.line.geometry.components.length;
        var lastPoint = this.line.geometry.components[this.line.geometry.components.length-this.N_COINCIDING_FIRST_POINT];	
		if (nPoints > this.N_COINCIDING_FIRST_POINT){
			var dx = lastPoint.x - this.line.geometry.components[this.line.geometry.components.length-(this.N_COINCIDING_FIRST_POINT+1)].x; 
			var dy = lastPoint.y - this.line.geometry.components[this.line.geometry.components.length-(this.N_COINCIDING_FIRST_POINT+1)].y;
			var a = Math.atan2(dy,dx);
			rad -= a;
		}
		return rad;
	},
	
	/**
	 * Method: clearFirstPoint
     * Remove first point of the path.
     * Is usefull if first poi is used as reference point
	 */
	clearFirstPoint: function(){
		this.clearFirstOrLastPoint(true);
	},
	
	/**
	 * Method: clearFirstPoint
     * Remove first point of the path.
     * Is usefull if first poi is used as reference point
	 */
	clearLastPoint: function(){
		this.clearFirstOrLastPoint(false);
	},
	
	clearFirstOrLastPoint: function(first){
		if(this.line){
			if(this.line.geometry.components.length > this.N_COINCIDING_FIRST_POINT){
				this.line.geometry.removeComponent(this.line.geometry.getVertices()[first?0:this.line.geometry.getVertices().length-2]);
				this.drawFeature();
			}
		}
	},
	
    /**
     * Constructor: OpenLayers.Handler.Path
     * Create a new path hander
     *
     * Parameters:
     * control - {<OpenLayers.Control>} The control that owns this handler
     * callbacks - {Object} An object with a properties whose values are
     *     functions.  Various callbacks described below.
     * options - {Object} An optional object with properties to be set on the
     *           handler
     *
     * Named callbacks:
     * create - Called when a sketch is first created.  Callback called with
     *     the creation point geometry and sketch feature.
     * modify - Called with each move of a vertex with the vertex (point)
     *     geometry and the sketch feature.
     * point - Called as each point is added.  Receives the new point geometry.
     * done - Called when the point drawing is finished.  The callback will
     *     recieve a single argument, the linestring geometry.
     * cancel - Called when the handler is deactivated while drawing.  The
     *     cancel callback will receive a geometry.
     */
    initialize: function(control, callbacks, options) {    	
        OpenLayers.Handler.Point.prototype.initialize.apply(this, arguments);
        // cache the bound event listener method so it can be unobserved later
        this.eventListener = OpenLayers.Function.bindAsEventListener(
            this.keydown, this
        );
    },
        
    /**
     * Method: createFeature
     * Add temporary geometries
     *
     * Parameters:
     * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
     *     feature.
     */
    createFeature: function(pixel) {
        var lonlat = this.control.map.getLonLatFromPixel(pixel);
        this.point = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
        );
        this.line = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.LineString([this.point.geometry])
        );
        
        if(this.infoOnMouse){
        	this.createInfoPopup(lonlat);
        }        
        this.callback("create", [this.point.geometry, this.getSketch()]);
        this.point.geometry.clearBounds();
        this.layer.addFeatures([this.line, this.point], {silent: true});
//        this.setConstraint(true,null,-90);
    },
        
    /**
     * Method: destroyFeature
     * Destroy temporary geometries
     */
    destroyFeature: function() {
        OpenLayers.Handler.Point.prototype.destroyFeature.apply(this);
        this.line = null;
        this.drawing = false;
        if(this.popup){
        	this.destroyInfoPopup();
        }          
    },

    /**
     * Method: removePoint
     * Destroy the temporary point.
     */
    removePoint: function() {
        if(this.point) {
            this.layer.removeFeatures([this.point]);
        }
    },
    
    /**
     * Method: addPoint
     * Add point to geometry.  Send the point index to override
     * the behavior of LinearRing that disregards adding duplicate points.
     *
     * Parameters:
     * pixel - {<OpenLayers.Pixel>} The pixel location for the new point.
     */
    addPoint: function(pixel) {
        this.layer.removeFeatures([this.point]);
        var lonlat = this.control.map.getLonLatFromPixel(pixel);
        this.point = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
        );
        this.line.geometry.addComponent(
            this.point.geometry, this.line.geometry.components.length
        );
        
        if(this.line.geometry.components.length == this.N_COINCIDING_FIRST_POINT){
        	this.callback("firstPoint", [this.point.geometry, this.getGeometry()]);        	        	
        }
        this.callback("point", [this.point.geometry, this.getGeometry()]);
        this.callback("modify", [this.point.geometry, this.getSketch()]);
        this.clearConstraint();
        this.drawFeature();
        this.callback("aftermodify", [this.point.geometry, this.getSketch()]);
    },
    
    /**
     * Method: freehandMode
     * Determine whether to behave in freehand mode or not.
     *
     * Returns:
     * {Boolean}
     */
     /*
    freehandMode: function(evt) {
        return (this.freehandToggle && evt[this.freehandToggle]) ?
                    !this.freehand : this.freehand;
    },
    * */

    /**
     * Method: modifyFeature
     * Modify the existing geometry given the new point
     *
     * Parameters:
     * pixel - {<OpenLayers.Pixel>} The updated pixel location for the latest
     *     point.
     */
    modifyFeature: function(pixel) {
    	if(!this.point){
    		this.createFeature(pixel);
    	}
    	
        var lonlat = this.control.map.getLonLatFromPixel(pixel);
        this.point.geometry.x = lonlat.lon;
        this.point.geometry.y = lonlat.lat;
        
        var nPoints = this.line.geometry.components.length;
        
        var lastPoint = this.line.geometry.components[this.line.geometry.components.length-this.N_COINCIDING_FIRST_POINT];
        this.updatePoint();
        
        this.callback("modify", [this.point.geometry, this.getSketch()]);
        
        if(this.point.geometry && lastPoint){
	        var distance = this.point.geometry.distanceTo(lastPoint);
	        if(this.infoOnMouse){        	
	        	var angolo = 0;
	        	if(this.constraint && this.constraint.angolo){
	        		angolo = this.constraint.angolo*Math.PI/180;
	        	}else{
	        		var dx = this.point.geometry.x - lastPoint.x; 
		        	var dy = this.point.geometry.y - lastPoint.y;
		        	var a = Math.atan2(dy,dx);
		        	angolo = a;
	        	}
	        	this.updateInfoPopup(pixel,this.point.geometry.distanceTo(lastPoint),this.relativeAngle?this.fromAbsoluteToRelativeAngle(angolo):angolo);
	        }
	        this.callback("distance", [distance]);
        }        
        
        this.point.geometry.clearBounds();
        this.drawFeature();
        this.callback("aftermodify", [this.point.geometry, this.getSketch()]);
    },

    /**
     * Method: drawFeature
     * Render geometries on the temporary layer.
     */
    drawFeature: function() {
        this.layer.drawFeature(this.line, this.style);
        this.layer.drawFeature(this.point, this.style);        
        //this.layer.drawFeature(new OpenLayers.Feature.Vector(this.line.geometry.components[this.line.geometry.components.length-this.N_COINCIDING_FIRST_POINT]), this.style);
    },

    /**
     * Method: getSketch
     * Return the sketch feature.
     *
     * Returns:
     * {<OpenLayers.Feature.Vector>}
     */
    getSketch: function() {
        return this.line;
    },
    
    /*
    geometryClone: function() {
        //return this.line.geometry.components[this.line.geometry.components.length-this.N_COINCIDING_FIRST_POINT].clone();
        return this.point.geometry.clone();
    },
    */

    /**
     * Method: getGeometry
     * Return the sketch geometry.  If <multi> is true, this will return
     *     a multi-part geometry.
     *
     * Returns:
     * {<OpenLayers.Geometry.LineString>}
     */
    getGeometry: function() {
        var geometry = this.line && this.line.geometry;
        if(geometry && this.multi) {
            geometry = new OpenLayers.Geometry.MultiLineString([geometry]);
        }
        return geometry;
    },
    
    drawFirstPoint: function(lon,lat){
    	if(this.lastDown)
    		return;
    	var pixel = this.control.map.getPixelFromLonLat(new OpenLayers.LonLat(lon,lat));
    	var evt = {xy:pixel};
    	this.mousedown(evt);
    	this.mouseup(evt);
    	this.mousemove(evt);
    },
    
    undo: function(){
    	if(this.drawing){
    		if(this.constraint){
    			this.clearConstraint();
    		}else if(this.line.geometry.components.length > this.N_COINCIDING_FIRST_POINT){
    			this.clearLastPoint();
    		}else if(this.line.geometry.components.length <= this.N_COINCIDING_FIRST_POINT){
    			this.removePoint();
    			this.cancel();
    		}
    	}    	
    },

    /**
     * Method: mousedown
     * Handle mouse down.  Add a new point to the geometry and
     * render it. Return determines whether to propagate the event on the map.
     * 
     * Parameters:
     * evt - {Event} The browser event
     *
     * Returns: 
     * {Boolean} Allow event propagation
     */
    mousedown: function(evt) {
        // ignore double-clicks    	
    	
        if (this.lastDown && this.lastDown.equals(evt.xy)) {
            return false;
        }
        if(this.lastDown == null) {
            if(this.persist) {
                this.destroyFeature();
            }
            //this.createFeature(evt.xy);
        } else if((this.lastUp == null) || !this.lastUp.equals(evt.xy)) {
            this.addPoint(evt.xy);
        }
        this.mouseDown = true;
        this.lastDown = evt.xy;
        this.drawing = true;
        return false;
    },

    /**
     * Method: mousemove
     * Handle mouse move.  Adjust the geometry and redraw.
     * Return determines whether to propagate the event on the map.
     * 
     * Parameters:
     * evt - {Event} The browser event
     *
     * Returns: 
     * {Boolean} Allow event propagation
     */
    mousemove: function (evt) {
       // if(this.drawing) { 
            //if(this.mouseDown && this.freehandMode(evt)) {
            //    this.addPoint(evt.xy);
            //} else {
                this.modifyFeature(evt.xy);
            //}
       // }
        return true;
    },
    
    /**
     * Method: mouseup
     * Handle mouse up.  Send the latest point in the geometry to
     * the control. Return determines whether to propagate the event on the map.
     * 
     * Parameters:
     * evt - {Event} The browser event
     *
     * Returns: 
     * {Boolean} Allow event propagation
     */
    mouseup: function (evt) {
        this.mouseDown = false;
        if(this.drawing) {
            //if(this.freehandMode(evt)) {
             //   this.removePoint();
            //    this.finalize();
           // } else {
                if(this.lastUp == null) {
                   this.addPoint(evt.xy);
                }
                this.lastUp = evt.xy;
            //}
            return false;
        }
        return true;
    },
  
    /**
     * Method: dblclick 
     * Handle double-clicks.  Finish the geometry and send it back
     * to the control.
     * 
     * Parameters:
     * evt - {Event} The browser event
     *
     * Returns: 
     * {Boolean} Allow event propagation
     */
    dblclick: function(evt) {

        var index = this.line.geometry.components.length - 1;
        this.line.geometry.removeComponent(this.line.geometry.components[index]);
        if(this.ring){
        	this.line = new OpenLayers.Feature.Vector(
	          new OpenLayers.Geometry.LinearRing(this.line.geometry.getVertices())
	        );
	        this.layer.addFeatures([this.line], {silent: true});
	        this.drawFeature();
        }
        this.removePoint();
        this.finalize();

        return false;
    },
    
    keydown: function(evt){
    	if(evt.keyCode == 46 /* Canc */ || (evt.ctrlKey && evt.keyCode == 90) /* Ctrl + z */){
    		this.undo();
    		return false;	
    	} else if (evt.ctrlKey && evt.keyCode == 13) {
    		this.dblclick();
    		return false;
    	}    	
    	return true;
    },

    CLASS_NAME: "OpenLayers.Handler.ConstrainedPath"
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */


/**
 * @requires OpenLayers/Handler/Path.js
 * @requires OpenLayers/Geometry/Polygon.js
 */

/**
 * Class: OpenLayers.Handler.Polygon
 * Handler to draw a polygon on the map.  Polygon is displayed on mouse down,
 * moves on mouse move, and is finished on mouse up.
 *
 * Inherits from:
 *  - <OpenLayers.Handler.Path>
 *  - <OpenLayers.Handler>
 */
OpenLayers.Handler.ConstrainedPoint = OpenLayers.Class(OpenLayers.Handler.ConstrainedPath, {	
	
	/**
     * Constructor: OpenLayers.Handler.ConstrainedPoint
     * Create a Constrained Point Handler.
     *
     * Parameters:
     * control - {<OpenLayers.Control>} The control that owns this handler
     * callbacks - {Object} An object with a properties whose values are
     *     functions.  Various callbacks described below.
     * options - {Object} An optional object with properties to be set on the
     *           handler
     *
     * Named callbacks:
     * firstPoint - Called when the first point is first created.  Callback called with
     *     the creation point geometry.
     * distance - Called with each move of a vertex with the distance from the last point.
     */
    initialize: function(control, callbacks, options) {
        OpenLayers.Handler.ConstrainedPath.prototype.initialize.apply(this, arguments);
        this.ring = false;
    },
	
    /**
     * Method: getGeometry
     *
     * Returns:
     * {<OpenLayers.Geometry.Polygon>}
     */		
    getGeometry: function() {  
    	var geometry = this.line && this.line.geometry;
    	if(geometry){
        	geometry = this.line.geometry.components[this.line.geometry.components.length-1];
    	}
    	return geometry;
    },
   

    CLASS_NAME: "OpenLayers.Handler.ConstrainedPoint"
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */


/**
 * @requires OpenLayers/Handler/Path.js
 * @requires OpenLayers/Geometry/Polygon.js
 */

/**
 * Class: OpenLayers.Handler.Polygon
 * Handler to draw a polygon on the map.  Polygon is displayed on mouse down,
 * moves on mouse move, and is finished on mouse up.
 *
 * Inherits from:
 *  - <OpenLayers.Handler.Path>
 *  - <OpenLayers.Handler>
 */
OpenLayers.Handler.ConstrainedPolygon = OpenLayers.Class(OpenLayers.Handler.ConstrainedPath, {
    
    /**
     * Parameter: polygon
     * {<OpenLayers.Feature.Vector>}
     */
    polygon: null,
    
    N_COINCIDING_FIRST_POINT: 3,

    /**
     * Constructor: OpenLayers.Handler.ConstrainedPolygon
     * Create a Polygon Handler.
     *
     * Parameters:
     * control - {<OpenLayers.Control>} The control that owns this handler
     * callbacks - {Object} An object with a properties whose values are
     *     functions.  Various callbacks described below.
     * options - {Object} An optional object with properties to be set on the
     *           handler
     *
     * Named callbacks:
     * create - Called when a sketch is first created.  Callback called with
     *     the creation point geometry and sketch feature.
     * modify - Called with each move of a vertex with the vertex (point)
     *     geometry and the sketch feature.
     * point - Called as each point is added.  Receives the new point geometry.
     * done - Called when the point drawing is finished.  The callback will
     *     recieve a single argument, the polygon geometry.
     * cancel - Called when the handler is deactivated while drawing.  The
     *     cancel callback will receive a geometry.
     */
    initialize: function(control, callbacks, options) {
        OpenLayers.Handler.ConstrainedPath.prototype.initialize.apply(this, arguments);
        this.ring = true;
    },
    
    /**
     * Method: createFeature
     * Add temporary geometries
     *
     * Parameters:
     * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
     *     feature.
     */
    createFeature: function(pixel) {
        var lonlat = this.control.map.getLonLatFromPixel(pixel);
        this.point = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
        );
        var lr = new OpenLayers.Geometry.LinearRing([this.point.geometry]);
        
        // Sovrascrivo il removeComponent per poter eliminare anche i punti
        // che il LinearRing non permette
        lr.removeComponent = function(point) {
	        if (this.components.length > 3) {
	
	            //remove last point
	            this.components.pop();
	            
	            //remove our point
	            OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, 
	                                                                    arguments);
	            //append copy of first point
	            var firstPoint = this.components[0];
	            OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, 
	                                                                [firstPoint]);
	        }
    	}; 
        this.line = new OpenLayers.Feature.Vector(
          lr 
        );
        this.polygon = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Polygon([this.line.geometry])
        );
        if(this.infoOnMouse){
        	this.createInfoPopup(lonlat);
        }
        this.callback("create", [this.point.geometry, this.getSketch()]);
        this.point.geometry.clearBounds();
        this.layer.addFeatures([this.polygon, this.point], {silent: true});
    },

    /**
     * Method: destroyFeature
     * Destroy temporary geometries
     */
    destroyFeature: function() {
        OpenLayers.Handler.ConstrainedPath.prototype.destroyFeature.apply(this);
        this.polygon = null;
    },

    /**
     * Method: drawFeature
     * Render geometries on the temporary layer.
     */
    drawFeature: function() {
        this.layer.drawFeature(this.polygon, this.style);
        this.layer.drawFeature(this.point, this.style);
    },
    
    /**
     * Method: getSketch
     * Return the sketch feature.
     *
     * Returns:
     * {<OpenLayers.Feature.Vector>}
     */
    getSketch: function() {
        return this.polygon;
    },

    /**
     * Method: getGeometry
     * Return the sketch geometry.  If <multi> is true, this will return
     *     a multi-part geometry.
     *
     * Returns:
     * {<OpenLayers.Geometry.Polygon>}
     */
    getGeometry: function() {
        var geometry = this.polygon && this.polygon.geometry;
        if(geometry && this.multi) {
            geometry = new OpenLayers.Geometry.MultiPolygon([geometry]);
        }
        return geometry;
    },

    /**
     * Method: dblclick
     * Handle double-clicks.  Finish the geometry and send it back
     * to the control.
     * 
     * Parameters:
     * evt - {Event} 
     */
    dblclick: function(evt) {
        //if(!this.freehandMode(evt)) {
            // remove the penultimate point
            var index = this.line.geometry.components.length - 2;
            this.line.geometry.removeComponent(this.line.geometry.components[index]);
            this.removePoint();
            this.finalize();
        //}
        return false;
    },

    CLASS_NAME: "OpenLayers.Handler.ConstrainedPolygon"
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @requires OpenLayers/Control.js
 */

/**
 * Class: OpenLayers.Control.Attribution
 * Controllo che aggiunge in mappa le attribuzioni gestendo la possibilità che un singolo layer openlayers 
 * visualizzi più di un layer con diversa attribuzione, caso possibile quando vengono raggruppate le richieste alla stesso wms
 * Utilizza la prorietà 'attribution' di ogni layer, che può essere anche un array.
 *
 * Inherits from:
 *  - <OpenLayers.Control>
 */
OpenLayers.Control.AttributionMulti = 
  OpenLayers.Class(OpenLayers.Control, {
    
	  
  /**
     * APIProperty: separator
     * {String} String used to separate layers.
     */
    separator: ", ",
    
    /**
     * APIProperty: template
     * {String} Template for the attribution. This has to include the substring
     *     "${layers}", which will be replaced by the layer specific
     *     attributions, separated by <separator>. The default is "${layers}".
     */
    template: "${layers}",
    
    /**
     * Constructor: OpenLayers.Control.Attribution 
     * 
     * Parameters:
     * options - {Object} Options for control.
     */
	  
    
    /**
     * Constructor: OpenLayers.Control.AttributionMulti 
     * 
     * Parameters:
     * options - {Object} Options for control.
     */
	  
    /** 
     * Method: destroy
     * Destroy control.
     */
    destroy: function() {
        this.map.events.un({
            "removelayer": this.updateAttribution,
            "addlayer": this.updateAttribution,
            "changelayer": this.updateAttribution,
            "changebaselayer": this.updateAttribution,
            scope: this
        });
        
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },    
    
    /**
     * Method: draw
     * Initialize control.
     * 
     * Returns: 
     * {DOMElement} A reference to the DIV DOMElement containing the control
     */    
    draw: function() {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        
        this.map.events.on({
            'changebaselayer': this.updateAttribution,
            'changelayer': this.updateAttribution,
            'addlayer': this.updateAttribution,
            'removelayer': this.updateAttribution,
            scope: this
        });
        this.updateAttribution();
        
        return this.div;    
    },

	  /**
     * Method: updateAttribution
     * Aggiorna l'attribuzione
     */
    updateAttribution: function() {
    	 
        var attributions = [];
        if (this.map && this.map.layers) {
            for(var i=0, len=this.map.layers.length; i<len; i++) {
                var layer = this.map.layers[i];
                if (layer.attribution && layer.getVisibility()) {
                	// controlla se ci sono più attribuzioni per questo layer
                	if (layer.attribution.constructor === Array) {
                		for (var j=0; j<layer.attribution.length; j++) {
                			var buff = "";
                			if (typeof layer.attribution[j] === 'string') {
                				buff = layer.attribution[j];	
                        	} else {
                        		buff = layer.attribution[j].title;
                        	}
	                		// add attribution only if attribution text is unique
	                        if (OpenLayers.Util.indexOf(
	                                        attributions, buff) === -1) {
	                            attributions.push( buff );
	                        }
                		}
                	} else {
                		// add attribution only if attribution text is unique
                		var buff = "";
            			if (typeof layer.attribution === 'string') {
            				buff = layer.attribution;
                    	} else {
                    		buff = layer.attribution.title;
                    	}
                        if (OpenLayers.Util.indexOf(
                                        attributions, buff) === -1) {
                        	attributions.push(buff);
                        }	
                	}
                }
            } 
            this.div.innerHTML = OpenLayers.String.format(this.template, {
                layers: attributions.join(this.separator)
            });
        }
    },

    CLASS_NAME: "OpenLayers.Control.AttributionMulti"
});/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/*
 * Fix OpenLayers IE
 */
if(document.namespaces == null){document.createElement('namespaces');}

/*
 * Fix vari non contenuto nella public release
 */
Ext.namespace("TolomeoExt");

//Corregge il fatto che la window "ruba" il focus di un eventuale campo di una form .Per esempio in windowToponomastica
/*Ext.override(Ext.Window, {
    toFront : function(e){
        if(this.manager.bringToFront(this)){
            if(e && !e.getTarget().focus){
                this.focus();
            }
        }
        return this;
    }
});
*/

/**
 * @method applyIfEmpty
 * Fix per diverso comportamento applyIf tra 3.0 e 3.1
 * 
 * Parameters:
 * o - .
 * c - .
 */	
TolomeoExt.applyIfEmpty = function (o,c) {
	if(o){
        for(var p in c){
        	//o["aaa"] = 'ale';
        	//o["TOLOMEOServer"] = c["TOLOMEOServer"];
        	
        	if (Ext.isEmpty(o[p])) {
                o[p] = c[p];
            //    alert(c[p]);
            }
        }
    }
}


if(Ext.isIE8) {
	Ext.form.ComboBox.prototype.shadow = false;
    Ext.Panel.prototype.shadow = false;
    Ext.menu.Menu.prototype.shadow = false;
    Ext.ToolTip.prototype.floating.shadow = false;
    Ext.ToolTip.prototype.floating.shim = true;
    Ext.ToolTip.prototype.floating.useDisplay = true;
    Ext.ToolTip.prototype.floating.constrain = false;
}

/*
 * Ovveride per connessioni SSL
 */
if(Ext.isSecure) {
	Ext.BLANK_IMAGE_URL= "https://pratomaps.comune.prato.it/img/pixel.gif";
	Ext.chart.Chart.CHART_URL = "/js/images/blankfile";
	Ext.FlashComponent.EXPRESS_INSTALL_URL = "/js/images/blankfile"
	Ext.SSL_SECURE_URL='javascript:false';
}else{
	Ext.BLANK_IMAGE_URL= "http://pratomaps.comune.prato.it/img/pixel.gif";
}


OpenLayers.DOTS_PER_INCH = 25.4 / 0.28;

/*
 * Cambio la risoluzione in base al sistema operativo
 */
/*
if(Ext.isWindows) {
	OpenLayers.DOTS_PER_INCH = 96;
} else if (Ext.isMac) {
	OpenLayers.DOTS_PER_INCH = 72;
} else if (Ext.isLinux) {
	OpenLayers.DOTS_PER_INCH = 72;
}
*/


/**
 * Sostituisce un parametro di una url con un altro
 * 
 * @param url - url nella quale viene effettuata la sostituzione
 * @param paramname - nome del parametro
 * @param paramvalue - nuovo valore del parametro
 * 
 */

TolomeoExt.urlAddOrChangeParameter = function (url, paramname, paramvalue) {

	var inizio = 0;
	var pre = "";
	var post = "";
	
	if (url.indexOf(paramname+"=")!=-1) {
		pre = url.substring(0,url.indexOf(paramname+"="));
		inizio = url.indexOf(paramname+"=");
		if (url.indexOf("&",inizio)!=-1) {
			var fine = url.indexOf("&",inizio);
			post = url.substring(fine); 
		} 
	} else {
		pre = url;
	}
	
	return pre + paramname+'=' + paramvalue + post;
	
}


/**
 * @method loadScript
 * Copyright (C) 2006-2007 Dao Gottwald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Contact information:
 *   Dao Gottwald  <dao@design-noir.de>
 *
 * version  1.6
 * url      http://design-noir.de/webdev/JS/loadScript/
 */
function loadScript(url, callback) {
	var f = arguments.callee;
	if (!("queue" in f))
		f.queue = {};
	var queue =  f.queue;
	if (url in queue) { // script is already in the document
		if (callback) {
			if (queue[url]) // still loading
				queue[url].push(callback);
			else // loaded
				callback();
		}
		return;
	}
	queue[url] = callback ? [callback] : [];
	var script = document.createElement("script");
	script.type = "text/javascript";
	script.onload = script.onreadystatechange = function() {
		if (script.readyState && script.readyState != "loaded" && script.readyState != "complete")
			return;
		script.onreadystatechange = script.onload = null;
		while (queue[url].length)
			queue[url].shift()();
		queue[url] = null;
	};
	script.src = url;
	document.getElementsByTagName("head")[0].appendChild(script);
}
/* Esempio di utilizzo
 * 
   <html>
	<head>
		<title>Test</title>
	</head>
	<body>
		<script type="text/javacript" src="scriptloader.js"></script>
		<script type="text/javascript">
			alert("Start");

			loadScript("otherscript.js",function() {
				// Call something in other script
				DoOther();
			});

			alert("Stop");
		</script>
	</body>
  </html>
 * 
 * 
 * 
 * 
 * 
 * 
 */ 

// FIX del fatto che IE<=8 non implementa la funzione standard degli array indexOf
if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function(obj, start) {
	     for (var i = (start || 0), j = this.length; i < j; i++) {
	         if (this[i] === obj) { return i; }
	     }
	     return -1;
	}
}

// Production steps of ECMA-262, Edition 6, 22.1.2.1
if (!Array.from) {
  Array.from = (function () {
    var toStr = Object.prototype.toString;
    var isCallable = function (fn) {
      return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
    };
    var toInteger = function (value) {
      var number = Number(value);
      if (isNaN(number)) { return 0; }
      if (number === 0 || !isFinite(number)) { return number; }
      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
    };
    var maxSafeInteger = Math.pow(2, 53) - 1;
    var toLength = function (value) {
      var len = toInteger(value);
      return Math.min(Math.max(len, 0), maxSafeInteger);
    };

    // The length property of the from method is 1.
    return function from(arrayLike/*, mapFn, thisArg */) {
      // 1. Let C be the this value.
      var C = this;

      // 2. Let items be ToObject(arrayLike).
      var items = Object(arrayLike);

      // 3. ReturnIfAbrupt(items).
      if (arrayLike == null) {
        throw new TypeError('Array.from requires an array-like object - not null or undefined');
      }

      // 4. If mapfn is undefined, then let mapping be false.
      var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
      var T;
      if (typeof mapFn !== 'undefined') {
        // 5. else
        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
        if (!isCallable(mapFn)) {
          throw new TypeError('Array.from: when provided, the second argument must be a function');
        }

        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
        if (arguments.length > 2) {
          T = arguments[2];
        }
      }

      // 10. Let lenValue be Get(items, "length").
      // 11. Let len be ToLength(lenValue).
      var len = toLength(items.length);

      // 13. If IsConstructor(C) is true, then
      // 13. a. Let A be the result of calling the [[Construct]] internal method 
      // of C with an argument list containing the single item len.
      // 14. a. Else, Let A be ArrayCreate(len).
      var A = isCallable(C) ? Object(new C(len)) : new Array(len);

      // 16. Let k be 0.
      var k = 0;
      // 17. Repeat, while k < len... (also steps a - h)
      var kValue;
      while (k < len) {
        kValue = items[k];
        if (mapFn) {
          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
        } else {
          A[k] = kValue;
        }
        k += 1;
      }
      // 18. Let putStatus be Put(A, "length", len, true).
      A.length = len;
      // 20. Return A.
      return A;
    };
  }());
}


// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
	
  Array.prototype.map = function(callback, thisArg) {

    var T, A, k;

    if (this == null) {
      throw new TypeError(" this is null or not defined");
    }

    // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
    // 3. Let len be ToUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If IsCallable(callback) is false, throw a TypeError exception.
    // See: http://es5.github.com/#x9.11
    if (typeof callback !== "function") {
      throw new TypeError(callback + " is not a function");
    }

    // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
    if (thisArg) {
      T = thisArg;
    }

    // 6. Let A be a new array created as if by the expression new Array(len) where Array is
    // the standard built-in constructor with that name and len is the value of len.
    A = new Array(len);

    // 7. Let k be 0
    k = 0;

    // 8. Repeat, while k < len
    while(k < len) {

      var kValue, mappedValue;

      // a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
        kValue = O[ k ];

        // ii. Let mappedValue be the result of calling the Call internal method of callback
        // with T as the this value and argument list containing kValue, k, and O.
        mappedValue = callback.call(T, kValue, k, O);

        // iii. Call the DefineOwnProperty internal method of A with arguments
        // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
        // and false.

        // In browsers that support Object.defineProperty, use the following:
        // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });

        // For best browser support, use the following:
        A[ k ] = mappedValue;
      }
      // d. Increase k by 1.
      k++;
    }

    // 9. return A
    return A;
  };      
}


if (!Array.prototype.reduce) {
	
  Array.prototype.reduce = function(callback /*, initialValue*/) {
  	
      if (this === null) {
        throw new TypeError( 'Array.prototype.reduce ' + 
          'called on null or undefined' );
      }
      if (typeof callback !== 'function') {
        throw new TypeError( callback +
          ' is not a function');
      }

      // 1. Let O be ? ToObject(this value).
      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0; 

      // Steps 3, 4, 5, 6, 7      
      var k = 0; 
      var value;

      if (arguments.length >= 2) {
        value = arguments[1];
      } else {
        while (k < len && !(k in o)) {
          k++; 
        }

        // 3. If len is 0 and initialValue is not present,
        //    throw a TypeError exception.
        if (k >= len) {
          throw new TypeError( 'Reduce of empty array ' +
            'with no initial value' );
        }
        value = o[k++];
      }

      // 8. Repeat, while k < len
      while (k < len) {
        // a. Let Pk be ! ToString(k).
        // b. Let kPresent be ? HasProperty(O, Pk).
        // c. If kPresent is true, then
        //    i.  Let kValue be ? Get(O, Pk).
        //    ii. Let accumulator be ? Call(
        //          callbackfn, undefined,
        //          « accumulator, kValue, k, O »).
        if (k in o) {
          value = callback(value, o[k], k, o);
        }

        // d. Increase k by 1.      
        k++;
      }

      // 9. Return accumulator.
      return value;
    }
}

// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {

  Array.prototype.forEach = function(callback/*, thisArg*/) {

    var T, k;

    if (this == null) {
      throw new TypeError('this is null or not defined');
    }

    // 1. Let O be the result of calling toObject() passing the
    // |this| value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get() internal
    // method of O with the argument "length".
    // 3. Let len be toUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If isCallable(callback) is false, throw a TypeError exception. 
    // See: http://es5.github.com/#x9.11
    if (typeof callback !== 'function') {
      throw new TypeError(callback + ' is not a function');
    }

    // 5. If thisArg was supplied, let T be thisArg; else let
    // T be undefined.
    if (arguments.length > 1) {
      T = arguments[1];
    }

    // 6. Let k be 0
    k = 0;

    // 7. Repeat, while k < len
    while (k < len) {

      var kValue;

      // a. Let Pk be ToString(k).
      //    This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty
      //    internal method of O with argument Pk.
      //    This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal
        // method of O with argument Pk.
        kValue = O[k];

        // ii. Call the Call internal method of callback with T as
        // the this value and argument list containing kValue, k, and O.
        callback.call(T, kValue, k, O);
      }
      // d. Increase k by 1.
      k++;
    }
    // 8. return undefined
  };
}

// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
  Object.keys = (function () {
    'use strict';
    var hasOwnProperty = Object.prototype.hasOwnProperty,
        hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
        dontEnums = [
          'toString',
          'toLocaleString',
          'valueOf',
          'hasOwnProperty',
          'isPrototypeOf',
          'propertyIsEnumerable',
          'constructor'
        ],
        dontEnumsLength = dontEnums.length;

    return function (obj) {
      if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
        throw new TypeError('Object.keys called on non-object');
      }

      var result = [], prop, i;

      for (prop in obj) {
        if (hasOwnProperty.call(obj, prop)) {
          result.push(prop);
        }
      }

      if (hasDontEnumBug) {
        for (i = 0; i < dontEnumsLength; i++) {
          if (hasOwnProperty.call(obj, dontEnums[i])) {
            result.push(dontEnums[i]);
          }
        }
      }
      return result;
    };
  }());
}

if (!window.JSON) {
  window.JSON = {
    parse: function(sJSON) { return eval('(' + sJSON + ')'); },
    stringify: (function () {
      var toString = Object.prototype.toString;
      var isArray = Array.isArray || function (a) { return toString.call(a) === '[object Array]'; };
      var escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'};
      var escFunc = function (m) { return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1); };
      var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g;
      return function stringify(value) {
        if (value == null) {
          return 'null';
        } else if (typeof value === 'number') {
          return isFinite(value) ? value.toString() : 'null';
        } else if (typeof value === 'boolean') {
          return value.toString();
        } else if (typeof value === 'object') {
          if (typeof value.toJSON === 'function') {
            return stringify(value.toJSON());
          } else if (isArray(value)) {
            var res = '[';
            for (var i = 0; i < value.length; i++)
              res += (i ? ', ' : '') + stringify(value[i]);
            return res + ']';
          } else if (toString.call(value) === '[object Object]') {
            var tmp = [];
            for (var k in value) {
              if (value.hasOwnProperty(k))
                tmp.push(stringify(k) + ': ' + stringify(value[k]));
            }
            return '{' + tmp.join(', ') + '}';
          }
        }
        return '"' + value.toString().replace(escRE, escFunc) + '"';
      };
    })()
  };
}

if(!String.format){
	String.format = function(format) {
	    var args = Array.prototype.slice.call(arguments, 1);
	    return format.replace(/{(\d+)}/g, function(match, number) { 
	      return typeof args[number] != 'undefined'
	        ? args[number] 
	        : match
	      ;
	    });
	};
}/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.namespace("TolomeoExt.Vars");
	
/**
 * Function: TolomeoExt.Vars.ApplyIfDefaults
 *
 * Parameters:
 * obj - {} obj
 */
TolomeoExt.Vars.ApplyIfDefaults = function(obj) {
	
	// passando alla versione 3.1 di ext la applyIf agisce se le proprieta' non sono definite, mentre prima lo faceva anche se erano null
	// per questo ho combiato con il codice esplicito sotto riportato 
	TolomeoExt.applyIfEmpty(obj,
					{ TOLOMEOServer: 		 TolomeoExt.Vars.TOLOMEOServer,
					  TOLOMEOContext: 		 TolomeoExt.Vars.TOLOMEOContext,
					  TOLOMEOStaticRoot: 	 TolomeoExt.Vars.TOLOMEOStaticRoot,
					  TOLOMEOServerSettings: TolomeoExt.Vars.TOLOMEOServerSettings,
					  paramsJS: TolomeoExt.Vars.paramsJS
					});
	/*var o = obj;
	var c = { TOLOMEOServer: TolomeoExt.Vars.TOLOMEOServer,
			  TOLOMEOContext: TolomeoExt.Vars.TOLOMEOContext,
			  paramsJS: TolomeoExt.Vars.paramsJS
			};
					
    if(o){
        for(var p in c){
        	//o["aaa"] = 'ale';
        	//o["TOLOMEOServer"] = c["TOLOMEOServer"];
        	
        	if (Ext.isEmpty(o[p])) {
                o[p] = c[p];
            //    alert(c[p]);
            }
        }
    }*/					
}

Ext.define('TolomeoExt.ToloI18n', {
	extend: 'Ext.util.Observable', 
	requires: [
        'Ext.app.Application'        
	],

	//@private
	defaultLanguage: 'it',

	constructor: function(config){
		config = config || {};
		this.loaded = false;

		var me = this;
		this.language = me.formatLanguageCode(config.lang || me.guessLanguage());
		
		this.addEvents('loaded');
		
		this.callParent(arguments);
	},
	
	setLanguage: function(lang){
		this.language = lang;
		this.fireEvent('languageChanged', lang);
	},
	
	getLanguage: function(){
		return this.language;
	
	},
	
	load: function(){
		// Applico in questo momento e non nel costruttore perchè questo oggetto viene creato subito, 
		// quindi prima che i valori corretti siano disponibili
		
		if(this.isLoaded()) return;
				
		var self = this;
		
		if(!(TolomeoExt.Vars && TolomeoExt.Vars.ApplyIfDefaults && TolomeoExt.Vars.TOLOMEOServer)) {
			setTimeout(function(){self.load.call(self)},500);
			return;
		}
		
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
		var url =  this.TOLOMEOServer + this.TOLOMEOStaticRoot + '/js/ext/extJS/locale/ext-lang-'+ this.language + '.js';
		
		Ext.Loader.loadScript({
            url: url,
            onLoad: this.loadPart2,
            onError: this.onFailure,
            scope: this
        });
	}, 
	
	loadPart2: function(){
		
		var url = this.TOLOMEOServer + this.TOLOMEOContext + "/AjaxI18nServlet";
		new TolomeoExt.ToloCrossAjax().request({
			method: 'get',
			url: url,
			params: {
				lang: this.language,
				format: 'ext'
			},
			success: this.loadSuccess,
			failure: function(transport){
				var errText = transport.responseText ? transport.responseText : ""+transport;
				alert(errText);
				/*this.fireEvent('selectRequestEnd',{
					ok:false,
					nResults:0,
					errText:transport.responseText?transport.responseText:""+transport
				});
				this.showAjaxError(transport);*/
			}, 
			scope: this
		});
		
	},
	
	loadSuccess: function(results, store) {
		
		var mf = new MessageFormat(this.language);
		
		this.i18n = mf.compile(results[0].raw);
		this.loaded = true;
    			
		this.fireEvent('loaded');
	},
	
    onFailure: function() {
        Ext.Msg.alert('Failure', 'Failed to load locale file.');
    },
	
	isLoaded: function(){
		return this.loaded;
	},
	
    getMsg: function(key, varobj) {
		if(this.i18n) {
			if (this.i18n[key]) {
				return this.i18n[key](varobj);
			}else{
				//alert("Translation of string with key '"+key+"' not found");
				return "NoTranslation. Key "+ key + " not found!";
			}
			
		} else {
			//alert("Translation of string with key '"+key+"' not found");
			return "NoTranslation. Translations not loaded!";
		}
	},

	/**
	 * @private
	 */
	guessLanguage: function(){
		return (navigator.language || navigator.browserLanguage || navigator.userLanguage || this.defaultLanguage);
	}, 
	
	/**
	 * @private
	 */
	formatLanguageCode: function(lang){
		var langCodes = lang.split('-'),
            primary, second;
		primary = (langCodes[0]) ? langCodes[0].toLowerCase() : '';
		second = (langCodes[1]) ? langCodes[1].toUpperCase() : '';

        return langCodes.length > 1 ? [primary, second].join('-') : primary;
	}

});

// Definisco subito la variabile globale, perchè sia disponibile in fase di caricamento degli altri js
// Il caricamento dei file della lingua avviene prima dell'avvio dell'applicazione (su onBeforeLaunch) e solo quando finito l'applicazione viene avviata
ToloI18n = Ext.create('TolomeoExt.ToloI18n',{
					lang: Ext.Object.fromQueryString(window.location.search).lang || 'it'
			});

// Ritardo il lancio di Ext.onReady di 120 secondi per essere strasicuro che non venga lanciato fino a che tutto non è pronto
// ATTENZIONE !!! Impostata anche sulla jsp che fa il caricamento dinamico degli script, per essere sicuri che tutto funzioni anche in eventuali casi misti di dynamic loader + Ext.onReady			
Ext.EventManager.deferReadyEvent = 120000;  

// Registro sul loaded del ToloI18n di controllare se, una volta che la lingua è caricata, il documento sarebbe già pronto per il lancio dell'Ext.onReady oppure no.
// Se lo è lancio immediatamente il metodo Ext.EventManager.fireReadyEvent che è quello che viene di fatto lanciato da Ext e quando arriverà anche il lancio differito non farà nulla
// di nuovo, perché la lista degli registrazioni all'evento è stata già esaurita (se si teme possa fare qualcosa, una volta lanciato si può sovrascrivere il metodo fireReadyEvent in modo che non faccia niente).
// Se invece il documento non è pronto (Ext.isReady = false) si rimette il deferReadyEvent ad 1 che è il valore di default e la sequenza degli eventi farà il suo normale corso.
ToloI18n.on('loaded', 
	function() {
		if(Ext.isReady){
			Ext.EventManager.fireReadyEvent();
		} else {
			Ext.EventManager.deferReadyEvent = 1;			
		}
	},
	this,
	{single: true}
)

    
ToloI18n.load();			
			



/*
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110�1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo � un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo � un software libero; � possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo � distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILIT� o
 IDONEIT� PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110�1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/
/**
 * overview Definizione costanti e tipi geometrici utilizzati dal progetto Tolomeo <br/>
 * <br/>
 * name Tolomeo - Geometrie
 * @author Ing. Alessandro Radaelli <br/>Comune di Prato
 * 
* Costante che rappresenta il tipo di geometria: Punto 
* @type number
*/
var geomTypePoint   = 1;
/**
* Costante che rappresenta il tipo di geometria: Linea 
* @type number
*/
var geomTypeLine    = 2;
/**
* Costante che rappresenta il tipo di geometria: Poligono 
* @type number
*/
var geomTypePolygon = 3;
/**
* Costante che rappresenta il tipo di geometria: Cerchio 
* @type number
*/
var geomTypeCircle  = 4;


/**
 * @class TolomeoExt.Point
 * Rappresentazione di un punto
 * 
 * @constructor
 * 
 * 
 * @param {Number} x
 * coordinata x
 * 
 * @param {Number} y
 * coordinata y
 * 
 */
 function Point(x, y) {
 	/**
 	 * @property {Number} x
 	 * coordinata x
 	 * 
 	 */
 	this.x = x;
 	/**
 	 * @property {Number} y
 	 * coordinata y
 	 * 
 	 */
 	this.y = y; 	
 }
 
/**
 * @method clone
 * Restituisce un nuovo punto con le medesime coordinate del punto stesso 
 * 
 * @return {TolomeoExt.Point}
 * 
 * 
 */
 Point.prototype.clone = function() {
        return new Point(this.x ,this.y);
 }
 
/**
 * @method transform
 * Trasforma il punto dal sistema di riferimento iniziale a quello di destinazione
 * 
 * @param {String} source
 * codice EPSG del sistema di riferimento di partenza (Es. EPSG:26591) 
 * 
 * @param {String} dest
 * codice EPSG del sistema di riferimento di destinazione (Es. EPSG:4326)
 * 
 */
 Point.prototype.transform = function(source, dest) {
    var newPoint = TolomeoExt.Projection.transform(this, source, dest);
    this.x = newPoint.x;
    this.y = newPoint.y;
 }
 
/**
 * @method round
 * Arrotonda le coordinate con un certo numero di decimali
 * 
 * @param {Number} precision
 * numero di decimali 
 * 
 */
 Point.prototype.round = function(precision){
 	var factor = Math.pow(10,Math.round(precision) || 0);
 	this.x = Math.round(this.x * factor)/factor;
 	this.y = Math.round(this.y * factor)/factor;
 }   
    
/**
 * @method toString
 * 
 * 
 * @return {String}
 * 
 * 
 */
 Point.prototype.toString = function() {
    return (this.x + "," + this.y);
 }
 
 /**
  * @method Point.parseWkt
  * Restituisce un oggetto Point dalla relativa geometria in formato wkt
  * 
  * @parma {String} wktPoint 
  * punto in formato WKT
  * 
  */
 Point.parseWkt = function(wktPoint) {
	if(!Point.isWktPoint(wktPoint)) return null;
	var xy = wktPoint.split("(")[1].split(")")[0].split(" ");
	return new Point(xy[0], xy[1]);
 }
 

 /**
  * @method Point.isWktPoint
  * Restituisce true se la stringa passata è un punto in formato wkt
  * 
  * @parma {String} wktPoint 
  * punto in formato WKT
  * 
  */
 Point.isWktPoint = function(wktPoint) {
	 if(wktPoint === undefined || typeof(wktPoint) !== "string" || wktPoint === "") return false;
	 
	 if (wktPoint.indexOf("POINT")!=-1) return true
		else return false;
 }
 
/**
 * @class TolomeoExt.BBox
 * Rappresentazione di un bounding box
 * 
 * @constructor
 * 
 * 
 * @param {Number} left
 * coordinata x minima
 * 
 * @param {Number} bottom
 * coordinata y minima
 * 
 * @param {Number} right
 * coordinata x massima
 * 
 * @param {Number} top
 * coordinata y massima
 * 
 */
 function BBox(left,bottom,right,top){
 	/**
   * @property {Number} left
   * coordinata x minima
   * 
 	 */
 	this.left   = left;
 	/**
   * @property {Number} bottom
   * coordinata y minima
   * 
 	 */
 	this.bottom = bottom;
 	/**
   * @property {Number} right
   * coordinata x massima
   * 
 	 */
 	this.right  = right;
 	/**
   * @property {Number} top
   * coordinata y massima
   * 
 	 */
 	this.top    = top
 }
 
/**
 * @method create
 * 
 * 
 * @param {TolomeoExt.BBox} bbox
 * 
 * 
 * @return {TolomeoExt.BBox}
 * 
 * 
 */
 BBox.create = function(bbox){
 	return new BBox(bbox.left,bbox.bottom,bbox.right,bbox.top);
 }
 
/**
 * @method transform
 * Trasforma il bounding box dal sistema di riferimento iniziale a quello di destinazione
 * 
 * @param {String} source
 * codice EPSG del sistema di riferimento di partenza (Es. EPSG:26591) 
 * 
 * @param {String} dest
 * codice EPSG del sistema di riferimento di destinazione (Es. EPSG:4326)
 * 
 */
 BBox.prototype.transform = function(source, dest) {
 	
 	var leftBottomPoint = new Point(this.left , this.bottom);
	var rightTopPoint   = new Point(this.right, this.top);
										
	leftBottomPoint = TolomeoExt.Projection.transform(leftBottomPoint,source,dest);
	rightTopPoint   = TolomeoExt.Projection.transform(rightTopPoint,  source,dest);
		    
    this.left   = leftBottomPoint.x;
    this.bottom = leftBottomPoint.y;
    this.right  = rightTopPoint.x;
    this.top    = rightTopPoint.y;
 }
 
/**
 * @method format
 * 
 * 
 * @param {String} separator
 * 
 * 
 * @return {String}
 * 
 * 
 */
 BBox.prototype.format = function(separator) {
 	separator = separator || " ";
    return (this.left + separator + this.bottom + separator + this.right + separator + this.top);
 }
 
/**
 * @method toString
 * 
 * 
 * @return {String}
 * 
 * 
 */
 BBox.prototype.toString = function() {
    return this.format(" ");
 }
 
/**
 * @class TolomeoExt.JSGeometryArray
 * Array di geometrie. E' composto da <br>
 * <ul>
 *  	<li>geometries - array contenente elementi di tipo JSGeometry </li>
 * 		<li>boundingbox - la stringa WKT che rappresenta il rettangolo minimo che contiene l'insieme delle geometrie</li>
 * </ul>
 * Questo costruttore crea un JSGeometryArray a partire dalla stringa JSON 
 * see JSGeometry
 * 
 * @constructor
 * 
 * 
 * @param {String} JSONString
 * String JSON dalla quale costruire l'oggetto
 * 
 */
function JSGeometryArray(JSONString) {
	var ret = new Array();
	if (arguments.length!=0) {	
		var buff1 = eval('(' + JSONString + ')');
		/**
		 * @type TolomeoExt.JSGeometry
		 */
		var buff = buff1.geometries;
		this.boundingbox = buff1.boundingbox;
		for (var i=0;i<buff.length;i++) {
			ret[i] = new JSGeometry(buff[i].codTPN, buff[i].key, buff[i].description, buff[i].geometry, buff[i].boundingbox, buff[i].SRID, buff[i].getFeatureInfoLink,
									buff[i].attributes, buff[i].sourceHTTP, buff[i].sourceBBOX, buff[i].sourceLayer,buff[i].sourceStyle,
									buff[i].sourceWidth, buff[i].sourceHeight, buff[i].sourceX, buff[i].sourceY);
		}
	}
	this.geometries = ret;
	
}

/**
 * @method FromUntypedArray
 * Inizializza a partire da un JSGeometryArray non tipizzato (hanno le giuste propriet� ma non i prototype)
 * tipicamente perche' l'array stesso proviene da JSON
 * 
 * @param {Array} JSGeometryArray
 * non tipizzato da trasformare
 * 
 */
JSGeometryArray.prototype.FromUntypedArray = function (untypedJSGeometryArray) {
	
	var untypedArray = untypedJSGeometryArray.geometries;
	for (var i=0;i<untypedArray.length; i++) {
		var geom = new JSGeometry(untypedArray[i].codTPN, 
									  untypedArray[i].key,
									  untypedArray[i].description.replace(/\"/g,'\\"'),
									  untypedArray[i].geometry,
									  untypedArray[i].boundingbox, 
									  untypedArray[i].SRID,
									  untypedArray[i].getFeatureInfoLink,
									  untypedArray[i].attributes,
									  untypedArray[i].sourceHTTP,
									  untypedArray[i].sourceBBOX,
									  untypedArray[i].sourceLayer,
									  untypedArray[i].sourceStyle,
									  untypedArray[i].sourceWidth,
									  untypedArray[i].sourceHeight,
									  untypedArray[i].sourceX,
									  untypedArray[i].sourceY
		);
	    this.geometries[i]= geom;
	}
	this.boundingbox = untypedJSGeometryArray.boundingbox;
		
}

/**
 * @method FromStore
 * 
 * 
 * @param {Array} results
 * 
 * 
 * @param {Object} store
 * 
 * 
 * @return {Object}
 * 
 * 
 */
JSGeometryArray.prototype.FromStore = function (results, store) {
	
	var untypedArray = new Object();
	untypedArray.geometries = new Array(); 
	for (var i=0; i<results.length; i++) {
		var geom = results[i].data;
		untypedArray.geometries.push(geom);
	}
	if (store.reader) {
		//EtJS 3
		untypedArray.boundingbox = store.reader.meta.boundingbox;
	} else {
		//EtJS 4
		untypedArray.boundingbox = store.getProxy().getReader().metaData.boundingbox;
	}
	
	return this.FromUntypedArray(untypedArray);
}

/**
 * @method FromStoreSingleRecord
 * 
 * 
 * @param {Object} result
 * 
 * 
 * @return {Object}
 * 
 * 
 */
JSGeometryArray.prototype.FromStoreSingleRecord = function (result) {
	
	var untypedArray = new Object();
	untypedArray.geometries = new Array();
	untypedArray.geometries.push(result);
	untypedArray.boundingbox = result.boundingbox;
	untypedArray.getFeatureInfoLink = result.getFeatureInfoLink;
	
	return this.FromUntypedArray(untypedArray);
	
}

/**
 * @method add
 * Aggiunge un oggetto JSGeometry alle geometrie di JSGeometryArray<br/>
 * Accetta in ingresso sia stringhe JSON contententi l'oggetto JSGeometry sia oggetti JSGeometry
 * 
 * @param {TolomeoExt.JSGeometry} jsGeometry
 * 
 * 
 */
JSGeometryArray.prototype.add = function(jsGeometry){
	var newGeom;
	
	if (jsGeometry instanceof String) {
		newGeom = new JSGeometry(jsGeometry);
		this.geometries.push(newGeom);
	} else {
		if (jsGeometry instanceof JSGeometry) {
			this.geometries.push(jsGeometry);
		} else {
			if (jsGeometry instanceof JSGeometryArray) {
				for (var i=0; i<jsGeometry.geometries.length; i++){
					this.geometries.push(jsGeometry.geometries[i]);
				}
			}	
		}
	}
	
}

/**
 * @method toString
 * 
 * 
 * @return {String}
 * La stringa JSOn che rappresenta l'oggetto
 * 
 */
JSGeometryArray.prototype.toString = function () {
	var ret = "{ boundingbox: \"" + this.boundingbox + "\",";
	
	ret += "geometries: [";
	for (var i=0;i<this.geometries.length;i++) {
		if (i!=0) ret += ",";
		ret  += this.geometries[i].toString();
	}
	ret += "]}";
	return ret;
}

/**
 * @method size
 * 
 * 
 * @return {Number}
 * Il numero di elementi dell'array
 * 
 */
JSGeometryArray.prototype.size = function(){
	if(!this.geometries){
		return 0;
	}
	return this.geometries.length;
}

/**
 * @method clear
 * 
 * 
 * @param {Number} codTPN
 * 
 * 
 */
JSGeometryArray.prototype.clear = function(codTPN){
	var removedGeoms = [];
	if(this.geometries){		
		if (codTPN) {			
			var buff=new Array();
			for (var i=0;i<this.geometries.length;i++) {
				if (this.geometries[i].codTPN==codTPN) {
					buff.push(i);
				}	
			}		
			for (var i=0; i<buff.length;i++) {
				removedGeoms.push(this.geometries.splice(buff.pop(),1));
			}
		} else {
			for (var i=0; i<this.geometries.length;i++){
				removedGeoms[i] = this.geometries[i]; 
			}
			this.geometries.length=0;
			//this.geometries.clear();
		}
		return removedGeoms;
	}
}

/**
 * @method getByIndex
 * 
 * 
 * @param {Number} index
 * 
 * 
 * @return {TolomeoExt.JSGeometry}
 * 
 * 
 */
JSGeometryArray.prototype.getByIndex = function(index){
	if (index<0 || index>=this.size()) return null;	
	return this.geometries[index];
}

/**
 * @method getByCodTPN
 * Ritorna la/le geometrie contenute ed appartenenti al layer identificato dal codTN passato.
 * Ritorna null se non presenti
 * 
 * @param {Number} codTPN
 * 
 * 
 * @return {TolomeoExt.JSGeometry}
 * 
 * 
 */
JSGeometryArray.prototype.getByCodTPN = function(codTPN){
	var ret = null;
	for (var i=0;i<this.geometries.length;i++) {
		if (this.geometries[i].codTPN==codTPN) {
			if (ret==null) ret = new JSGeometryArray();
			ret.add(this.geometries[i]);
		}	
	}
	return ret;
}

/**
 * @method ContainsCodTPN
 * Ritorna true se contiene una o pi� geometrie appartenenti al layer identificato dal codTN passato.
 * altrimenti ritorna false
 * 
 * @param {Number} codTPN
 * 
 * 
 * @return {Boolean}
 * 
 * 
 */
JSGeometryArray.prototype.ContainsCodTPN = function(codTPN){
	
	for (var i=0;i<this.geometries.length;i++) {
		if (this.geometries[i].codTPN==codTPN) return true;
	}
	
	return false;
}
	
/**
 * @class TolomeoExt.JSGeometry
 * Rappresenta una geometria complessa. 
 * 
 * @constructor
 * Il costruttore se viene passato un solo parametro costruisce l'oggetto interpretando quanto passato come una stringa JSON, altrimenti utilizzando i valori passati
 * 
 * @param {Number} codTPN
 * Se è l'unico valore passato deve essere la stringa JSON che rappresenta l'oggetto da creare, altrimenti e' il valore da assegnare alla property omonima
 * 
 * @param {String} key
 * e' il valore da assegnare alla property omonima
 * 
 * @param {String} description
 * e' il valore da assegnare alla property omonima
 * 
 * @param {String} geometry
 * e' il valore da assegnare alla property omonima
 * 
 * @param {String} boundingbox
 * e' il valore da assegnare alla property omonima
 * 
 * @param {String} SRID
 * e' l'ID del sistema di riferimento utilizzato per la geometria (es. EPSG:26591)
 * 
 * @param {String} getFeatureInfoLink
 * e' il link per ottenere la getfeatureinfo in un WMS
 * 
 * @param {String} attributi
 * attributi della feature
 * 
 * @param {String} sourceHTTP 
 * url server nel caso feature prelevata da wms
 * 
 * @param {String} sourceBBOX 
 * BBOX utilizzato per la getfeatureinfo nel caso feature prelevata da wms
 * 
 * @param {String} sourceLayer 
 * Layer utilizzato per la getfeatureinfo nel caso feature prelevata da wms
 * 
 * @param {String} sourceStyle 
 * Style utilizzato per la getfeatureinfo nel caso feature prelevata da wms
 * 
 * 
 * @param {String} sourceWidth 
 * Width utilizzato per la getfeatureinfo nel caso feature prelevata da wms
 * 
 * @param {String} sourceHeight 
 * Height utilizzato per la getfeatureinfo nel caso feature prelevata da wms
 * 
 * @param {String} sourceX 
 * X utilizzato per la getfeatureinfo nel caso feature prelevata da wms
 * 
 * @param {String} sourceY 
 * Y utilizzato per la getfeatureinfo nel caso feature prelevata da wms
 * 
 */
function JSGeometry(codTPN, key, description, geometry, boundingbox, SRID, getFeatureInfoLink,
					attributes, sourceHTTP, sourceBBOX, sourceLayer, sourceStyle,
					sourceWidth, sourceHeight, sourceX, sourceY) {
	if (arguments.length == 1) {
		var JSONString = codTPN;
		var buff = eval('(' + JSONString + ')');
	
	/**
     * @property {Number} codTPN
     * Codice del layer di appartenenza dell'oggetto 
     * 
     */
		this.geometryIsValid = true;
		
		/**
     * @property {Number} codTPN
     * Codice del layer di appartenenza dell'oggetto 
     * 
     */
		this.codTPN = buff.codTPN;
		/**
     * @property {String} key
     * Chiave unica identificativa dell'oggetto all'interno del layer
     * 
     */
		this.key = buff.key;
		/**
     * @property {String} description
     * Descrizione dell'oggetto da utilizzarsi quando e' necessario per l'utente distiguere un oggetto dall'altro
     * 
     */
		this.description = buff.description;
		/**
     * @property {String} geometry
     * Rappresentazione WKT della geometria
     * 
     */
		this.geometry = buff.geometry;	
		/**
     * @property {String} boundingbox
     * Rappresentazione WKT del minimo rettangolo che contiene la geometria
     * 
     */
		this.boundingbox = buff.boundingbox;
		this.SRID = buff.SRID;
		this.getFeatureInfoLink = buff.getFeatureInfoLink;
		this.attributes = buff.attributes;
		this.sourceHTTP = buff.sourceHTTP;
		this.sourceBBOX = buff.sourceBBOX;
		this.sourceLayer = buff.sourceLayer;
		this.sourceStyle = buff.sourceStyle;
		
		this.sourceWidth = buff.sourceWidth;
		this.sourceHeight = buff.sourceHeight;
		this.sourceX = buff.sourceX;
		this.sourceY = buff.sourceY;
		
	} else {
		this.codTPN = codTPN;
		this.key = key;
		this.description = description;
		this.geometry = geometry;
		this.boundingbox = boundingbox;
		this.SRID = SRID;
		this.getFeatureInfoLink = getFeatureInfoLink;
		this.attributes = attributes;
		this.sourceHTTP = sourceHTTP;
		this.sourceBBOX = sourceBBOX;
		this.sourceLayer = sourceLayer;
		this.sourceStyle = sourceStyle;
		
		this.sourceWidth = sourceWidth;
		this.sourceHeight = sourceHeight;
		this.sourceX = sourceX;
		this.sourceY = sourceY;
	}
	
} 

/**
 * @method isPoint
 * 
 * 
 * @return {Boolean}
 * 
 * 
 */
JSGeometry.prototype.isPoint= function () {
	if (this.geometry.indexOf("POINT")!=-1) return true
	else return false;
}

/**
 * @method toString
 * 
 * 
 * @return {String}
 * La stringa JSOn che rappresenta l'oggetto
 * 
 */
JSGeometry.prototype.toString= function () {
		var retVal = "{codTPN: " + this.codTPN;
		retVal += ", key: \"" + this.key + "\"";
		retVal += ", description: \"" + this.description + "\"";
		retVal += ", geometry: \"" + this.geometry + "\"";
		retVal += ", SRID: \"" + this.SRID + "\"";//}";
		retVal += ", attributes: " + Ext.JSON.encode(this.attributes) + "";
		retVal += ", sourceHTTP: \"" + this.sourceHTTP + "\""; 
		retVal += ", sourceBBOX: \"" + this.sourceBBOX + "\""; 
		retVal += ", sourceLayer: \"" + this.sourceLayer + "\""; 
		retVal += ", sourceStyle: \"" + this.sourceStyle + "\"";
		retVal += ", sourceWidth: \"" + this.sourceWidth + "\"";
		retVal += ", sourceHeight: \"" + this.sourceHeight + "\"";
		retVal += ", sourceX: \"" + this.sourceX + "\"";
		retVal += ", sourceY: \"" + this.sourceY + "\"";
		
		retVal += ", getFeatureInfoLink: \"" + this.getFeatureInfoLink + "\"}"; 
		return retVal;
	}

/**
 * @method getParts
 * 
 * 
 * @return {JSGeometryArray}
 * Se multipart insieme delle geometrie che compongono la geometria, altrimenti la geometria stessa
 * 
 */
JSGeometry.prototype.getParts= function () {
	
	if (!this.parts) {
		var wktParts = TolomeoExt.ToloViewerOLPanel.getWKTParts(this.geometry);
		this.parts = new JSGeometryArray();
		if (wktParts) {
			for (var i=0; i < wktParts.length; i++){
				
				//codTPN, key, description, geometry, boundingbox, SRID, getFeatureInfoLink
				var g = new JSGeometry(
					this.codTPN,  this.key, "Parte " + (i+1), wktParts[i], "", this.SRID );
				this.parts.add(g);
			}
		}
	}
	return this.parts;
	
}// Il ; in testa al codice è stato messo perché la minificazione e concatenazione interpretava questo codice come il lancio di na funzione definita nel file che lo precede
;(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.MessageFormat = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var reserved = require('reserved-words');
var parse = require('messageformat-parser').parse;


/** Creates a new message compiler. Called internally from {@link MessageFormat#compile}.
 *
 * @class
 * @param {MessageFormat} mf - A MessageFormat instance
 * @property {object} locales - The locale identifiers that are used by the compiled functions
 * @property {object} runtime - Names of the core runtime functions that are used by the compiled functions
 * @property {object} formatters - The formatter functions that are used by the compiled functions 
 */
function Compiler(mf) {
    this.mf = mf;
    this.lc = null;
    this.locales = {};
    this.runtime = {};
    this.formatters = {};
}

module.exports = Compiler;


/** Utility function for quoting an Object's key value iff required
 *
 *  Quotes the key if it contains invalid characters or is an
 *  ECMAScript 3rd Edition reserved word (for IE8).
 */
Compiler.propname = function(key, obj) {
  if (/^[A-Z_$][0-9A-Z_$]*$/i.test(key) &&
     ['break', 'continue', 'delete', 'else', 'for', 'function', 'if', 'in', 'new',
      'return', 'this', 'typeof', 'var', 'void', 'while', 'with', 'case', 'catch',
      'default', 'do', 'finally', 'instanceof', 'switch', 'throw', 'try'].indexOf(key) < 0) {
    return obj ? obj + '.' + key : key;
  } else {
    var jkey = JSON.stringify(key);
    return obj ? obj + '[' + jkey + ']' : jkey;
  }
}


/** Utility function for escaping a function name iff required
 */
Compiler.funcname = function(key) {
  var fn = key.trim().replace(/\W+/g, '_');
  return reserved.check(fn, 'es2015', true) || /^\d/.test(fn) ? '_' + fn : fn;
}


/** Utility formatter function for enforcing Bidi Structured Text by using UCC
 *
 *  List inlined from data extracted from CLDR v27 & v28
 *  To verify/recreate, use the following:
 *
 *     git clone https://github.com/unicode-cldr/cldr-misc-full.git
 *     cd cldr-misc-full/main/
 *     grep characterOrder -r . | tr '"/' '\t' | cut -f2,6 | grep -C4 right-to-left
 */
Compiler.bidiMarkText = function(text, locale) {
  function isLocaleRTL(locale) {
    var rtlLanguages = ['ar', 'ckb', 'fa', 'he', 'ks($|[^bfh])', 'lrc', 'mzn',
                        'pa-Arab', 'ps', 'ug', 'ur', 'uz-Arab', 'yi'];
    return new RegExp('^' + rtlLanguages.join('|^')).test(locale);
  }
  var mark = JSON.stringify(isLocaleRTL(locale) ? '\u200F' : '\u200E');
  return mark + ' + ' + text + ' + ' + mark;
}


/** @private */
Compiler.prototype.cases = function(token, plural) {
  var needOther = true;
  var r = token.cases.map(function(c) {
    if (c.key === 'other') needOther = false;
    var s = c.tokens.map(function(tok) { return this.token(tok, plural); }, this);
    return Compiler.propname(c.key) + ': ' + (s.join(' + ') || '""');
  }, this);
  if (needOther) throw new Error("No 'other' form found in " + JSON.stringify(token));
  return '{ ' + r.join(', ') + ' }';
}


/** @private */
Compiler.prototype.token = function(token, plural) {
  if (typeof token == 'string') return JSON.stringify(token);

  var fn, args = [ Compiler.propname(token.arg, 'd') ];
  switch (token.type) {
    case 'argument':
      return this.mf.bidiSupport ? Compiler.bidiMarkText(args[0], this.lc) : args[0];

    case 'select':
      fn = 'select';
      args.push(this.cases(token, this.mf.strictNumberSign ? null : plural));
      this.runtime.select = true;
      break;

    case 'selectordinal':
      fn = 'plural';
      args.push(0, Compiler.funcname(this.lc), this.cases(token, token), 1);
      this.locales[this.lc] = true;
      this.runtime.plural = true;
      break;

    case 'plural':
      fn = 'plural';
      args.push(token.offset || 0, Compiler.funcname(this.lc), this.cases(token, token));
      this.locales[this.lc] = true;
      this.runtime.plural = true;
      break;

    case 'function':
      if (this.mf.intlSupport && !(token.key in this.mf.fmt) && (token.key in this.mf.constructor.formatters)) {
        var fmt = this.mf.constructor.formatters[token.key];
        this.mf.fmt[token.key] = (typeof fmt(this.mf) == 'function') ? fmt(this.mf) : fmt;
      }
      if (!this.mf.fmt[token.key]) throw new Error('Formatting function ' + JSON.stringify(token.key) + ' not found!');
      args.push(JSON.stringify(this.lc));
      if (token.params) switch (token.params.length) {
          case 0:   break;
          case 1:   args.push(JSON.stringify(token.params[0])); break;
          default:  args.push(JSON.stringify(token.params)); break;
      }
      fn = Compiler.propname(token.key, 'fmt');
      this.formatters[token.key] = true;
      break;

    case 'octothorpe':
      if (!plural) return '"#"';
      fn = 'number';
      args = [ Compiler.propname(plural.arg, 'd'), JSON.stringify(plural.arg) ];
      if (plural.offset) args.push(plural.offset);
      this.runtime.number = true;
      break;
  }

  if (!fn) throw new Error('Parser error for token ' + JSON.stringify(token));
  return fn + '(' + args.join(', ') + ')';
};


/** Recursively compile a string or a tree of strings to JavaScript function sources
 *
 *  If `src` is an object with a key that is also present in `plurals`, the key
 *  in question will be used as the locale identifier for its value. To disable
 *  the compile-time checks for plural & selectordinal keys while maintaining
 *  multi-locale support, use falsy values in `plurals`.
 *
 * @param {string|object} src - the source for which the JS code should be generated
 * @param {string} lc - the default locale
 * @param {object} plurals - a map of pluralization keys for all available locales
 */
Compiler.prototype.compile = function(src, lc, plurals) {
  if (typeof src != 'object') {
    this.lc = lc;
    var pc = plurals[lc] || { cardinal: [], ordinal: [] };
    var r = parse(src, pc).map(function(token) { return this.token(token); }, this);
    return 'function(d) { return ' + (r.join(' + ') || '""') + '; }';
  } else {
    var result = {};
    for (var key in src) {
      var lcKey = plurals.hasOwnProperty(key) ? key : lc;
      result[key] = this.compile(src[key], lcKey, plurals);
    }
    return result;
  }
}

},{"messageformat-parser":8,"reserved-words":10}],2:[function(require,module,exports){
/** @file messageformat.js - ICU PluralFormat + SelectFormat for JavaScript
 *
 * @author Alex Sexton - @SlexAxton, Eemeli Aro
 * @version 1.0.2
 * @copyright 2012-2016 Alex Sexton, Eemeli Aro, and Contributors
 * @license To use or fork, MIT. To contribute back, Dojo CLA
 */

var Compiler = require('./compiler');
var Runtime = require('./runtime');


/** Utility getter/wrapper for pluralization functions from
 *  {@link http://github.com/eemeli/make-plural.js make-plural}
 *
 * @private
 */
function getPluralFunc(locale, noPluralKeyChecks) {
  var plurals = require('make-plural/plurals');
  var pluralCategories = require('make-plural/pluralCategories');
  for (var l = locale; l; l = l.replace(/[-_]?[^-_]*$/, '')) {
    var pf = plurals[l];
    if (pf) {
      var pc = noPluralKeyChecks ? { cardinal: [], ordinal: [] } : (pluralCategories[l] || {});
      var fn = function() { return pf.apply(this, arguments); };
      fn.toString = function() { return pf.toString(); };
      fn.cardinal = pc.cardinal;
      fn.ordinal = pc.ordinal;
      return fn;
    }
  }
  throw new Error('Localisation function not found for locale ' + JSON.stringify(locale));
}


/** Create a new message formatter
 *
 *  If `locale` is not set, calls to `compile()` will fetch the default locale
 *  each time. A string `locale` will create a single-locale MessageFormat
 *  instance, with pluralisation rules fetched from the Unicode CLDR using
 *  {@link http://github.com/eemeli/make-plural.js make-plural}.
 *
 *  Using an array of strings as `locale` will create a MessageFormat object
 *  with multi-language support, with pluralisation rules fetched as above. To
 *  select which to use, use the second parameter of `compile()`, or use message
 *  keys corresponding to your locales.
 *
 *  Using an object `locale` with all properties of type `function` allows for
 *  the use of custom/externally defined pluralisation rules.
 *
 * @class
 * @param {string|string[]|Object.<string,function>} [locale] - The locale(s) to use
 */
function MessageFormat(locale) {
  this.pluralFuncs = {};
  if (locale) {
    if (typeof locale == 'string') {
      this.pluralFuncs[locale] = getPluralFunc(locale);
    } else if (Array.isArray(locale)) {
      locale.forEach(function(lc) { this.pluralFuncs[lc] = getPluralFunc(lc); }, this);
    } else if (typeof locale == 'object') {
      for (var lc in locale) if (locale.hasOwnProperty(lc)) {
        if (typeof locale[lc] != 'function') throw new Error('Expected function value for locale ' + JSON.stringify(lc));
        this.pluralFuncs[lc] = locale[lc];
      }
    }
  }
  this.fmt = {};
  this.runtime = new Runtime(this);
}


/** The default locale
 *
 *  Read by `compile()` when no locale has been previously set
 *
 * @memberof MessageFormat
 * @default 'en'
 */
MessageFormat.defaultLocale = 'en';


/** Escape special characaters
 *
 *  Prefix the characters `#`, `{`, `}` and `\` in the input string with a `\`.
 *  This will allow those characters to not be considered as MessageFormat
 *  control characters.
 *
 * @param {string} str - The input string
 * @returns {string} The escaped string
 */
MessageFormat.escape = function(str) {
  return str.replace(/[#{}\\]/g, '\\$&');
}


/** Default number formatting functions in the style of ICU's
 *  {@link http://icu-project.org/apiref/icu4j/com/ibm/icu/text/MessageFormat.html simpleArg syntax}
 *  implemented using the
 *  {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl Intl}
 *  object defined by ECMA-402.
 *
 *  **Note**: Intl is not defined in default Node until 0.11.15 / 0.12.0, so
 *  earlier versions require a {@link https://www.npmjs.com/package/intl polyfill}.
 *  Therefore {@link MessageFormat.intlSupport} needs to be true for these default
 *  functions to be available for inclusion in the output.
 *
 * @see MessageFormat#setIntlSupport
 *
 * @namespace
 */
MessageFormat.formatters = {


  /** Represent a number as an integer, percent or currency value
   *
   *  Available in MessageFormat strings as `{VAR, number, integer|percent|currency}`.
   *  Internally, calls Intl.NumberFormat with appropriate parameters. `currency` will
   *  default to USD; to change, set `MessageFormat#currency` to the appropriate
   *  three-letter currency code.
   *
   * @param {number} value - The value to operate on
   * @param {string} type - One of `'integer'`, `'percent'` , or `currency`
   *
   * @example
   * var mf = new MessageFormat('en').setIntlSupport(true);
   * mf.currency = 'EUR';  // needs to be set before first compile() call
   *
   * mf.compile('{N} is almost {N, number, integer}')({ N: 3.14 })
   * // '3.14 is almost 3'
   *
   * mf.compile('{P, number, percent} complete')({ P: 0.99 })
   * // '99% complete'
   *
   * mf.compile('The total is {V, number, currency}.')({ V: 5.5 })
   * // 'The total is €5.50.'
   */
  number: function(self) {
    return new Function("v,lc,p",
      "return new Intl.NumberFormat(lc,\n" +
      "    p=='integer' ? {maximumFractionDigits:0}\n" +
      "  : p=='percent' ? {style:'percent'}\n" +
      "  : p=='currency' ? {style:'currency', currency:'" + (self.currency || 'USD') + "', minimumFractionDigits:2, maximumFractionDigits:2}\n" +
      "  : {}).format(v)"
    );
  },


  /** Represent a date as a short/default/long/full string
   *
   * The input value needs to be in a form that the
   * {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date Date object}
   * can process using its single-argument form, `new Date(value)`.
   *
   * @param {number|string} value - Either a Unix epoch time in milliseconds, or a string value representing a date
   * @param {string} [type='default'] - One of `'short'`, `'default'`, `'long'` , or `full`
   *
   * @example
   * var mf = new MessageFormat(['en', 'fi']).setIntlSupport(true);
   *
   * mf.compile('Today is {T, date}')({ T: Date.now() })
   * // 'Today is Feb 21, 2016'
   *
   * mf.compile('Tänään on {T, date}', 'fi')({ T: Date.now() })
   * // 'Tänään on 21. helmikuuta 2016'
   *
   * mf.compile('Unix time started on {T, date, full}')({ T: 0 })
   * // 'Unix time started on Thursday, January 1, 1970'
   *
   * var cf = mf.compile('{sys} became operational on {d0, date, short}');
   * cf({ sys: 'HAL 9000', d0: '12 January 1999' })
   * // 'HAL 9000 became operational on 1/12/1999'
   */
  date: function(v,lc,p) {
    var o = {day:'numeric', month:'short', year:'numeric'};
    switch (p) {
      case 'full': o.weekday = 'long';
      case 'long': o.month = 'long'; break;
      case 'short': o.month = 'numeric';
    }
    return (new Date(v)).toLocaleDateString(lc, o)
  },


  /** Represent a time as a short/default/long string
   *
   * The input value needs to be in a form that the
   * {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date Date object}
   * can process using its single-argument form, `new Date(value)`.
   *
   * @param {number|string} value - Either a Unix epoch time in milliseconds, or a string value representing a date
   * @param {string} [type='default'] - One of `'short'`, `'default'`, `'long'` , or `full`
   *
   * @example
   * var mf = new MessageFormat(['en', 'fi']).setIntlSupport(true);
   *
   * mf.compile('The time is now {T, time}')({ T: Date.now() })
   * // 'The time is now 11:26:35 PM'
   *
   * mf.compile('Kello on nyt {T, time}', 'fi')({ T: Date.now() })
   * // 'Kello on nyt 23.26.35'
   *
   * var cf = mf.compile('The Eagle landed at {T, time, full} on {T, date, full}');
   * cf({ T: '1969-07-20 20:17:40 UTC' })
   * // 'The Eagle landed at 10:17:40 PM GMT+2 on Sunday, July 20, 1969'
   */
  time: function(v,lc,p) {
    var o = {second:'numeric', minute:'numeric', hour:'numeric'};
    switch (p) {
      case 'full': case 'long': o.timeZoneName = 'short'; break;
      case 'short': delete o.second;
    }
    return (new Date(v)).toLocaleTimeString(lc, o)
  }
};


/** Add custom formatter functions to this MessageFormat instance
 *
 *  The general syntax for calling a formatting function in MessageFormat is
 *  `{var, fn[, args]*}`, where `var` is the variable that will be set by the
 *  user code, `fn` determines the formatting function, and `args` is an
 *  optional comma-separated list of additional arguments.
 *
 *  In JavaScript, each formatting function is called with three parameters;
 *  the variable value `v`, the current locale `lc`, and (if set) `args` as a
 *  single string, or an array of strings. Formatting functions should not have
 *  side effects.
 *
 * @see MessageFormat.formatters
 *
 * @memberof MessageFormat
 * @param {Object.<string,function>} fmt - A map of formatting functions
 * @returns {MessageFormat} The MessageFormat instance, to allow for chaining
 *
 * @example
 * var mf = new MessageFormat('en-GB');
 * mf.addFormatters({
 *   upcase: function(v) { return v.toUpperCase(); },
 *   locale: function(v, lc) { return lc; },
 *   prop: function(v, lc, p) { return v[p] }
 * });
 *
 * mf.compile('This is {VAR, upcase}.')({ VAR: 'big' })
 * // 'This is BIG.'
 *
 * mf.compile('The current locale is {_, locale}.')({ _: '' })
 * // 'The current locale is en-GB.'
 *
 * mf.compile('Answer: {obj, prop, a}')({ obj: {q: 3, a: 42} })
 * // 'Answer: 42'
 */
MessageFormat.prototype.addFormatters = function(fmt) {
  for (var name in fmt) if (fmt.hasOwnProperty(name)) {
    this.fmt[name] = fmt[name];
  }
  return this;
};


/** Disable the validation of plural & selectordinal keys
 *
 *  Previous versions of messageformat.js allowed the use of plural &
 *  selectordinal statements with any keys; now we throw an error when a
 *  statement uses a non-numerical key that will never be matched as a
 *  pluralization category for the current locale.
 *
 *  Use this method to disable the validation and allow usage as previously.
 *  To re-enable, you'll need to create a new MessageFormat instance.
 *
 * @returns {MessageFormat} The MessageFormat instance, to allow for chaining
 *
 * @example
 * var mf = new MessageFormat('en');
 * var msg = '{X, plural, zero{no answers} one{an answer} other{# answers}}';
 *
 * mf.compile(msg);
 * // Error: Invalid key `zero` for argument `X`. Valid plural keys for this
 * //        locale are `one`, `other`, and explicit keys like `=0`.
 *
 * mf.disablePluralKeyChecks();
 * mf.compile(msg)({ X: 0 });
 * // '0 answers'
 */
MessageFormat.prototype.disablePluralKeyChecks = function() {
  this.noPluralKeyChecks = true;
  for (var lc in this.pluralFuncs) if (this.pluralFuncs.hasOwnProperty(lc)) {
    this.pluralFuncs[lc].cardinal = [];
    this.pluralFuncs[lc].ordinal = [];
  }
  return this;
};


/** Enable or disable the addition of Unicode control characters to all input
 *  to preserve the integrity of the output when mixing LTR and RTL text.
 *
 * @see http://cldr.unicode.org/development/development-process/design-proposals/bidi-handling-of-structured-text
 *
 * @memberof MessageFormat
 * @param {boolean} [enable=true]
 * @returns {MessageFormat} The MessageFormat instance, to allow for chaining
 *
 * @example
 * // upper case stands for RTL characters, output is shown as rendered
 * var mf = new MessageFormat('en');
 *
 * mf.compile('{0} >> {1} >> {2}')(['first', 'SECOND', 'THIRD']);
 * // 'first >> THIRD << SECOND'
 *
 * mf.setBiDiSupport(true);
 * mf.compile('{0} >> {1} >> {2}')(['first', 'SECOND', 'THIRD']);
 * // 'first >> SECOND >> THIRD'
 */
MessageFormat.prototype.setBiDiSupport = function(enable) {
    this.bidiSupport = !!enable || (typeof enable == 'undefined');
    return this;
};


/** Enable or disable support for the default formatters, which require the
 *  `Intl` object. Note that this can't be autodetected, as the environment
 *  in which the formatted text is compiled into Javascript functions is not
 *  necessarily the same environment in which they will get executed.
 *
 * @see MessageFormat.formatters
 *
 * @memberof MessageFormat
 * @param {boolean} [enable=true]
 * @returns {MessageFormat} The MessageFormat instance, to allow for chaining
 */
MessageFormat.prototype.setIntlSupport = function(enable) {
    this.intlSupport = !!enable || (typeof enable == 'undefined');
    return this;
};


/** According to the ICU MessageFormat spec, a `#` character directly inside a
 *  `plural` or `selectordinal` statement should be replaced by the number
 *  matching the surrounding statement. By default, messageformat.js will
 *  replace `#` signs with the value of the nearest surrounding `plural` or
 *  `selectordinal` statement.
 *
 *  Set this to true to follow the stricter ICU MessageFormat spec, and to
 *  throw a runtime error if `#` is used with non-numeric input.
 *
 * @memberof MessageFormat
 * @param {boolean} [enable=true]
 * @returns {MessageFormat} The MessageFormat instance, to allow for chaining
 *
 * @example
 * var mf = new MessageFormat('en');
 *
 * var cookieMsg = '#: {X, plural, =0{no cookies} one{a cookie} other{# cookies}}';
 * mf.compile(cookieMsg)({ X: 3 });
 * // '#: 3 cookies'
 *
 * var pastryMsg = '{X, plural, one{{P, select, cookie{a cookie} other{a pie}}} other{{P, select, cookie{# cookies} other{# pies}}}}';
 * mf.compile(pastryMsg)({ X: 3, P: 'pie' });
 * // '3 pies'
 *
 * mf.setStrictNumberSign(true);
 * mf.compile(pastryMsg)({ X: 3, P: 'pie' });
 * // '# pies'
 */
MessageFormat.prototype.setStrictNumberSign = function(enable) {
    this.strictNumberSign = !!enable || (typeof enable == 'undefined');
    this.runtime.setStrictNumber(this.strictNumberSign);
    return this;
};


/** Compile messages into storable functions
 *
 *  If `messages` is a single string including ICU MessageFormat declarations,
 *  the result of `compile()` is a function taking a single Object parameter
 *  `d` representing each of the input's defined variables.
 *
 *  If `messages` is a hierarchical structure of such strings, the output of
 *  `compile()` will match that structure, with each string replaced by its
 *  corresponding JavaScript function.
 *
 *  If the input `messages` -- and therefore the output -- of `compile()` is an
 *  object, the output object will have a `toString(global)` method that may be
 *  used to store or cache the compiled functions to disk, for later inclusion
 *  in any JS environment, without a local MessageFormat instance required. Its
 *  `global` parameters sets the name (if any) of the resulting global variable,
 *  with special handling for `exports`, `module.exports`, and `export default`.
 *  If `global` does not contain a `.`, the output defaults to an UMD pattern.
 *
 *  If `locale` is not set, the first locale set in the object's constructor
 *  will be used by default; using a key at any depth of `messages` that is a
 *  declared locale will set its child elements to use that locale.
 *
 *  If `locale` is set, it is used for all messages. If the constructor
 *  declared any locales, `locale` needs to be one of them.
 *
 * @memberof MessageFormat
 * @param {string|Object} messages - The input message(s) to be compiled, in ICU MessageFormat
 * @param {string} [locale] - A locale to use for the messages
 * @returns {function|Object} The first match found for the given locale(s)
 *
 * @example
 * var mf = new MessageFormat('en');
 * var cf = mf.compile('A {TYPE} example.');
 *
 * cf({ TYPE: 'simple' })
 * // 'A simple example.'
 *
 * @example
 * var mf = new MessageFormat(['en', 'fi']);
 * var cf = mf.compile({
 *   en: { a: 'A {TYPE} example.',
 *         b: 'This is the {COUNT, selectordinal, one{#st} two{#nd} few{#rd} other{#th}} example.' },
 *   fi: { a: '{TYPE} esimerkki.',
 *         b: 'Tämä on {COUNT, selectordinal, other{#.}} esimerkki.' }
 * });
 *
 * cf.en.b({ COUNT: 2 })
 * // 'This is the 2nd example.'
 *
 * cf.fi.b({ COUNT: 2 })
 * // 'Tämä on 2. esimerkki.'
 *
 * @example
 * var fs = require('fs');
 * var mf = new MessageFormat('en').setIntlSupport();
 * var msgSet = {
 *   a: 'A {TYPE} example.',
 *   b: 'This has {COUNT, plural, one{one member} other{# members}}.',
 *   c: 'We have {P, number, percent} code coverage.'
 * };
 * var cfStr = mf.compile(msgSet).toString('module.exports');
 * fs.writeFileSync('messages.js', cfStr);
 * ...
 * var messages = require('./messages');
 *
 * messages.a({ TYPE: 'more complex' })
 * // 'A more complex example.'
 *
 * messages.b({ COUNT: 3 })
 * // 'This has 3 members.'
 */
MessageFormat.prototype.compile = function(messages, locale) {
  function _stringify(obj, level) {
    if (!level) level = 0;
    if (typeof obj != 'object') return obj;
    var o = [], indent = '';
    for (var i = 0; i < level; ++i) indent += '  ';
    for (var k in obj) o.push('\n' + indent + '  ' + Compiler.propname(k) + ': ' + _stringify(obj[k], level + 1));
    return '{' + o.join(',') + '\n' + indent + '}';
  }

  var pf;
  if (Object.keys(this.pluralFuncs).length == 0) {
    if (!locale) locale = MessageFormat.defaultLocale;
    pf = {};
    pf[locale] = getPluralFunc(locale, this.noPluralKeyChecks);
  } else if (locale) {
    pf = {};
    pf[locale] = this.pluralFuncs[locale];
    if (!pf[locale]) throw new Error('Locale ' + JSON.stringify(locale) + 'not found in ' + JSON.stringify(this.pluralFuncs) + '!');
  } else {
    pf = this.pluralFuncs;
    locale = Object.keys(pf)[0];
  }

  var compiler = new Compiler(this);
  var obj = compiler.compile(messages, locale, pf);

  if (typeof messages != 'object') {
    var fn = new Function(
        'number, plural, select, fmt', Compiler.funcname(locale),
        'return ' + obj);
    var rt = this.runtime;
    return fn(rt.number, rt.plural, rt.select, this.fmt, pf[locale]);
  }

  var rtStr = this.runtime.toString(pf, compiler) + '\n';
  var objStr = _stringify(obj);
  var result = new Function(rtStr + 'return ' + objStr)();
  if (result.hasOwnProperty('toString')) throw new Error('The top-level message key `toString` is reserved');

  result.toString = function(global) {
    switch (global || '') {
      case 'exports':
        var o = [];
        for (var k in obj) o.push(Compiler.propname(k, 'exports') + ' = ' + _stringify(obj[k]));
        return rtStr + o.join(';\n');
      case 'module.exports':
        return rtStr + 'module.exports = ' + objStr;
      case 'export default':
        return rtStr +  'export default ' + objStr;
      case '':
        return rtStr + 'return ' + objStr;
      default:
        if (global.indexOf('.') > -1) return rtStr + global + ' = ' + objStr;
        return rtStr + [
          '(function (root, G) {',
          '  if (typeof define === "function" && define.amd) { define(G); }',
          '  else if (typeof exports === "object") { module.exports = G; }',
          '  else { ' + Compiler.propname(global, 'root') + ' = G; }',
          '})(this, ' + objStr + ');'
        ].join('\n');
    }
  }
  return result;
}


module.exports = MessageFormat;

},{"./compiler":1,"./runtime":3,"make-plural/pluralCategories":6,"make-plural/plurals":7}],3:[function(require,module,exports){
var Compiler = require('./compiler');


/** A set of utility functions that are called by the compiled Javascript
 *  functions, these are included locally in the output of {@link
 *  MessageFormat#compile compile()}.
 *
 * @class
 * @param {MessageFormat} mf - A MessageFormat instance
 */
function Runtime(mf) {
  this.mf = mf;
  this.setStrictNumber(mf.strictNumberSign);
}

module.exports = Runtime;


/** Utility function for `#` in plural rules
 *
 *  Will throw an Error if `value` has a non-numeric value and `offset` is
 *  non-zero or {@link MessageFormat#setStrictNumberSign} is set.
 *
 * @function Runtime#number
 * @param {number} value - The value to operate on
 * @param {string} name - The name of the argument, used for error reporting
 * @param {number} [offset=0] - An optional offset, set by the surrounding context
 * @returns {number|string} The result of applying the offset to the input value
 */
function defaultNumber(value, name, offset) {
  if (!offset) return value;
  if (isNaN(value)) throw new Error('Can\'t apply offset:' + offset + ' to argument `' + name +
                                    '` with non-numerical value ' + JSON.stringify(value) + '.');
  return value - offset;
}


/** @private */
function strictNumber(value, name, offset) {
  if (isNaN(value)) throw new Error('Argument `' + name + '` has non-numerical value ' + JSON.stringify(value) + '.');
  return value - (offset || 0);
}


/** Set how strictly the {@link number} method parses its input.
 *
 *  According to the ICU MessageFormat spec, `#` can only be used to replace a
 *  number input of a `plural` statement. By default, messageformat.js does not
 *  throw a runtime error if you use non-numeric argument with a `plural` rule,
 *  unless rule also includes a non-zero `offset`.
 *
 *  This is called by {@link MessageFormat#setStrictNumberSign} to follow the
 *  stricter ICU MessageFormat spec.
 *
 * @param {boolean} [enable=false]
 */
Runtime.prototype.setStrictNumber = function(enable) {
  this.number = enable ? strictNumber : defaultNumber;
}


/** Utility function for `{N, plural|selectordinal, ...}`
 *
 * @param {number} value - The key to use to find a pluralization rule
 * @param {number} offset - An offset to apply to `value`
 * @param {function} lcfunc - A locale function from `pluralFuncs`
 * @param {Object.<string,string>} data - The object from which results are looked up
 * @param {?boolean} isOrdinal - If true, use ordinal rather than cardinal rules
 * @returns {string} The result of the pluralization
 */
Runtime.prototype.plural = function(value, offset, lcfunc, data, isOrdinal) {
  if ({}.hasOwnProperty.call(data, value)) return data[value];
  if (offset) value -= offset;
  var key = lcfunc(value, isOrdinal);
  if (key in data) return data[key];
  return data.other;
}


/** Utility function for `{N, select, ...}`
 *
 * @param {number} value - The key to use to find a selection
 * @param {Object.<string,string>} data - The object from which results are looked up
 * @returns {string} The result of the select statement
 */
Runtime.prototype.select = function(value, data) {
  if ({}.hasOwnProperty.call(data, value)) return data[value];
  return data.other;
}


/** @private */
Runtime.prototype.toString = function(pluralFuncs, compiler) {
  function _stringify(o, level) {
    if (typeof o != 'object') {
      var funcStr = o.toString().replace(/^(function )\w*/, '$1');
      var indent = /([ \t]*)\S.*$/.exec(funcStr);
      return indent ? funcStr.replace(new RegExp('^' + indent[1], 'mg'), '') : funcStr;
    }
    var s = [];
    for (var i in o) {
      if (level == 0) s.push('var ' + i + ' = ' + _stringify(o[i], level + 1) + ';\n');
      else s.push(Compiler.propname(i) + ': ' + _stringify(o[i], level + 1));
    }
    if (level == 0) return s.join('');
    if (s.length == 0) return '{}';
    var indent = '  '; while (--level) indent += '  ';
    return '{\n' + s.join(',\n').replace(/^/gm, indent) + '\n}';
  }

  var obj = {};
  Object.keys(compiler.locales).forEach(function(lc) { obj[Compiler.funcname(lc)] = pluralFuncs[lc]; });
  Object.keys(compiler.runtime).forEach(function(fn) { obj[fn] = this[fn]; }, this);
  var fmtKeys = Object.keys(compiler.formatters);
  var fmt = this.mf.fmt;
  if (fmtKeys.length) obj.fmt = fmtKeys.reduce(function(o, key) { o[key] = fmt[key]; return o; }, {});
  return _stringify(obj, 0);
}

},{"./compiler":1}],4:[function(require,module,exports){
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
//
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
//
// Originally from narwhal.js (http://narwhaljs.org)
// Copyright (c) 2009 Thomas Robinson <280north.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// when used in node, this will actually load the util module we depend on
// versus loading the builtin util module as happens otherwise
// this is a bug in node module loading as far as I am concerned
var util = require('util/');

var pSlice = Array.prototype.slice;
var hasOwn = Object.prototype.hasOwnProperty;

// 1. The assert module provides functions that throw
// AssertionError's when particular conditions are not met. The
// assert module must conform to the following interface.

var assert = module.exports = ok;

// 2. The AssertionError is defined in assert.
// new assert.AssertionError({ message: message,
//                             actual: actual,
//                             expected: expected })

assert.AssertionError = function AssertionError(options) {
  this.name = 'AssertionError';
  this.actual = options.actual;
  this.expected = options.expected;
  this.operator = options.operator;
  if (options.message) {
    this.message = options.message;
    this.generatedMessage = false;
  } else {
    this.message = getMessage(this);
    this.generatedMessage = true;
  }
  var stackStartFunction = options.stackStartFunction || fail;

  if (Error.captureStackTrace) {
    Error.captureStackTrace(this, stackStartFunction);
  }
  else {
    // non v8 browsers so we can have a stacktrace
    var err = new Error();
    if (err.stack) {
      var out = err.stack;

      // try to strip useless frames
      var fn_name = stackStartFunction.name;
      var idx = out.indexOf('\n' + fn_name);
      if (idx >= 0) {
        // once we have located the function frame
        // we need to strip out everything before it (and its line)
        var next_line = out.indexOf('\n', idx + 1);
        out = out.substring(next_line + 1);
      }

      this.stack = out;
    }
  }
};

// assert.AssertionError instanceof Error
util.inherits(assert.AssertionError, Error);

function replacer(key, value) {
  if (util.isUndefined(value)) {
    return '' + value;
  }
  if (util.isNumber(value) && !isFinite(value)) {
    return value.toString();
  }
  if (util.isFunction(value) || util.isRegExp(value)) {
    return value.toString();
  }
  return value;
}

function truncate(s, n) {
  if (util.isString(s)) {
    return s.length < n ? s : s.slice(0, n);
  } else {
    return s;
  }
}

function getMessage(self) {
  return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' +
         self.operator + ' ' +
         truncate(JSON.stringify(self.expected, replacer), 128);
}

// At present only the three keys mentioned above are used and
// understood by the spec. Implementations or sub modules can pass
// other keys to the AssertionError's constructor - they will be
// ignored.

// 3. All of the following functions must throw an AssertionError
// when a corresponding condition is not met, with a message that
// may be undefined if not provided.  All assertion methods provide
// both the actual and expected values to the assertion error for
// display purposes.

function fail(actual, expected, message, operator, stackStartFunction) {
  throw new assert.AssertionError({
    message: message,
    actual: actual,
    expected: expected,
    operator: operator,
    stackStartFunction: stackStartFunction
  });
}

// EXTENSION! allows for well behaved errors defined elsewhere.
assert.fail = fail;

// 4. Pure assertion tests whether a value is truthy, as determined
// by !!guard.
// assert.ok(guard, message_opt);
// This statement is equivalent to assert.equal(true, !!guard,
// message_opt);. To test strictly for the value true, use
// assert.strictEqual(true, guard, message_opt);.

function ok(value, message) {
  if (!value) fail(value, true, message, '==', assert.ok);
}
assert.ok = ok;

// 5. The equality assertion tests shallow, coercive equality with
// ==.
// assert.equal(actual, expected, message_opt);

assert.equal = function equal(actual, expected, message) {
  if (actual != expected) fail(actual, expected, message, '==', assert.equal);
};

// 6. The non-equality assertion tests for whether two objects are not equal
// with != assert.notEqual(actual, expected, message_opt);

assert.notEqual = function notEqual(actual, expected, message) {
  if (actual == expected) {
    fail(actual, expected, message, '!=', assert.notEqual);
  }
};

// 7. The equivalence assertion tests a deep equality relation.
// assert.deepEqual(actual, expected, message_opt);

assert.deepEqual = function deepEqual(actual, expected, message) {
  if (!_deepEqual(actual, expected)) {
    fail(actual, expected, message, 'deepEqual', assert.deepEqual);
  }
};

function _deepEqual(actual, expected) {
  // 7.1. All identical values are equivalent, as determined by ===.
  if (actual === expected) {
    return true;

  } else if (util.isBuffer(actual) && util.isBuffer(expected)) {
    if (actual.length != expected.length) return false;

    for (var i = 0; i < actual.length; i++) {
      if (actual[i] !== expected[i]) return false;
    }

    return true;

  // 7.2. If the expected value is a Date object, the actual value is
  // equivalent if it is also a Date object that refers to the same time.
  } else if (util.isDate(actual) && util.isDate(expected)) {
    return actual.getTime() === expected.getTime();

  // 7.3 If the expected value is a RegExp object, the actual value is
  // equivalent if it is also a RegExp object with the same source and
  // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
  } else if (util.isRegExp(actual) && util.isRegExp(expected)) {
    return actual.source === expected.source &&
           actual.global === expected.global &&
           actual.multiline === expected.multiline &&
           actual.lastIndex === expected.lastIndex &&
           actual.ignoreCase === expected.ignoreCase;

  // 7.4. Other pairs that do not both pass typeof value == 'object',
  // equivalence is determined by ==.
  } else if (!util.isObject(actual) && !util.isObject(expected)) {
    return actual == expected;

  // 7.5 For all other Object pairs, including Array objects, equivalence is
  // determined by having the same number of owned properties (as verified
  // with Object.prototype.hasOwnProperty.call), the same set of keys
  // (although not necessarily the same order), equivalent values for every
  // corresponding key, and an identical 'prototype' property. Note: this
  // accounts for both named and indexed properties on Arrays.
  } else {
    return objEquiv(actual, expected);
  }
}

function isArguments(object) {
  return Object.prototype.toString.call(object) == '[object Arguments]';
}

function objEquiv(a, b) {
  if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b))
    return false;
  // an identical 'prototype' property.
  if (a.prototype !== b.prototype) return false;
  // if one is a primitive, the other must be same
  if (util.isPrimitive(a) || util.isPrimitive(b)) {
    return a === b;
  }
  var aIsArgs = isArguments(a),
      bIsArgs = isArguments(b);
  if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
    return false;
  if (aIsArgs) {
    a = pSlice.call(a);
    b = pSlice.call(b);
    return _deepEqual(a, b);
  }
  var ka = objectKeys(a),
      kb = objectKeys(b),
      key, i;
  // having the same number of owned properties (keys incorporates
  // hasOwnProperty)
  if (ka.length != kb.length)
    return false;
  //the same set of keys (although not necessarily the same order),
  ka.sort();
  kb.sort();
  //~~~cheap key test
  for (i = ka.length - 1; i >= 0; i--) {
    if (ka[i] != kb[i])
      return false;
  }
  //equivalent values for every corresponding key, and
  //~~~possibly expensive deep test
  for (i = ka.length - 1; i >= 0; i--) {
    key = ka[i];
    if (!_deepEqual(a[key], b[key])) return false;
  }
  return true;
}

// 8. The non-equivalence assertion tests for any deep inequality.
// assert.notDeepEqual(actual, expected, message_opt);

assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
  if (_deepEqual(actual, expected)) {
    fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
  }
};

// 9. The strict equality assertion tests strict equality, as determined by ===.
// assert.strictEqual(actual, expected, message_opt);

assert.strictEqual = function strictEqual(actual, expected, message) {
  if (actual !== expected) {
    fail(actual, expected, message, '===', assert.strictEqual);
  }
};

// 10. The strict non-equality assertion tests for strict inequality, as
// determined by !==.  assert.notStrictEqual(actual, expected, message_opt);

assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
  if (actual === expected) {
    fail(actual, expected, message, '!==', assert.notStrictEqual);
  }
};

function expectedException(actual, expected) {
  if (!actual || !expected) {
    return false;
  }

  if (Object.prototype.toString.call(expected) == '[object RegExp]') {
    return expected.test(actual);
  } else if (actual instanceof expected) {
    return true;
  } else if (expected.call({}, actual) === true) {
    return true;
  }

  return false;
}

function _throws(shouldThrow, block, expected, message) {
  var actual;

  if (util.isString(expected)) {
    message = expected;
    expected = null;
  }

  try {
    block();
  } catch (e) {
    actual = e;
  }

  message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
            (message ? ' ' + message : '.');

  if (shouldThrow && !actual) {
    fail(actual, expected, 'Missing expected exception' + message);
  }

  if (!shouldThrow && expectedException(actual, expected)) {
    fail(actual, expected, 'Got unwanted exception' + message);
  }

  if ((shouldThrow && actual && expected &&
      !expectedException(actual, expected)) || (!shouldThrow && actual)) {
    throw actual;
  }
}

// 11. Expected to throw an error:
// assert.throws(block, Error_opt, message_opt);

assert["throws"] = function(block, /*optional*/error, /*optional*/message) {
  _throws.apply(this, [true].concat(pSlice.call(arguments)));
};

// EXTENSION! This is annoying to write outside this module.
assert.doesNotThrow = function(block, /*optional*/message) {
  _throws.apply(this, [false].concat(pSlice.call(arguments)));
};

assert.ifError = function(err) { if (err) {throw err;}};

var objectKeys = Object.keys || function (obj) {
  var keys = [];
  for (var key in obj) {
    if (hasOwn.call(obj, key)) keys.push(key);
  }
  return keys;
};

},{"util/":13}],5:[function(require,module,exports){
if (typeof Object.create === 'function') {
  // implementation from standard node.js 'util' module
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    ctor.prototype = Object.create(superCtor.prototype, {
      constructor: {
        value: ctor,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  };
} else {
  // old school shim for old browsers
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    var TempCtor = function () {}
    TempCtor.prototype = superCtor.prototype
    ctor.prototype = new TempCtor()
    ctor.prototype.constructor = ctor
  }
}

},{}],6:[function(require,module,exports){
var _cc = [
  {cardinal:["other"],ordinal:["other"]},
  {cardinal:["one","other"],ordinal:["other"]},
  {cardinal:["one","other"],ordinal:["one","other"]},
  {cardinal:["one","two","other"],ordinal:["other"]}
];

(function (root, pluralCategories) {
  if (typeof define === 'function' && define.amd) {
    define(pluralCategories);
  } else if (typeof exports === 'object') {
    module.exports = pluralCategories;
  } else {
    root.pluralCategories = pluralCategories;
  }
}(this, {
af: _cc[1],
ak: _cc[1],
am: _cc[1],
ar: {cardinal:["zero","one","two","few","many","other"],ordinal:["other"]},
as: {cardinal:["one","other"],ordinal:["one","two","few","many","other"]},
asa: _cc[1],
ast: _cc[1],
az: {cardinal:["one","other"],ordinal:["one","few","many","other"]},
be: {cardinal:["one","few","many","other"],ordinal:["few","other"]},
bem: _cc[1],
bez: _cc[1],
bg: _cc[1],
bh: _cc[1],
bm: _cc[0],
bn: {cardinal:["one","other"],ordinal:["one","two","few","many","other"]},
bo: _cc[0],
br: {cardinal:["one","two","few","many","other"],ordinal:["other"]},
brx: _cc[1],
bs: {cardinal:["one","few","other"],ordinal:["other"]},
ca: {cardinal:["one","other"],ordinal:["one","two","few","other"]},
ce: _cc[1],
cgg: _cc[1],
chr: _cc[1],
ckb: _cc[1],
cs: {cardinal:["one","few","many","other"],ordinal:["other"]},
cy: {cardinal:["zero","one","two","few","many","other"],ordinal:["zero","one","two","few","many","other"]},
da: _cc[1],
de: _cc[1],
dsb: {cardinal:["one","two","few","other"],ordinal:["other"]},
dv: _cc[1],
dz: _cc[0],
ee: _cc[1],
el: _cc[1],
en: {cardinal:["one","other"],ordinal:["one","two","few","other"]},
eo: _cc[1],
es: _cc[1],
et: _cc[1],
eu: _cc[1],
fa: _cc[1],
ff: _cc[1],
fi: _cc[1],
fil: _cc[2],
fo: _cc[1],
fr: _cc[2],
fur: _cc[1],
fy: _cc[1],
ga: {cardinal:["one","two","few","many","other"],ordinal:["one","other"]},
gd: {cardinal:["one","two","few","other"],ordinal:["other"]},
gl: _cc[1],
gsw: _cc[1],
gu: {cardinal:["one","other"],ordinal:["one","two","few","many","other"]},
guw: _cc[1],
gv: {cardinal:["one","two","few","many","other"],ordinal:["other"]},
ha: _cc[1],
haw: _cc[1],
he: {cardinal:["one","two","many","other"],ordinal:["other"]},
hi: {cardinal:["one","other"],ordinal:["one","two","few","many","other"]},
hr: {cardinal:["one","few","other"],ordinal:["other"]},
hsb: {cardinal:["one","two","few","other"],ordinal:["other"]},
hu: _cc[2],
hy: _cc[2],
id: _cc[0],
ig: _cc[0],
ii: _cc[0],
"in": _cc[0],
is: _cc[1],
it: {cardinal:["one","other"],ordinal:["many","other"]},
iu: _cc[3],
iw: {cardinal:["one","two","many","other"],ordinal:["other"]},
ja: _cc[0],
jbo: _cc[0],
jgo: _cc[1],
ji: _cc[1],
jmc: _cc[1],
jv: _cc[0],
jw: _cc[0],
ka: {cardinal:["one","other"],ordinal:["one","many","other"]},
kab: _cc[1],
kaj: _cc[1],
kcg: _cc[1],
kde: _cc[0],
kea: _cc[0],
kk: {cardinal:["one","other"],ordinal:["many","other"]},
kkj: _cc[1],
kl: _cc[1],
km: _cc[0],
kn: _cc[1],
ko: _cc[0],
ks: _cc[1],
ksb: _cc[1],
ksh: {cardinal:["zero","one","other"],ordinal:["other"]},
ku: _cc[1],
kw: _cc[3],
ky: _cc[1],
lag: {cardinal:["zero","one","other"],ordinal:["other"]},
lb: _cc[1],
lg: _cc[1],
lkt: _cc[0],
ln: _cc[1],
lo: {cardinal:["other"],ordinal:["one","other"]},
lt: {cardinal:["one","few","many","other"],ordinal:["other"]},
lv: {cardinal:["zero","one","other"],ordinal:["other"]},
mas: _cc[1],
mg: _cc[1],
mgo: _cc[1],
mk: {cardinal:["one","other"],ordinal:["one","two","many","other"]},
ml: _cc[1],
mn: _cc[1],
mo: {cardinal:["one","few","other"],ordinal:["one","other"]},
mr: {cardinal:["one","other"],ordinal:["one","two","few","other"]},
ms: {cardinal:["other"],ordinal:["one","other"]},
mt: {cardinal:["one","few","many","other"],ordinal:["other"]},
my: _cc[0],
nah: _cc[1],
naq: _cc[3],
nb: _cc[1],
nd: _cc[1],
ne: _cc[2],
nl: _cc[1],
nn: _cc[1],
nnh: _cc[1],
no: _cc[1],
nqo: _cc[0],
nr: _cc[1],
nso: _cc[1],
ny: _cc[1],
nyn: _cc[1],
om: _cc[1],
or: _cc[1],
os: _cc[1],
pa: _cc[1],
pap: _cc[1],
pl: {cardinal:["one","few","many","other"],ordinal:["other"]},
prg: {cardinal:["zero","one","other"],ordinal:["other"]},
ps: _cc[1],
pt: _cc[1],
"pt-PT": _cc[1],
rm: _cc[1],
ro: {cardinal:["one","few","other"],ordinal:["one","other"]},
rof: _cc[1],
root: _cc[0],
ru: {cardinal:["one","few","many","other"],ordinal:["other"]},
rwk: _cc[1],
sah: _cc[0],
saq: _cc[1],
sdh: _cc[1],
se: _cc[3],
seh: _cc[1],
ses: _cc[0],
sg: _cc[0],
sh: {cardinal:["one","few","other"],ordinal:["other"]},
shi: {cardinal:["one","few","other"],ordinal:["other"]},
si: _cc[1],
sk: {cardinal:["one","few","many","other"],ordinal:["other"]},
sl: {cardinal:["one","two","few","other"],ordinal:["other"]},
sma: _cc[3],
smi: _cc[3],
smj: _cc[3],
smn: _cc[3],
sms: _cc[3],
sn: _cc[1],
so: _cc[1],
sq: {cardinal:["one","other"],ordinal:["one","many","other"]},
sr: {cardinal:["one","few","other"],ordinal:["other"]},
ss: _cc[1],
ssy: _cc[1],
st: _cc[1],
sv: _cc[2],
sw: _cc[1],
syr: _cc[1],
ta: _cc[1],
te: _cc[1],
teo: _cc[1],
th: _cc[0],
ti: _cc[1],
tig: _cc[1],
tk: _cc[1],
tl: _cc[2],
tn: _cc[1],
to: _cc[0],
tr: _cc[1],
ts: _cc[1],
tzm: _cc[1],
ug: _cc[1],
uk: {cardinal:["one","few","many","other"],ordinal:["few","other"]},
ur: _cc[1],
uz: _cc[1],
ve: _cc[1],
vi: {cardinal:["other"],ordinal:["one","other"]},
vo: _cc[1],
vun: _cc[1],
wa: _cc[1],
wae: _cc[1],
wo: _cc[0],
xh: _cc[1],
xog: _cc[1],
yi: _cc[1],
yo: _cc[0],
zh: _cc[0],
zu: _cc[1]
}));

},{}],7:[function(require,module,exports){
var _cp = [
function(n, ord) {
  if (ord) return 'other';
  return 'other';
},
function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one' : 'other';
},
function(n, ord) {
  if (ord) return 'other';
  return ((n == 0
          || n == 1)) ? 'one' : 'other';
},
function(n, ord) {
  var s = String(n).split('.'), v0 = !s[1];
  if (ord) return 'other';
  return (n == 1 && v0) ? 'one' : 'other';
}
];

(function (root, plurals) {
  if (typeof define === 'function' && define.amd) {
    define(plurals);
  } else if (typeof exports === 'object') {
    module.exports = plurals;
  } else {
    root.plurals = plurals;
  }
}(this, {
af: _cp[1],

ak: _cp[2],

am: function(n, ord) {
  if (ord) return 'other';
  return (n >= 0 && n <= 1) ? 'one' : 'other';
},

ar: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n,
      n100 = t0 && s[0].slice(-2);
  if (ord) return 'other';
  return (n == 0) ? 'zero'
      : (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : ((n100 >= 3 && n100 <= 10)) ? 'few'
      : ((n100 >= 11 && n100 <= 99)) ? 'many'
      : 'other';
},

as: function(n, ord) {
  if (ord) return ((n == 1 || n == 5 || n == 7 || n == 8 || n == 9
          || n == 10)) ? 'one'
      : ((n == 2
          || n == 3)) ? 'two'
      : (n == 4) ? 'few'
      : (n == 6) ? 'many'
      : 'other';
  return (n >= 0 && n <= 1) ? 'one' : 'other';
},

asa: _cp[1],

ast: _cp[3],

az: function(n, ord) {
  var s = String(n).split('.'), i = s[0], i10 = i.slice(-1),
      i100 = i.slice(-2), i1000 = i.slice(-3);
  if (ord) return ((i10 == 1 || i10 == 2 || i10 == 5 || i10 == 7 || i10 == 8)
          || (i100 == 20 || i100 == 50 || i100 == 70
          || i100 == 80)) ? 'one'
      : ((i10 == 3 || i10 == 4) || (i1000 == 100 || i1000 == 200
          || i1000 == 300 || i1000 == 400 || i1000 == 500 || i1000 == 600 || i1000 == 700
          || i1000 == 800
          || i1000 == 900)) ? 'few'
      : (i == 0 || i10 == 6 || (i100 == 40 || i100 == 60
          || i100 == 90)) ? 'many'
      : 'other';
  return (n == 1) ? 'one' : 'other';
},

be: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2);
  if (ord) return ((n10 == 2
          || n10 == 3) && n100 != 12 && n100 != 13) ? 'few' : 'other';
  return (n10 == 1 && n100 != 11) ? 'one'
      : ((n10 >= 2 && n10 <= 4) && (n100 < 12
          || n100 > 14)) ? 'few'
      : (t0 && n10 == 0 || (n10 >= 5 && n10 <= 9)
          || (n100 >= 11 && n100 <= 14)) ? 'many'
      : 'other';
},

bem: _cp[1],

bez: _cp[1],

bg: _cp[1],

bh: _cp[2],

bm: _cp[0],

bn: function(n, ord) {
  if (ord) return ((n == 1 || n == 5 || n == 7 || n == 8 || n == 9
          || n == 10)) ? 'one'
      : ((n == 2
          || n == 3)) ? 'two'
      : (n == 4) ? 'few'
      : (n == 6) ? 'many'
      : 'other';
  return (n >= 0 && n <= 1) ? 'one' : 'other';
},

bo: _cp[0],

br: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2),
      n1000000 = t0 && s[0].slice(-6);
  if (ord) return 'other';
  return (n10 == 1 && n100 != 11 && n100 != 71 && n100 != 91) ? 'one'
      : (n10 == 2 && n100 != 12 && n100 != 72 && n100 != 92) ? 'two'
      : (((n10 == 3 || n10 == 4) || n10 == 9) && (n100 < 10
          || n100 > 19) && (n100 < 70 || n100 > 79) && (n100 < 90
          || n100 > 99)) ? 'few'
      : (n != 0 && t0 && n1000000 == 0) ? 'many'
      : 'other';
},

brx: _cp[1],

bs: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1],
      i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1), f100 = f.slice(-2);
  if (ord) return 'other';
  return (v0 && i10 == 1 && i100 != 11
          || f10 == 1 && f100 != 11) ? 'one'
      : (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14)
          || (f10 >= 2 && f10 <= 4) && (f100 < 12
          || f100 > 14)) ? 'few'
      : 'other';
},

ca: function(n, ord) {
  var s = String(n).split('.'), v0 = !s[1];
  if (ord) return ((n == 1
          || n == 3)) ? 'one'
      : (n == 2) ? 'two'
      : (n == 4) ? 'few'
      : 'other';
  return (n == 1 && v0) ? 'one' : 'other';
},

ce: _cp[1],

cgg: _cp[1],

chr: _cp[1],

ckb: _cp[1],

cs: function(n, ord) {
  var s = String(n).split('.'), i = s[0], v0 = !s[1];
  if (ord) return 'other';
  return (n == 1 && v0) ? 'one'
      : ((i >= 2 && i <= 4) && v0) ? 'few'
      : (!v0) ? 'many'
      : 'other';
},

cy: function(n, ord) {
  if (ord) return ((n == 0 || n == 7 || n == 8
          || n == 9)) ? 'zero'
      : (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : ((n == 3
          || n == 4)) ? 'few'
      : ((n == 5
          || n == 6)) ? 'many'
      : 'other';
  return (n == 0) ? 'zero'
      : (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : (n == 3) ? 'few'
      : (n == 6) ? 'many'
      : 'other';
},

da: function(n, ord) {
  var s = String(n).split('.'), i = s[0], t0 = Number(s[0]) == n;
  if (ord) return 'other';
  return (n == 1 || !t0 && (i == 0
          || i == 1)) ? 'one' : 'other';
},

de: _cp[3],

dsb: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1],
      i100 = i.slice(-2), f100 = f.slice(-2);
  if (ord) return 'other';
  return (v0 && i100 == 1
          || f100 == 1) ? 'one'
      : (v0 && i100 == 2
          || f100 == 2) ? 'two'
      : (v0 && (i100 == 3 || i100 == 4) || (f100 == 3
          || f100 == 4)) ? 'few'
      : 'other';
},

dv: _cp[1],

dz: _cp[0],

ee: _cp[1],

el: _cp[1],

en: function(n, ord) {
  var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2);
  if (ord) return (n10 == 1 && n100 != 11) ? 'one'
      : (n10 == 2 && n100 != 12) ? 'two'
      : (n10 == 3 && n100 != 13) ? 'few'
      : 'other';
  return (n == 1 && v0) ? 'one' : 'other';
},

eo: _cp[1],

es: _cp[1],

et: _cp[3],

eu: _cp[1],

fa: function(n, ord) {
  if (ord) return 'other';
  return (n >= 0 && n <= 1) ? 'one' : 'other';
},

ff: function(n, ord) {
  if (ord) return 'other';
  return (n >= 0 && n < 2) ? 'one' : 'other';
},

fi: _cp[3],

fil: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1],
      i10 = i.slice(-1), f10 = f.slice(-1);
  if (ord) return (n == 1) ? 'one' : 'other';
  return (v0 && (i == 1 || i == 2 || i == 3)
          || v0 && i10 != 4 && i10 != 6 && i10 != 9
          || !v0 && f10 != 4 && f10 != 6 && f10 != 9) ? 'one' : 'other';
},

fo: _cp[1],

fr: function(n, ord) {
  if (ord) return (n == 1) ? 'one' : 'other';
  return (n >= 0 && n < 2) ? 'one' : 'other';
},

fur: _cp[1],

fy: _cp[3],

ga: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n;
  if (ord) return (n == 1) ? 'one' : 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : ((t0 && n >= 3 && n <= 6)) ? 'few'
      : ((t0 && n >= 7 && n <= 10)) ? 'many'
      : 'other';
},

gd: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n;
  if (ord) return 'other';
  return ((n == 1
          || n == 11)) ? 'one'
      : ((n == 2
          || n == 12)) ? 'two'
      : (((t0 && n >= 3 && n <= 10)
          || (t0 && n >= 13 && n <= 19))) ? 'few'
      : 'other';
},

gl: _cp[3],

gsw: _cp[1],

gu: function(n, ord) {
  if (ord) return (n == 1) ? 'one'
      : ((n == 2
          || n == 3)) ? 'two'
      : (n == 4) ? 'few'
      : (n == 6) ? 'many'
      : 'other';
  return (n >= 0 && n <= 1) ? 'one' : 'other';
},

guw: _cp[2],

gv: function(n, ord) {
  var s = String(n).split('.'), i = s[0], v0 = !s[1], i10 = i.slice(-1),
      i100 = i.slice(-2);
  if (ord) return 'other';
  return (v0 && i10 == 1) ? 'one'
      : (v0 && i10 == 2) ? 'two'
      : (v0 && (i100 == 0 || i100 == 20 || i100 == 40 || i100 == 60
          || i100 == 80)) ? 'few'
      : (!v0) ? 'many'
      : 'other';
},

ha: _cp[1],

haw: _cp[1],

he: function(n, ord) {
  var s = String(n).split('.'), i = s[0], v0 = !s[1], t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1);
  if (ord) return 'other';
  return (n == 1 && v0) ? 'one'
      : (i == 2 && v0) ? 'two'
      : (v0 && (n < 0
          || n > 10) && t0 && n10 == 0) ? 'many'
      : 'other';
},

hi: function(n, ord) {
  if (ord) return (n == 1) ? 'one'
      : ((n == 2
          || n == 3)) ? 'two'
      : (n == 4) ? 'few'
      : (n == 6) ? 'many'
      : 'other';
  return (n >= 0 && n <= 1) ? 'one' : 'other';
},

hr: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1],
      i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1), f100 = f.slice(-2);
  if (ord) return 'other';
  return (v0 && i10 == 1 && i100 != 11
          || f10 == 1 && f100 != 11) ? 'one'
      : (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14)
          || (f10 >= 2 && f10 <= 4) && (f100 < 12
          || f100 > 14)) ? 'few'
      : 'other';
},

hsb: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1],
      i100 = i.slice(-2), f100 = f.slice(-2);
  if (ord) return 'other';
  return (v0 && i100 == 1
          || f100 == 1) ? 'one'
      : (v0 && i100 == 2
          || f100 == 2) ? 'two'
      : (v0 && (i100 == 3 || i100 == 4) || (f100 == 3
          || f100 == 4)) ? 'few'
      : 'other';
},

hu: function(n, ord) {
  if (ord) return ((n == 1
          || n == 5)) ? 'one' : 'other';
  return (n == 1) ? 'one' : 'other';
},

hy: function(n, ord) {
  if (ord) return (n == 1) ? 'one' : 'other';
  return (n >= 0 && n < 2) ? 'one' : 'other';
},

id: _cp[0],

ig: _cp[0],

ii: _cp[0],

"in": _cp[0],

is: function(n, ord) {
  var s = String(n).split('.'), i = s[0], t0 = Number(s[0]) == n,
      i10 = i.slice(-1), i100 = i.slice(-2);
  if (ord) return 'other';
  return (t0 && i10 == 1 && i100 != 11
          || !t0) ? 'one' : 'other';
},

it: function(n, ord) {
  var s = String(n).split('.'), v0 = !s[1];
  if (ord) return ((n == 11 || n == 8 || n == 80
          || n == 800)) ? 'many' : 'other';
  return (n == 1 && v0) ? 'one' : 'other';
},

iu: function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : 'other';
},

iw: function(n, ord) {
  var s = String(n).split('.'), i = s[0], v0 = !s[1], t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1);
  if (ord) return 'other';
  return (n == 1 && v0) ? 'one'
      : (i == 2 && v0) ? 'two'
      : (v0 && (n < 0
          || n > 10) && t0 && n10 == 0) ? 'many'
      : 'other';
},

ja: _cp[0],

jbo: _cp[0],

jgo: _cp[1],

ji: _cp[3],

jmc: _cp[1],

jv: _cp[0],

jw: _cp[0],

ka: function(n, ord) {
  var s = String(n).split('.'), i = s[0], i100 = i.slice(-2);
  if (ord) return (i == 1) ? 'one'
      : (i == 0 || ((i100 >= 2 && i100 <= 20) || i100 == 40 || i100 == 60
          || i100 == 80)) ? 'many'
      : 'other';
  return (n == 1) ? 'one' : 'other';
},

kab: function(n, ord) {
  if (ord) return 'other';
  return (n >= 0 && n < 2) ? 'one' : 'other';
},

kaj: _cp[1],

kcg: _cp[1],

kde: _cp[0],

kea: _cp[0],

kk: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1);
  if (ord) return (n10 == 6 || n10 == 9
          || t0 && n10 == 0 && n != 0) ? 'many' : 'other';
  return (n == 1) ? 'one' : 'other';
},

kkj: _cp[1],

kl: _cp[1],

km: _cp[0],

kn: function(n, ord) {
  if (ord) return 'other';
  return (n >= 0 && n <= 1) ? 'one' : 'other';
},

ko: _cp[0],

ks: _cp[1],

ksb: _cp[1],

ksh: function(n, ord) {
  if (ord) return 'other';
  return (n == 0) ? 'zero'
      : (n == 1) ? 'one'
      : 'other';
},

ku: _cp[1],

kw: function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : 'other';
},

ky: _cp[1],

lag: function(n, ord) {
  var s = String(n).split('.'), i = s[0];
  if (ord) return 'other';
  return (n == 0) ? 'zero'
      : ((i == 0
          || i == 1) && n != 0) ? 'one'
      : 'other';
},

lb: _cp[1],

lg: _cp[1],

lkt: _cp[0],

ln: _cp[2],

lo: function(n, ord) {
  if (ord) return (n == 1) ? 'one' : 'other';
  return 'other';
},

lt: function(n, ord) {
  var s = String(n).split('.'), f = s[1] || '', t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2);
  if (ord) return 'other';
  return (n10 == 1 && (n100 < 11
          || n100 > 19)) ? 'one'
      : ((n10 >= 2 && n10 <= 9) && (n100 < 11
          || n100 > 19)) ? 'few'
      : (f != 0) ? 'many'
      : 'other';
},

lv: function(n, ord) {
  var s = String(n).split('.'), f = s[1] || '', v = f.length,
      t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1),
      n100 = t0 && s[0].slice(-2), f100 = f.slice(-2), f10 = f.slice(-1);
  if (ord) return 'other';
  return (t0 && n10 == 0 || (n100 >= 11 && n100 <= 19)
          || v == 2 && (f100 >= 11 && f100 <= 19)) ? 'zero'
      : (n10 == 1 && n100 != 11 || v == 2 && f10 == 1 && f100 != 11
          || v != 2 && f10 == 1) ? 'one'
      : 'other';
},

mas: _cp[1],

mg: _cp[2],

mgo: _cp[1],

mk: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1],
      i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1);
  if (ord) return (i10 == 1 && i100 != 11) ? 'one'
      : (i10 == 2 && i100 != 12) ? 'two'
      : ((i10 == 7
          || i10 == 8) && i100 != 17 && i100 != 18) ? 'many'
      : 'other';
  return (v0 && i10 == 1
          || f10 == 1) ? 'one' : 'other';
},

ml: _cp[1],

mn: _cp[1],

mo: function(n, ord) {
  var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n,
      n100 = t0 && s[0].slice(-2);
  if (ord) return (n == 1) ? 'one' : 'other';
  return (n == 1 && v0) ? 'one'
      : (!v0 || n == 0
          || n != 1 && (n100 >= 1 && n100 <= 19)) ? 'few'
      : 'other';
},

mr: function(n, ord) {
  if (ord) return (n == 1) ? 'one'
      : ((n == 2
          || n == 3)) ? 'two'
      : (n == 4) ? 'few'
      : 'other';
  return (n >= 0 && n <= 1) ? 'one' : 'other';
},

ms: function(n, ord) {
  if (ord) return (n == 1) ? 'one' : 'other';
  return 'other';
},

mt: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n,
      n100 = t0 && s[0].slice(-2);
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 0
          || (n100 >= 2 && n100 <= 10)) ? 'few'
      : ((n100 >= 11 && n100 <= 19)) ? 'many'
      : 'other';
},

my: _cp[0],

nah: _cp[1],

naq: function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : 'other';
},

nb: _cp[1],

nd: _cp[1],

ne: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n;
  if (ord) return ((t0 && n >= 1 && n <= 4)) ? 'one' : 'other';
  return (n == 1) ? 'one' : 'other';
},

nl: _cp[3],

nn: _cp[1],

nnh: _cp[1],

no: _cp[1],

nqo: _cp[0],

nr: _cp[1],

nso: _cp[2],

ny: _cp[1],

nyn: _cp[1],

om: _cp[1],

or: _cp[1],

os: _cp[1],

pa: _cp[2],

pap: _cp[1],

pl: function(n, ord) {
  var s = String(n).split('.'), i = s[0], v0 = !s[1], i10 = i.slice(-1),
      i100 = i.slice(-2);
  if (ord) return 'other';
  return (n == 1 && v0) ? 'one'
      : (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12
          || i100 > 14)) ? 'few'
      : (v0 && i != 1 && (i10 == 0 || i10 == 1)
          || v0 && (i10 >= 5 && i10 <= 9)
          || v0 && (i100 >= 12 && i100 <= 14)) ? 'many'
      : 'other';
},

prg: function(n, ord) {
  var s = String(n).split('.'), f = s[1] || '', v = f.length,
      t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1),
      n100 = t0 && s[0].slice(-2), f100 = f.slice(-2), f10 = f.slice(-1);
  if (ord) return 'other';
  return (t0 && n10 == 0 || (n100 >= 11 && n100 <= 19)
          || v == 2 && (f100 >= 11 && f100 <= 19)) ? 'zero'
      : (n10 == 1 && n100 != 11 || v == 2 && f10 == 1 && f100 != 11
          || v != 2 && f10 == 1) ? 'one'
      : 'other';
},

ps: _cp[1],

pt: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n;
  if (ord) return 'other';
  return ((t0 && n >= 0 && n <= 2) && n != 2) ? 'one' : 'other';
},

"pt-PT": _cp[3],

rm: _cp[1],

ro: function(n, ord) {
  var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n,
      n100 = t0 && s[0].slice(-2);
  if (ord) return (n == 1) ? 'one' : 'other';
  return (n == 1 && v0) ? 'one'
      : (!v0 || n == 0
          || n != 1 && (n100 >= 1 && n100 <= 19)) ? 'few'
      : 'other';
},

rof: _cp[1],

root: _cp[0],

ru: function(n, ord) {
  var s = String(n).split('.'), i = s[0], v0 = !s[1], i10 = i.slice(-1),
      i100 = i.slice(-2);
  if (ord) return 'other';
  return (v0 && i10 == 1 && i100 != 11) ? 'one'
      : (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12
          || i100 > 14)) ? 'few'
      : (v0 && i10 == 0 || v0 && (i10 >= 5 && i10 <= 9)
          || v0 && (i100 >= 11 && i100 <= 14)) ? 'many'
      : 'other';
},

rwk: _cp[1],

sah: _cp[0],

saq: _cp[1],

sdh: _cp[1],

se: function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : 'other';
},

seh: _cp[1],

ses: _cp[0],

sg: _cp[0],

sh: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1],
      i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1), f100 = f.slice(-2);
  if (ord) return 'other';
  return (v0 && i10 == 1 && i100 != 11
          || f10 == 1 && f100 != 11) ? 'one'
      : (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14)
          || (f10 >= 2 && f10 <= 4) && (f100 < 12
          || f100 > 14)) ? 'few'
      : 'other';
},

shi: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n;
  if (ord) return 'other';
  return (n >= 0 && n <= 1) ? 'one'
      : ((t0 && n >= 2 && n <= 10)) ? 'few'
      : 'other';
},

si: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '';
  if (ord) return 'other';
  return ((n == 0 || n == 1)
          || i == 0 && f == 1) ? 'one' : 'other';
},

sk: function(n, ord) {
  var s = String(n).split('.'), i = s[0], v0 = !s[1];
  if (ord) return 'other';
  return (n == 1 && v0) ? 'one'
      : ((i >= 2 && i <= 4) && v0) ? 'few'
      : (!v0) ? 'many'
      : 'other';
},

sl: function(n, ord) {
  var s = String(n).split('.'), i = s[0], v0 = !s[1], i100 = i.slice(-2);
  if (ord) return 'other';
  return (v0 && i100 == 1) ? 'one'
      : (v0 && i100 == 2) ? 'two'
      : (v0 && (i100 == 3 || i100 == 4)
          || !v0) ? 'few'
      : 'other';
},

sma: function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : 'other';
},

smi: function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : 'other';
},

smj: function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : 'other';
},

smn: function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : 'other';
},

sms: function(n, ord) {
  if (ord) return 'other';
  return (n == 1) ? 'one'
      : (n == 2) ? 'two'
      : 'other';
},

sn: _cp[1],

so: _cp[1],

sq: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2);
  if (ord) return (n == 1) ? 'one'
      : (n10 == 4 && n100 != 14) ? 'many'
      : 'other';
  return (n == 1) ? 'one' : 'other';
},

sr: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1],
      i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1), f100 = f.slice(-2);
  if (ord) return 'other';
  return (v0 && i10 == 1 && i100 != 11
          || f10 == 1 && f100 != 11) ? 'one'
      : (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14)
          || (f10 >= 2 && f10 <= 4) && (f100 < 12
          || f100 > 14)) ? 'few'
      : 'other';
},

ss: _cp[1],

ssy: _cp[1],

st: _cp[1],

sv: function(n, ord) {
  var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2);
  if (ord) return ((n10 == 1
          || n10 == 2) && n100 != 11 && n100 != 12) ? 'one' : 'other';
  return (n == 1 && v0) ? 'one' : 'other';
},

sw: _cp[3],

syr: _cp[1],

ta: _cp[1],

te: _cp[1],

teo: _cp[1],

th: _cp[0],

ti: _cp[2],

tig: _cp[1],

tk: _cp[1],

tl: function(n, ord) {
  var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1],
      i10 = i.slice(-1), f10 = f.slice(-1);
  if (ord) return (n == 1) ? 'one' : 'other';
  return (v0 && (i == 1 || i == 2 || i == 3)
          || v0 && i10 != 4 && i10 != 6 && i10 != 9
          || !v0 && f10 != 4 && f10 != 6 && f10 != 9) ? 'one' : 'other';
},

tn: _cp[1],

to: _cp[0],

tr: _cp[1],

ts: _cp[1],

tzm: function(n, ord) {
  var s = String(n).split('.'), t0 = Number(s[0]) == n;
  if (ord) return 'other';
  return ((n == 0 || n == 1)
          || (t0 && n >= 11 && n <= 99)) ? 'one' : 'other';
},

ug: _cp[1],

uk: function(n, ord) {
  var s = String(n).split('.'), i = s[0], v0 = !s[1], t0 = Number(s[0]) == n,
      n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2), i10 = i.slice(-1),
      i100 = i.slice(-2);
  if (ord) return (n10 == 3 && n100 != 13) ? 'few' : 'other';
  return (v0 && i10 == 1 && i100 != 11) ? 'one'
      : (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12
          || i100 > 14)) ? 'few'
      : (v0 && i10 == 0 || v0 && (i10 >= 5 && i10 <= 9)
          || v0 && (i100 >= 11 && i100 <= 14)) ? 'many'
      : 'other';
},

ur: _cp[3],

uz: _cp[1],

ve: _cp[1],

vi: function(n, ord) {
  if (ord) return (n == 1) ? 'one' : 'other';
  return 'other';
},

vo: _cp[1],

vun: _cp[1],

wa: _cp[2],

wae: _cp[1],

wo: _cp[0],

xh: _cp[1],

xog: _cp[1],

yi: _cp[3],

yo: _cp[0],

zh: _cp[0],

zu: function(n, ord) {
  if (ord) return 'other';
  return (n >= 0 && n <= 1) ? 'one' : 'other';
}
}));

},{}],8:[function(require,module,exports){
/*
 * Generated by PEG.js 0.10.0.
 *
 * http://pegjs.org/
 */

"use strict";

function peg$subclass(child, parent) {
  function ctor() { this.constructor = child; }
  ctor.prototype = parent.prototype;
  child.prototype = new ctor();
}

function peg$SyntaxError(message, expected, found, location) {
  this.message  = message;
  this.expected = expected;
  this.found    = found;
  this.location = location;
  this.name     = "SyntaxError";

  if (typeof Error.captureStackTrace === "function") {
    Error.captureStackTrace(this, peg$SyntaxError);
  }
}

peg$subclass(peg$SyntaxError, Error);

peg$SyntaxError.buildMessage = function(expected, found) {
  var DESCRIBE_EXPECTATION_FNS = {
        literal: function(expectation) {
          return "\"" + literalEscape(expectation.text) + "\"";
        },

        "class": function(expectation) {
          var escapedParts = "",
              i;

          for (i = 0; i < expectation.parts.length; i++) {
            escapedParts += expectation.parts[i] instanceof Array
              ? classEscape(expectation.parts[i][0]) + "-" + classEscape(expectation.parts[i][1])
              : classEscape(expectation.parts[i]);
          }

          return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
        },

        any: function(expectation) {
          return "any character";
        },

        end: function(expectation) {
          return "end of input";
        },

        other: function(expectation) {
          return expectation.description;
        }
      };

  function hex(ch) {
    return ch.charCodeAt(0).toString(16).toUpperCase();
  }

  function literalEscape(s) {
    return s
      .replace(/\\/g, '\\\\')
      .replace(/"/g,  '\\"')
      .replace(/\0/g, '\\0')
      .replace(/\t/g, '\\t')
      .replace(/\n/g, '\\n')
      .replace(/\r/g, '\\r')
      .replace(/[\x00-\x0F]/g,          function(ch) { return '\\x0' + hex(ch); })
      .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x'  + hex(ch); });
  }

  function classEscape(s) {
    return s
      .replace(/\\/g, '\\\\')
      .replace(/\]/g, '\\]')
      .replace(/\^/g, '\\^')
      .replace(/-/g,  '\\-')
      .replace(/\0/g, '\\0')
      .replace(/\t/g, '\\t')
      .replace(/\n/g, '\\n')
      .replace(/\r/g, '\\r')
      .replace(/[\x00-\x0F]/g,          function(ch) { return '\\x0' + hex(ch); })
      .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x'  + hex(ch); });
  }

  function describeExpectation(expectation) {
    return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
  }

  function describeExpected(expected) {
    var descriptions = new Array(expected.length),
        i, j;

    for (i = 0; i < expected.length; i++) {
      descriptions[i] = describeExpectation(expected[i]);
    }

    descriptions.sort();

    if (descriptions.length > 0) {
      for (i = 1, j = 1; i < descriptions.length; i++) {
        if (descriptions[i - 1] !== descriptions[i]) {
          descriptions[j] = descriptions[i];
          j++;
        }
      }
      descriptions.length = j;
    }

    switch (descriptions.length) {
      case 1:
        return descriptions[0];

      case 2:
        return descriptions[0] + " or " + descriptions[1];

      default:
        return descriptions.slice(0, -1).join(", ")
          + ", or "
          + descriptions[descriptions.length - 1];
    }
  }

  function describeFound(found) {
    return found ? "\"" + literalEscape(found) + "\"" : "end of input";
  }

  return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
};

function peg$parse(input, options) {
  options = options !== void 0 ? options : {};

  var peg$FAILED = {},

      peg$startRuleFunctions = { start: peg$parsestart },
      peg$startRuleFunction  = peg$parsestart,

      peg$c0 = "#",
      peg$c1 = peg$literalExpectation("#", false),
      peg$c2 = function() { return { type: 'octothorpe' }; },
      peg$c3 = function(str) { return str.join(''); },
      peg$c4 = "{",
      peg$c5 = peg$literalExpectation("{", false),
      peg$c6 = "}",
      peg$c7 = peg$literalExpectation("}", false),
      peg$c8 = function(arg) {
          return {
            type: 'argument',
            arg: arg
          };
        },
      peg$c9 = ",",
      peg$c10 = peg$literalExpectation(",", false),
      peg$c11 = "select",
      peg$c12 = peg$literalExpectation("select", false),
      peg$c13 = function(arg, cases) {
          return {
            type: 'select',
            arg: arg,
            cases: cases
          };
        },
      peg$c14 = "plural",
      peg$c15 = peg$literalExpectation("plural", false),
      peg$c16 = "selectordinal",
      peg$c17 = peg$literalExpectation("selectordinal", false),
      peg$c18 = function(arg, type, offset, cases) {
          var ls = ((type === 'selectordinal') ? options.ordinal : options.cardinal)
                   || ['zero', 'one', 'two', 'few', 'many', 'other'];
          if (ls && ls.length) cases.forEach(function(c) {
            if (isNaN(c.key) && ls.indexOf(c.key) < 0) throw new Error(
              'Invalid key `' + c.key + '` for argument `' + arg + '`.' +
              ' Valid ' + type + ' keys for this locale are `' + ls.join('`, `') +
              '`, and explicit keys like `=0`.');
          });
          return {
            type: type,
            arg: arg,
            offset: offset || 0,
            cases: cases
          };
        },
      peg$c19 = function(arg, key, params) {
          return {
            type: 'function',
            arg: arg,
            key: key,
            params: params
          };
        },
      peg$c20 = /^[0-9a-zA-Z$_]/,
      peg$c21 = peg$classExpectation([["0", "9"], ["a", "z"], ["A", "Z"], "$", "_"], false, false),
      peg$c22 = /^[^ \t\n\r,.+={}]/,
      peg$c23 = peg$classExpectation([" ", "\t", "\n", "\r", ",", ".", "+", "=", "{", "}"], true, false),
      peg$c24 = function(key, tokens) { return { key: key, tokens: tokens }; },
      peg$c25 = function(tokens) { return tokens; },
      peg$c26 = "offset",
      peg$c27 = peg$literalExpectation("offset", false),
      peg$c28 = ":",
      peg$c29 = peg$literalExpectation(":", false),
      peg$c30 = function(d) { return d; },
      peg$c31 = "=",
      peg$c32 = peg$literalExpectation("=", false),
      peg$c33 = function(p) { return p; },
      peg$c34 = /^[^{}#\\\0-\x08\x0E-\x1F\x7F]/,
      peg$c35 = peg$classExpectation(["{", "}", "#", "\\", ["\0", "\b"], ["\x0E", "\x1F"], "\x7F"], true, false),
      peg$c36 = "\\\\",
      peg$c37 = peg$literalExpectation("\\\\", false),
      peg$c38 = function() { return '\\'; },
      peg$c39 = "\\#",
      peg$c40 = peg$literalExpectation("\\#", false),
      peg$c41 = function() { return '#'; },
      peg$c42 = "\\{",
      peg$c43 = peg$literalExpectation("\\{", false),
      peg$c44 = function() { return '\u007B'; },
      peg$c45 = "\\}",
      peg$c46 = peg$literalExpectation("\\}", false),
      peg$c47 = function() { return '\u007D'; },
      peg$c48 = "\\u",
      peg$c49 = peg$literalExpectation("\\u", false),
      peg$c50 = function(h1, h2, h3, h4) {
            return String.fromCharCode(parseInt('0x' + h1 + h2 + h3 + h4));
          },
      peg$c51 = /^[0-9]/,
      peg$c52 = peg$classExpectation([["0", "9"]], false, false),
      peg$c53 = /^[0-9a-fA-F]/,
      peg$c54 = peg$classExpectation([["0", "9"], ["a", "f"], ["A", "F"]], false, false),
      peg$c55 = /^[ \t\n\r]/,
      peg$c56 = peg$classExpectation([" ", "\t", "\n", "\r"], false, false),

      peg$currPos          = 0,
      peg$savedPos         = 0,
      peg$posDetailsCache  = [{ line: 1, column: 1 }],
      peg$maxFailPos       = 0,
      peg$maxFailExpected  = [],
      peg$silentFails      = 0,

      peg$result;

  if ("startRule" in options) {
    if (!(options.startRule in peg$startRuleFunctions)) {
      throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
    }

    peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
  }

  function text() {
    return input.substring(peg$savedPos, peg$currPos);
  }

  function location() {
    return peg$computeLocation(peg$savedPos, peg$currPos);
  }

  function expected(description, location) {
    location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)

    throw peg$buildStructuredError(
      [peg$otherExpectation(description)],
      input.substring(peg$savedPos, peg$currPos),
      location
    );
  }

  function error(message, location) {
    location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)

    throw peg$buildSimpleError(message, location);
  }

  function peg$literalExpectation(text, ignoreCase) {
    return { type: "literal", text: text, ignoreCase: ignoreCase };
  }

  function peg$classExpectation(parts, inverted, ignoreCase) {
    return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };
  }

  function peg$anyExpectation() {
    return { type: "any" };
  }

  function peg$endExpectation() {
    return { type: "end" };
  }

  function peg$otherExpectation(description) {
    return { type: "other", description: description };
  }

  function peg$computePosDetails(pos) {
    var details = peg$posDetailsCache[pos], p;

    if (details) {
      return details;
    } else {
      p = pos - 1;
      while (!peg$posDetailsCache[p]) {
        p--;
      }

      details = peg$posDetailsCache[p];
      details = {
        line:   details.line,
        column: details.column
      };

      while (p < pos) {
        if (input.charCodeAt(p) === 10) {
          details.line++;
          details.column = 1;
        } else {
          details.column++;
        }

        p++;
      }

      peg$posDetailsCache[pos] = details;
      return details;
    }
  }

  function peg$computeLocation(startPos, endPos) {
    var startPosDetails = peg$computePosDetails(startPos),
        endPosDetails   = peg$computePosDetails(endPos);

    return {
      start: {
        offset: startPos,
        line:   startPosDetails.line,
        column: startPosDetails.column
      },
      end: {
        offset: endPos,
        line:   endPosDetails.line,
        column: endPosDetails.column
      }
    };
  }

  function peg$fail(expected) {
    if (peg$currPos < peg$maxFailPos) { return; }

    if (peg$currPos > peg$maxFailPos) {
      peg$maxFailPos = peg$currPos;
      peg$maxFailExpected = [];
    }

    peg$maxFailExpected.push(expected);
  }

  function peg$buildSimpleError(message, location) {
    return new peg$SyntaxError(message, null, null, location);
  }

  function peg$buildStructuredError(expected, found, location) {
    return new peg$SyntaxError(
      peg$SyntaxError.buildMessage(expected, found),
      expected,
      found,
      location
    );
  }

  function peg$parsestart() {
    var s0, s1;

    s0 = [];
    s1 = peg$parsetoken();
    while (s1 !== peg$FAILED) {
      s0.push(s1);
      s1 = peg$parsetoken();
    }

    return s0;
  }

  function peg$parsetoken() {
    var s0, s1, s2;

    s0 = peg$parseargument();
    if (s0 === peg$FAILED) {
      s0 = peg$parseselect();
      if (s0 === peg$FAILED) {
        s0 = peg$parseplural();
        if (s0 === peg$FAILED) {
          s0 = peg$parsefunction();
          if (s0 === peg$FAILED) {
            s0 = peg$currPos;
            if (input.charCodeAt(peg$currPos) === 35) {
              s1 = peg$c0;
              peg$currPos++;
            } else {
              s1 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c1); }
            }
            if (s1 !== peg$FAILED) {
              peg$savedPos = s0;
              s1 = peg$c2();
            }
            s0 = s1;
            if (s0 === peg$FAILED) {
              s0 = peg$currPos;
              s1 = [];
              s2 = peg$parsechar();
              if (s2 !== peg$FAILED) {
                while (s2 !== peg$FAILED) {
                  s1.push(s2);
                  s2 = peg$parsechar();
                }
              } else {
                s1 = peg$FAILED;
              }
              if (s1 !== peg$FAILED) {
                peg$savedPos = s0;
                s1 = peg$c3(s1);
              }
              s0 = s1;
            }
          }
        }
      }
    }

    return s0;
  }

  function peg$parseargument() {
    var s0, s1, s2, s3, s4, s5;

    s0 = peg$currPos;
    if (input.charCodeAt(peg$currPos) === 123) {
      s1 = peg$c4;
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c5); }
    }
    if (s1 !== peg$FAILED) {
      s2 = peg$parse_();
      if (s2 !== peg$FAILED) {
        s3 = peg$parseid();
        if (s3 !== peg$FAILED) {
          s4 = peg$parse_();
          if (s4 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 125) {
              s5 = peg$c6;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c7); }
            }
            if (s5 !== peg$FAILED) {
              peg$savedPos = s0;
              s1 = peg$c8(s3);
              s0 = s1;
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseselect() {
    var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13;

    s0 = peg$currPos;
    if (input.charCodeAt(peg$currPos) === 123) {
      s1 = peg$c4;
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c5); }
    }
    if (s1 !== peg$FAILED) {
      s2 = peg$parse_();
      if (s2 !== peg$FAILED) {
        s3 = peg$parseid();
        if (s3 !== peg$FAILED) {
          s4 = peg$parse_();
          if (s4 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 44) {
              s5 = peg$c9;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c10); }
            }
            if (s5 !== peg$FAILED) {
              s6 = peg$parse_();
              if (s6 !== peg$FAILED) {
                if (input.substr(peg$currPos, 6) === peg$c11) {
                  s7 = peg$c11;
                  peg$currPos += 6;
                } else {
                  s7 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c12); }
                }
                if (s7 !== peg$FAILED) {
                  s8 = peg$parse_();
                  if (s8 !== peg$FAILED) {
                    if (input.charCodeAt(peg$currPos) === 44) {
                      s9 = peg$c9;
                      peg$currPos++;
                    } else {
                      s9 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c10); }
                    }
                    if (s9 !== peg$FAILED) {
                      s10 = peg$parse_();
                      if (s10 !== peg$FAILED) {
                        s11 = [];
                        s12 = peg$parseselectCase();
                        if (s12 !== peg$FAILED) {
                          while (s12 !== peg$FAILED) {
                            s11.push(s12);
                            s12 = peg$parseselectCase();
                          }
                        } else {
                          s11 = peg$FAILED;
                        }
                        if (s11 !== peg$FAILED) {
                          s12 = peg$parse_();
                          if (s12 !== peg$FAILED) {
                            if (input.charCodeAt(peg$currPos) === 125) {
                              s13 = peg$c6;
                              peg$currPos++;
                            } else {
                              s13 = peg$FAILED;
                              if (peg$silentFails === 0) { peg$fail(peg$c7); }
                            }
                            if (s13 !== peg$FAILED) {
                              peg$savedPos = s0;
                              s1 = peg$c13(s3, s11);
                              s0 = s1;
                            } else {
                              peg$currPos = s0;
                              s0 = peg$FAILED;
                            }
                          } else {
                            peg$currPos = s0;
                            s0 = peg$FAILED;
                          }
                        } else {
                          peg$currPos = s0;
                          s0 = peg$FAILED;
                        }
                      } else {
                        peg$currPos = s0;
                        s0 = peg$FAILED;
                      }
                    } else {
                      peg$currPos = s0;
                      s0 = peg$FAILED;
                    }
                  } else {
                    peg$currPos = s0;
                    s0 = peg$FAILED;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$FAILED;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$FAILED;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseplural() {
    var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14;

    s0 = peg$currPos;
    if (input.charCodeAt(peg$currPos) === 123) {
      s1 = peg$c4;
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c5); }
    }
    if (s1 !== peg$FAILED) {
      s2 = peg$parse_();
      if (s2 !== peg$FAILED) {
        s3 = peg$parseid();
        if (s3 !== peg$FAILED) {
          s4 = peg$parse_();
          if (s4 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 44) {
              s5 = peg$c9;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c10); }
            }
            if (s5 !== peg$FAILED) {
              s6 = peg$parse_();
              if (s6 !== peg$FAILED) {
                if (input.substr(peg$currPos, 6) === peg$c14) {
                  s7 = peg$c14;
                  peg$currPos += 6;
                } else {
                  s7 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c15); }
                }
                if (s7 === peg$FAILED) {
                  if (input.substr(peg$currPos, 13) === peg$c16) {
                    s7 = peg$c16;
                    peg$currPos += 13;
                  } else {
                    s7 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c17); }
                  }
                }
                if (s7 !== peg$FAILED) {
                  s8 = peg$parse_();
                  if (s8 !== peg$FAILED) {
                    if (input.charCodeAt(peg$currPos) === 44) {
                      s9 = peg$c9;
                      peg$currPos++;
                    } else {
                      s9 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c10); }
                    }
                    if (s9 !== peg$FAILED) {
                      s10 = peg$parse_();
                      if (s10 !== peg$FAILED) {
                        s11 = peg$parseoffset();
                        if (s11 === peg$FAILED) {
                          s11 = null;
                        }
                        if (s11 !== peg$FAILED) {
                          s12 = [];
                          s13 = peg$parsepluralCase();
                          if (s13 !== peg$FAILED) {
                            while (s13 !== peg$FAILED) {
                              s12.push(s13);
                              s13 = peg$parsepluralCase();
                            }
                          } else {
                            s12 = peg$FAILED;
                          }
                          if (s12 !== peg$FAILED) {
                            s13 = peg$parse_();
                            if (s13 !== peg$FAILED) {
                              if (input.charCodeAt(peg$currPos) === 125) {
                                s14 = peg$c6;
                                peg$currPos++;
                              } else {
                                s14 = peg$FAILED;
                                if (peg$silentFails === 0) { peg$fail(peg$c7); }
                              }
                              if (s14 !== peg$FAILED) {
                                peg$savedPos = s0;
                                s1 = peg$c18(s3, s7, s11, s12);
                                s0 = s1;
                              } else {
                                peg$currPos = s0;
                                s0 = peg$FAILED;
                              }
                            } else {
                              peg$currPos = s0;
                              s0 = peg$FAILED;
                            }
                          } else {
                            peg$currPos = s0;
                            s0 = peg$FAILED;
                          }
                        } else {
                          peg$currPos = s0;
                          s0 = peg$FAILED;
                        }
                      } else {
                        peg$currPos = s0;
                        s0 = peg$FAILED;
                      }
                    } else {
                      peg$currPos = s0;
                      s0 = peg$FAILED;
                    }
                  } else {
                    peg$currPos = s0;
                    s0 = peg$FAILED;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$FAILED;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$FAILED;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parsefunction() {
    var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;

    s0 = peg$currPos;
    if (input.charCodeAt(peg$currPos) === 123) {
      s1 = peg$c4;
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c5); }
    }
    if (s1 !== peg$FAILED) {
      s2 = peg$parse_();
      if (s2 !== peg$FAILED) {
        s3 = peg$parseid();
        if (s3 !== peg$FAILED) {
          s4 = peg$parse_();
          if (s4 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 44) {
              s5 = peg$c9;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c10); }
            }
            if (s5 !== peg$FAILED) {
              s6 = peg$parse_();
              if (s6 !== peg$FAILED) {
                s7 = peg$parseid();
                if (s7 !== peg$FAILED) {
                  s8 = peg$parse_();
                  if (s8 !== peg$FAILED) {
                    s9 = [];
                    s10 = peg$parsefunctionParams();
                    while (s10 !== peg$FAILED) {
                      s9.push(s10);
                      s10 = peg$parsefunctionParams();
                    }
                    if (s9 !== peg$FAILED) {
                      if (input.charCodeAt(peg$currPos) === 125) {
                        s10 = peg$c6;
                        peg$currPos++;
                      } else {
                        s10 = peg$FAILED;
                        if (peg$silentFails === 0) { peg$fail(peg$c7); }
                      }
                      if (s10 !== peg$FAILED) {
                        peg$savedPos = s0;
                        s1 = peg$c19(s3, s7, s9);
                        s0 = s1;
                      } else {
                        peg$currPos = s0;
                        s0 = peg$FAILED;
                      }
                    } else {
                      peg$currPos = s0;
                      s0 = peg$FAILED;
                    }
                  } else {
                    peg$currPos = s0;
                    s0 = peg$FAILED;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$FAILED;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$FAILED;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseid() {
    var s0, s1, s2, s3, s4;

    s0 = peg$currPos;
    s1 = peg$currPos;
    if (peg$c20.test(input.charAt(peg$currPos))) {
      s2 = input.charAt(peg$currPos);
      peg$currPos++;
    } else {
      s2 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c21); }
    }
    if (s2 !== peg$FAILED) {
      s3 = [];
      if (peg$c22.test(input.charAt(peg$currPos))) {
        s4 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s4 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c23); }
      }
      while (s4 !== peg$FAILED) {
        s3.push(s4);
        if (peg$c22.test(input.charAt(peg$currPos))) {
          s4 = input.charAt(peg$currPos);
          peg$currPos++;
        } else {
          s4 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c23); }
        }
      }
      if (s3 !== peg$FAILED) {
        s2 = [s2, s3];
        s1 = s2;
      } else {
        peg$currPos = s1;
        s1 = peg$FAILED;
      }
    } else {
      peg$currPos = s1;
      s1 = peg$FAILED;
    }
    if (s1 !== peg$FAILED) {
      s0 = input.substring(s0, peg$currPos);
    } else {
      s0 = s1;
    }

    return s0;
  }

  function peg$parseselectCase() {
    var s0, s1, s2, s3, s4;

    s0 = peg$currPos;
    s1 = peg$parse_();
    if (s1 !== peg$FAILED) {
      s2 = peg$parseid();
      if (s2 !== peg$FAILED) {
        s3 = peg$parse_();
        if (s3 !== peg$FAILED) {
          s4 = peg$parsecaseTokens();
          if (s4 !== peg$FAILED) {
            peg$savedPos = s0;
            s1 = peg$c24(s2, s4);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parsepluralCase() {
    var s0, s1, s2, s3, s4;

    s0 = peg$currPos;
    s1 = peg$parse_();
    if (s1 !== peg$FAILED) {
      s2 = peg$parsepluralKey();
      if (s2 !== peg$FAILED) {
        s3 = peg$parse_();
        if (s3 !== peg$FAILED) {
          s4 = peg$parsecaseTokens();
          if (s4 !== peg$FAILED) {
            peg$savedPos = s0;
            s1 = peg$c24(s2, s4);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parsecaseTokens() {
    var s0, s1, s2, s3, s4, s5;

    s0 = peg$currPos;
    if (input.charCodeAt(peg$currPos) === 123) {
      s1 = peg$c4;
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c5); }
    }
    if (s1 !== peg$FAILED) {
      s2 = peg$currPos;
      s3 = peg$parse_();
      if (s3 !== peg$FAILED) {
        s4 = peg$currPos;
        peg$silentFails++;
        if (input.charCodeAt(peg$currPos) === 123) {
          s5 = peg$c4;
          peg$currPos++;
        } else {
          s5 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c5); }
        }
        peg$silentFails--;
        if (s5 !== peg$FAILED) {
          peg$currPos = s4;
          s4 = void 0;
        } else {
          s4 = peg$FAILED;
        }
        if (s4 !== peg$FAILED) {
          s3 = [s3, s4];
          s2 = s3;
        } else {
          peg$currPos = s2;
          s2 = peg$FAILED;
        }
      } else {
        peg$currPos = s2;
        s2 = peg$FAILED;
      }
      if (s2 === peg$FAILED) {
        s2 = null;
      }
      if (s2 !== peg$FAILED) {
        s3 = [];
        s4 = peg$parsetoken();
        while (s4 !== peg$FAILED) {
          s3.push(s4);
          s4 = peg$parsetoken();
        }
        if (s3 !== peg$FAILED) {
          s4 = peg$parse_();
          if (s4 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 125) {
              s5 = peg$c6;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c7); }
            }
            if (s5 !== peg$FAILED) {
              peg$savedPos = s0;
              s1 = peg$c25(s3);
              s0 = s1;
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseoffset() {
    var s0, s1, s2, s3, s4, s5, s6, s7;

    s0 = peg$currPos;
    s1 = peg$parse_();
    if (s1 !== peg$FAILED) {
      if (input.substr(peg$currPos, 6) === peg$c26) {
        s2 = peg$c26;
        peg$currPos += 6;
      } else {
        s2 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c27); }
      }
      if (s2 !== peg$FAILED) {
        s3 = peg$parse_();
        if (s3 !== peg$FAILED) {
          if (input.charCodeAt(peg$currPos) === 58) {
            s4 = peg$c28;
            peg$currPos++;
          } else {
            s4 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c29); }
          }
          if (s4 !== peg$FAILED) {
            s5 = peg$parse_();
            if (s5 !== peg$FAILED) {
              s6 = peg$parsedigits();
              if (s6 !== peg$FAILED) {
                s7 = peg$parse_();
                if (s7 !== peg$FAILED) {
                  peg$savedPos = s0;
                  s1 = peg$c30(s6);
                  s0 = s1;
                } else {
                  peg$currPos = s0;
                  s0 = peg$FAILED;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$FAILED;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parsepluralKey() {
    var s0, s1, s2;

    s0 = peg$parseid();
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 61) {
        s1 = peg$c31;
        peg$currPos++;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c32); }
      }
      if (s1 !== peg$FAILED) {
        s2 = peg$parsedigits();
        if (s2 !== peg$FAILED) {
          peg$savedPos = s0;
          s1 = peg$c30(s2);
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    }

    return s0;
  }

  function peg$parsefunctionParams() {
    var s0, s1, s2, s3, s4, s5;

    s0 = peg$currPos;
    s1 = peg$parse_();
    if (s1 !== peg$FAILED) {
      if (input.charCodeAt(peg$currPos) === 44) {
        s2 = peg$c9;
        peg$currPos++;
      } else {
        s2 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c10); }
      }
      if (s2 !== peg$FAILED) {
        s3 = peg$parse_();
        if (s3 !== peg$FAILED) {
          s4 = peg$parseid();
          if (s4 !== peg$FAILED) {
            s5 = peg$parse_();
            if (s5 !== peg$FAILED) {
              peg$savedPos = s0;
              s1 = peg$c33(s4);
              s0 = s1;
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parsechar() {
    var s0, s1, s2, s3, s4, s5;

    if (peg$c34.test(input.charAt(peg$currPos))) {
      s0 = input.charAt(peg$currPos);
      peg$currPos++;
    } else {
      s0 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c35); }
    }
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.substr(peg$currPos, 2) === peg$c36) {
        s1 = peg$c36;
        peg$currPos += 2;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c37); }
      }
      if (s1 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c38();
      }
      s0 = s1;
      if (s0 === peg$FAILED) {
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 2) === peg$c39) {
          s1 = peg$c39;
          peg$currPos += 2;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c40); }
        }
        if (s1 !== peg$FAILED) {
          peg$savedPos = s0;
          s1 = peg$c41();
        }
        s0 = s1;
        if (s0 === peg$FAILED) {
          s0 = peg$currPos;
          if (input.substr(peg$currPos, 2) === peg$c42) {
            s1 = peg$c42;
            peg$currPos += 2;
          } else {
            s1 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c43); }
          }
          if (s1 !== peg$FAILED) {
            peg$savedPos = s0;
            s1 = peg$c44();
          }
          s0 = s1;
          if (s0 === peg$FAILED) {
            s0 = peg$currPos;
            if (input.substr(peg$currPos, 2) === peg$c45) {
              s1 = peg$c45;
              peg$currPos += 2;
            } else {
              s1 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c46); }
            }
            if (s1 !== peg$FAILED) {
              peg$savedPos = s0;
              s1 = peg$c47();
            }
            s0 = s1;
            if (s0 === peg$FAILED) {
              s0 = peg$currPos;
              if (input.substr(peg$currPos, 2) === peg$c48) {
                s1 = peg$c48;
                peg$currPos += 2;
              } else {
                s1 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c49); }
              }
              if (s1 !== peg$FAILED) {
                s2 = peg$parsehexDigit();
                if (s2 !== peg$FAILED) {
                  s3 = peg$parsehexDigit();
                  if (s3 !== peg$FAILED) {
                    s4 = peg$parsehexDigit();
                    if (s4 !== peg$FAILED) {
                      s5 = peg$parsehexDigit();
                      if (s5 !== peg$FAILED) {
                        peg$savedPos = s0;
                        s1 = peg$c50(s2, s3, s4, s5);
                        s0 = s1;
                      } else {
                        peg$currPos = s0;
                        s0 = peg$FAILED;
                      }
                    } else {
                      peg$currPos = s0;
                      s0 = peg$FAILED;
                    }
                  } else {
                    peg$currPos = s0;
                    s0 = peg$FAILED;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$FAILED;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$FAILED;
              }
            }
          }
        }
      }
    }

    return s0;
  }

  function peg$parsedigits() {
    var s0, s1, s2;

    s0 = peg$currPos;
    s1 = [];
    if (peg$c51.test(input.charAt(peg$currPos))) {
      s2 = input.charAt(peg$currPos);
      peg$currPos++;
    } else {
      s2 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c52); }
    }
    if (s2 !== peg$FAILED) {
      while (s2 !== peg$FAILED) {
        s1.push(s2);
        if (peg$c51.test(input.charAt(peg$currPos))) {
          s2 = input.charAt(peg$currPos);
          peg$currPos++;
        } else {
          s2 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c52); }
        }
      }
    } else {
      s1 = peg$FAILED;
    }
    if (s1 !== peg$FAILED) {
      s0 = input.substring(s0, peg$currPos);
    } else {
      s0 = s1;
    }

    return s0;
  }

  function peg$parsehexDigit() {
    var s0;

    if (peg$c53.test(input.charAt(peg$currPos))) {
      s0 = input.charAt(peg$currPos);
      peg$currPos++;
    } else {
      s0 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c54); }
    }

    return s0;
  }

  function peg$parse_() {
    var s0, s1, s2;

    s0 = peg$currPos;
    s1 = [];
    if (peg$c55.test(input.charAt(peg$currPos))) {
      s2 = input.charAt(peg$currPos);
      peg$currPos++;
    } else {
      s2 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c56); }
    }
    while (s2 !== peg$FAILED) {
      s1.push(s2);
      if (peg$c55.test(input.charAt(peg$currPos))) {
        s2 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s2 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c56); }
      }
    }
    if (s1 !== peg$FAILED) {
      s0 = input.substring(s0, peg$currPos);
    } else {
      s0 = s1;
    }

    return s0;
  }

  peg$result = peg$startRuleFunction();

  if (peg$result !== peg$FAILED && peg$currPos === input.length) {
    return peg$result;
  } else {
    if (peg$result !== peg$FAILED && peg$currPos < input.length) {
      peg$fail(peg$endExpectation());
    }

    throw peg$buildStructuredError(
      peg$maxFailExpected,
      peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
      peg$maxFailPos < input.length
        ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
        : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
    );
  }
}

module.exports = {
  SyntaxError: peg$SyntaxError,
  parse:       peg$parse
};

},{}],9:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};

// cached from whatever global is present so that test runners that stub it
// don't break things.  But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals.  It's inside a
// function because try/catches deoptimize in certain engines.

var cachedSetTimeout;
var cachedClearTimeout;

function defaultSetTimout() {
    throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
    throw new Error('clearTimeout has not been defined');
}
(function () {
    try {
        if (typeof setTimeout === 'function') {
            cachedSetTimeout = setTimeout;
        } else {
            cachedSetTimeout = defaultSetTimout;
        }
    } catch (e) {
        cachedSetTimeout = defaultSetTimout;
    }
    try {
        if (typeof clearTimeout === 'function') {
            cachedClearTimeout = clearTimeout;
        } else {
            cachedClearTimeout = defaultClearTimeout;
        }
    } catch (e) {
        cachedClearTimeout = defaultClearTimeout;
    }
} ())
function runTimeout(fun) {
    if (cachedSetTimeout === setTimeout) {
        //normal enviroments in sane situations
        return setTimeout(fun, 0);
    }
    // if setTimeout wasn't available but was latter defined
    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
        cachedSetTimeout = setTimeout;
        return setTimeout(fun, 0);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedSetTimeout(fun, 0);
    } catch(e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
            return cachedSetTimeout.call(null, fun, 0);
        } catch(e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
            return cachedSetTimeout.call(this, fun, 0);
        }
    }


}
function runClearTimeout(marker) {
    if (cachedClearTimeout === clearTimeout) {
        //normal enviroments in sane situations
        return clearTimeout(marker);
    }
    // if clearTimeout wasn't available but was latter defined
    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
        cachedClearTimeout = clearTimeout;
        return clearTimeout(marker);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedClearTimeout(marker);
    } catch (e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
            return cachedClearTimeout.call(null, marker);
        } catch (e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
            return cachedClearTimeout.call(this, marker);
        }
    }



}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;

function cleanUpNextTick() {
    if (!draining || !currentQueue) {
        return;
    }
    draining = false;
    if (currentQueue.length) {
        queue = currentQueue.concat(queue);
    } else {
        queueIndex = -1;
    }
    if (queue.length) {
        drainQueue();
    }
}

function drainQueue() {
    if (draining) {
        return;
    }
    var timeout = runTimeout(cleanUpNextTick);
    draining = true;

    var len = queue.length;
    while(len) {
        currentQueue = queue;
        queue = [];
        while (++queueIndex < len) {
            if (currentQueue) {
                currentQueue[queueIndex].run();
            }
        }
        queueIndex = -1;
        len = queue.length;
    }
    currentQueue = null;
    draining = false;
    runClearTimeout(timeout);
}

process.nextTick = function (fun) {
    var args = new Array(arguments.length - 1);
    if (arguments.length > 1) {
        for (var i = 1; i < arguments.length; i++) {
            args[i - 1] = arguments[i];
        }
    }
    queue.push(new Item(fun, args));
    if (queue.length === 1 && !draining) {
        runTimeout(drainQueue);
    }
};

// v8 likes predictible objects
function Item(fun, array) {
    this.fun = fun;
    this.array = array;
}
Item.prototype.run = function () {
    this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};

function noop() {}

process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;

process.binding = function (name) {
    throw new Error('process.binding is not supported');
};

process.cwd = function () { return '/' };
process.chdir = function (dir) {
    throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };

},{}],10:[function(require,module,exports){
module.exports = require('./reserved-words');

},{"./reserved-words":11}],11:[function(require,module,exports){
var assert = require('assert');

/**
 * Structure for storing keywords.
 *
 * @typedef {Object.<String,Boolean>} KeywordsHash
 */

/**
 * ECMAScript dialects.
 *
 * @const
 * @type {Object.<String,Number|String>} - keys as readable names and values as versions
 */
var DIALECTS = {
    es3: 3,
    es5: 5,
    es2015: 6,
    es7: 7,

    // aliases
    es6: 6,
    'default': 5,
    next: 6
};

/**
 * ECMAScript reserved words.
 *
 * @type {Object.<String,KeywordsHash>}
 */
var KEYWORDS = exports.KEYWORDS = {};

/**
 * Check word for being an reserved word.
 *
 * @param {String} word - word to check
 * @param {String|Number} [dialect] - dialect or version
 * @param {Boolean} [strict] - strict mode
 * @returns {?Boolean}
 */
exports.check = function check(word, dialect, strict) {
    dialect = dialect || DIALECTS["default"];
    var version = DIALECTS[dialect] || dialect;

    if (strict && version >= 5) {
        version += '-strict';
    }

    assert(KEYWORDS[version], 'Unknown dialect');

    return KEYWORDS[version][word];
};

/**
 * Reserved Words for ES3
 *
 * ECMA-262 3rd: 7.5.1
 * http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf
 *
 * @type {KeywordsHash}
 */
KEYWORDS['3'] = _hash(
    // Keyword, ECMA-262 3rd: 7.5.2
    'break    else       new     var',
    'case     finally    return  void',
    'catch    for        switch  while',
    'continue function   this    with',
    'default  if         throw',
    'delete   in         try',
    'do       instanceof typeof',
    // FutureReservedWord, ECMA-262 3rd 7.5.3
    'abstract enum       int        short',
    'boolean  export     interface  static',
    'byte     extends    long       super',
    'char     final      native     synchronized',
    'class    float      package    throws',
    'const    goto       private    transient',
    'debugger implements protected  volatile',
    'double   import     public',
    // NullLiteral & BooleanLiteral
    'null true false'
);

/**
 * Reserved Words for ES5.
 *
 * http://es5.github.io/#x7.6.1
 *
 * @type {KeywordsHash}
 */
KEYWORDS['5'] = _hash(
    // Keyword
    'break    do       instanceof typeof',
    'case     else     new        var',
    'catch    finally  return     void',
    'continue for      switch     while',
    'debugger function this       with',
    'default  if       throw',
    'delete   in       try',
    // FutureReservedWord
    'class enum extends super',
    'const export import',
    // NullLiteral & BooleanLiteral
    'null true false'
);

/**
 * Reserved Words for ES5 in strict mode.
 *
 * @type {KeywordsHash}
 */
KEYWORDS['5-strict'] = _hash(
    KEYWORDS['5'],
    // FutureReservedWord, strict mode. http://es5.github.io/#x7.6.1.2
    'implements let     private   public yield',
    'interface  package protected static'
);

/**
 * Reserved Words for ES6.
 *
 * 11.6.2
 * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-reserved-words
 *
 * @type {KeywordsHash}
 */
KEYWORDS['6'] = _hash(
    // Keywords, ES6 11.6.2.1, http://www.ecma-international.org/ecma-262/6.0/index.html#sec-keywords
    'break    do       in         typeof',
    'case     else     instanceof var',
    'catch    export   new        void',
    'class    extends  return     while',
    'const    finally  super      with',
    'continue for      switch     yield',
    'debugger function this',
    'default  if       throw',
    'delete   import   try',
    // Future Reserved Words, ES6 11.6.2.2
    // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-future-reserved-words
    'enum await',
    // NullLiteral & BooleanLiteral
    'null true false'
);

/**
 * Reserved Words for ES6 in strict mode.
 *
 * @type {KeywordsHash}
 */
KEYWORDS['6-strict'] = _hash(
    KEYWORDS['6'],
    // Keywords, ES6 11.6.2.1
    'let static',
    // Future Reserved Words, ES6 11.6.2.2
    'implements package protected',
    'interface private public'
);

/**
 * Generates hash from strings
 *
 * @private
 * @param {...String|KeywordsHash} keywords - Space-delimited string or previous result of _hash
 * @return {KeywordsHash} - Object with keywords in keys and true in values
 */
function _hash() {
    var set = Array.prototype.map.call(Array.from(arguments), function(v) {
        return typeof v === 'string' ? v : Object.keys(v).join(' ');
    }).join(' ');

    return set.split(/\s+/)
        .reduce(function(res, keyword) {
            res[keyword] = true;
            return res;
        }, {});
}

},{"assert":4}],12:[function(require,module,exports){
module.exports = function isBuffer(arg) {
  return arg && typeof arg === 'object'
    && typeof arg.copy === 'function'
    && typeof arg.fill === 'function'
    && typeof arg.readUInt8 === 'function';
}
},{}],13:[function(require,module,exports){
(function (process,global){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var formatRegExp = /%[sdj%]/g;
exports.format = function(f) {
  if (!isString(f)) {
    var objects = [];
    for (var i = 0; i < arguments.length; i++) {
      objects.push(inspect(arguments[i]));
    }
    return objects.join(' ');
  }

  var i = 1;
  var args = arguments;
  var len = args.length;
  var str = String(f).replace(formatRegExp, function(x) {
    if (x === '%%') return '%';
    if (i >= len) return x;
    switch (x) {
      case '%s': return String(args[i++]);
      case '%d': return Number(args[i++]);
      case '%j':
        try {
          return JSON.stringify(args[i++]);
        } catch (_) {
          return '[Circular]';
        }
      default:
        return x;
    }
  });
  for (var x = args[i]; i < len; x = args[++i]) {
    if (isNull(x) || !isObject(x)) {
      str += ' ' + x;
    } else {
      str += ' ' + inspect(x);
    }
  }
  return str;
};


// Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
exports.deprecate = function(fn, msg) {
  // Allow for deprecating things in the process of starting up.
  if (isUndefined(global.process)) {
    return function() {
      return exports.deprecate(fn, msg).apply(this, arguments);
    };
  }

  if (process.noDeprecation === true) {
    return fn;
  }

  var warned = false;
  function deprecated() {
    if (!warned) {
      if (process.throwDeprecation) {
        throw new Error(msg);
      } else if (process.traceDeprecation) {
        console.trace(msg);
      } else {
        console.error(msg);
      }
      warned = true;
    }
    return fn.apply(this, arguments);
  }

  return deprecated;
};


var debugs = {};
var debugEnviron;
exports.debuglog = function(set) {
  if (isUndefined(debugEnviron))
    debugEnviron = process.env.NODE_DEBUG || '';
  set = set.toUpperCase();
  if (!debugs[set]) {
    if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
      var pid = process.pid;
      debugs[set] = function() {
        var msg = exports.format.apply(exports, arguments);
        console.error('%s %d: %s', set, pid, msg);
      };
    } else {
      debugs[set] = function() {};
    }
  }
  return debugs[set];
};


/**
 * Echos the value of a value. Trys to print the value out
 * in the best way possible given the different types.
 *
 * @param {Object} obj The object to print out.
 * @param {Object} opts Optional options object that alters the output.
 */
/* legacy: obj, showHidden, depth, colors*/
function inspect(obj, opts) {
  // default options
  var ctx = {
    seen: [],
    stylize: stylizeNoColor
  };
  // legacy...
  if (arguments.length >= 3) ctx.depth = arguments[2];
  if (arguments.length >= 4) ctx.colors = arguments[3];
  if (isBoolean(opts)) {
    // legacy...
    ctx.showHidden = opts;
  } else if (opts) {
    // got an "options" object
    exports._extend(ctx, opts);
  }
  // set default options
  if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
  if (isUndefined(ctx.depth)) ctx.depth = 2;
  if (isUndefined(ctx.colors)) ctx.colors = false;
  if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
  if (ctx.colors) ctx.stylize = stylizeWithColor;
  return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;


// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
  'bold' : [1, 22],
  'italic' : [3, 23],
  'underline' : [4, 24],
  'inverse' : [7, 27],
  'white' : [37, 39],
  'grey' : [90, 39],
  'black' : [30, 39],
  'blue' : [34, 39],
  'cyan' : [36, 39],
  'green' : [32, 39],
  'magenta' : [35, 39],
  'red' : [31, 39],
  'yellow' : [33, 39]
};

// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
  'special': 'cyan',
  'number': 'yellow',
  'boolean': 'yellow',
  'undefined': 'grey',
  'null': 'bold',
  'string': 'green',
  'date': 'magenta',
  // "name": intentionally not styling
  'regexp': 'red'
};


function stylizeWithColor(str, styleType) {
  var style = inspect.styles[styleType];

  if (style) {
    return '\u001b[' + inspect.colors[style][0] + 'm' + str +
           '\u001b[' + inspect.colors[style][1] + 'm';
  } else {
    return str;
  }
}


function stylizeNoColor(str, styleType) {
  return str;
}


function arrayToHash(array) {
  var hash = {};

  array.forEach(function(val, idx) {
    hash[val] = true;
  });

  return hash;
}


function formatValue(ctx, value, recurseTimes) {
  // Provide a hook for user-specified inspect functions.
  // Check that value is an object with an inspect function on it
  if (ctx.customInspect &&
      value &&
      isFunction(value.inspect) &&
      // Filter out the util module, it's inspect function is special
      value.inspect !== exports.inspect &&
      // Also filter out any prototype objects using the circular check.
      !(value.constructor && value.constructor.prototype === value)) {
    var ret = value.inspect(recurseTimes, ctx);
    if (!isString(ret)) {
      ret = formatValue(ctx, ret, recurseTimes);
    }
    return ret;
  }

  // Primitive types cannot have properties
  var primitive = formatPrimitive(ctx, value);
  if (primitive) {
    return primitive;
  }

  // Look up the keys of the object.
  var keys = Object.keys(value);
  var visibleKeys = arrayToHash(keys);

  if (ctx.showHidden) {
    keys = Object.getOwnPropertyNames(value);
  }

  // IE doesn't make error fields non-enumerable
  // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
  if (isError(value)
      && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
    return formatError(value);
  }

  // Some type of object without properties can be shortcutted.
  if (keys.length === 0) {
    if (isFunction(value)) {
      var name = value.name ? ': ' + value.name : '';
      return ctx.stylize('[Function' + name + ']', 'special');
    }
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    }
    if (isDate(value)) {
      return ctx.stylize(Date.prototype.toString.call(value), 'date');
    }
    if (isError(value)) {
      return formatError(value);
    }
  }

  var base = '', array = false, braces = ['{', '}'];

  // Make Array say that they are Array
  if (isArray(value)) {
    array = true;
    braces = ['[', ']'];
  }

  // Make functions say that they are functions
  if (isFunction(value)) {
    var n = value.name ? ': ' + value.name : '';
    base = ' [Function' + n + ']';
  }

  // Make RegExps say that they are RegExps
  if (isRegExp(value)) {
    base = ' ' + RegExp.prototype.toString.call(value);
  }

  // Make dates with properties first say the date
  if (isDate(value)) {
    base = ' ' + Date.prototype.toUTCString.call(value);
  }

  // Make error with message first say the error
  if (isError(value)) {
    base = ' ' + formatError(value);
  }

  if (keys.length === 0 && (!array || value.length == 0)) {
    return braces[0] + base + braces[1];
  }

  if (recurseTimes < 0) {
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    } else {
      return ctx.stylize('[Object]', 'special');
    }
  }

  ctx.seen.push(value);

  var output;
  if (array) {
    output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
  } else {
    output = keys.map(function(key) {
      return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
    });
  }

  ctx.seen.pop();

  return reduceToSingleString(output, base, braces);
}


function formatPrimitive(ctx, value) {
  if (isUndefined(value))
    return ctx.stylize('undefined', 'undefined');
  if (isString(value)) {
    var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
                                             .replace(/'/g, "\\'")
                                             .replace(/\\"/g, '"') + '\'';
    return ctx.stylize(simple, 'string');
  }
  if (isNumber(value))
    return ctx.stylize('' + value, 'number');
  if (isBoolean(value))
    return ctx.stylize('' + value, 'boolean');
  // For some reason typeof null is "object", so special case here.
  if (isNull(value))
    return ctx.stylize('null', 'null');
}


function formatError(value) {
  return '[' + Error.prototype.toString.call(value) + ']';
}


function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  var output = [];
  for (var i = 0, l = value.length; i < l; ++i) {
    if (hasOwnProperty(value, String(i))) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          String(i), true));
    } else {
      output.push('');
    }
  }
  keys.forEach(function(key) {
    if (!key.match(/^\d+$/)) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          key, true));
    }
  });
  return output;
}


function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
  var name, str, desc;
  desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
  if (desc.get) {
    if (desc.set) {
      str = ctx.stylize('[Getter/Setter]', 'special');
    } else {
      str = ctx.stylize('[Getter]', 'special');
    }
  } else {
    if (desc.set) {
      str = ctx.stylize('[Setter]', 'special');
    }
  }
  if (!hasOwnProperty(visibleKeys, key)) {
    name = '[' + key + ']';
  }
  if (!str) {
    if (ctx.seen.indexOf(desc.value) < 0) {
      if (isNull(recurseTimes)) {
        str = formatValue(ctx, desc.value, null);
      } else {
        str = formatValue(ctx, desc.value, recurseTimes - 1);
      }
      if (str.indexOf('\n') > -1) {
        if (array) {
          str = str.split('\n').map(function(line) {
            return '  ' + line;
          }).join('\n').substr(2);
        } else {
          str = '\n' + str.split('\n').map(function(line) {
            return '   ' + line;
          }).join('\n');
        }
      }
    } else {
      str = ctx.stylize('[Circular]', 'special');
    }
  }
  if (isUndefined(name)) {
    if (array && key.match(/^\d+$/)) {
      return str;
    }
    name = JSON.stringify('' + key);
    if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
      name = name.substr(1, name.length - 2);
      name = ctx.stylize(name, 'name');
    } else {
      name = name.replace(/'/g, "\\'")
                 .replace(/\\"/g, '"')
                 .replace(/(^"|"$)/g, "'");
      name = ctx.stylize(name, 'string');
    }
  }

  return name + ': ' + str;
}


function reduceToSingleString(output, base, braces) {
  var numLinesEst = 0;
  var length = output.reduce(function(prev, cur) {
    numLinesEst++;
    if (cur.indexOf('\n') >= 0) numLinesEst++;
    return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
  }, 0);

  if (length > 60) {
    return braces[0] +
           (base === '' ? '' : base + '\n ') +
           ' ' +
           output.join(',\n  ') +
           ' ' +
           braces[1];
  }

  return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
}


// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
  return Array.isArray(ar);
}
exports.isArray = isArray;

function isBoolean(arg) {
  return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;

function isNull(arg) {
  return arg === null;
}
exports.isNull = isNull;

function isNullOrUndefined(arg) {
  return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;

function isNumber(arg) {
  return typeof arg === 'number';
}
exports.isNumber = isNumber;

function isString(arg) {
  return typeof arg === 'string';
}
exports.isString = isString;

function isSymbol(arg) {
  return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;

function isUndefined(arg) {
  return arg === void 0;
}
exports.isUndefined = isUndefined;

function isRegExp(re) {
  return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;

function isDate(d) {
  return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;

function isError(e) {
  return isObject(e) &&
      (objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;

function isFunction(arg) {
  return typeof arg === 'function';
}
exports.isFunction = isFunction;

function isPrimitive(arg) {
  return arg === null ||
         typeof arg === 'boolean' ||
         typeof arg === 'number' ||
         typeof arg === 'string' ||
         typeof arg === 'symbol' ||  // ES6 symbol
         typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;

exports.isBuffer = require('./support/isBuffer');

function objectToString(o) {
  return Object.prototype.toString.call(o);
}


function pad(n) {
  return n < 10 ? '0' + n.toString(10) : n.toString(10);
}


var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
              'Oct', 'Nov', 'Dec'];

// 26 Feb 16:19:34
function timestamp() {
  var d = new Date();
  var time = [pad(d.getHours()),
              pad(d.getMinutes()),
              pad(d.getSeconds())].join(':');
  return [d.getDate(), months[d.getMonth()], time].join(' ');
}


// log is just a thin wrapper to console.log that prepends a timestamp
exports.log = function() {
  console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
};


/**
 * Inherit the prototype methods from one constructor into another.
 *
 * The Function.prototype.inherits from lang.js rewritten as a standalone
 * function (not on Function.prototype). NOTE: If this file is to be loaded
 * during bootstrapping this function needs to be rewritten using some native
 * functions as prototype setup using normal JavaScript does not work as
 * expected during bootstrapping (see mirror.js in r114903).
 *
 * @param {function} ctor Constructor function which needs to inherit the
 *     prototype.
 * @param {function} superCtor Constructor function to inherit prototype from.
 */
exports.inherits = require('inherits');

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || !isObject(add)) return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};

function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":12,"_process":9,"inherits":5}]},{},[2])(2)
});/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloParamsJS
 * Classe wrapper dei parametri di configurazione di Tolomeo
 *
 * @constructor
 * 
 * 
 * @param {Object} config
 * 
 * 
 */
function ToloParamsJS(config){
	if(config && typeof config == 'object'){
        for(var p in config){
            this[p] = config[p];
        }
        this.createMapDefinition();
    }
	
}

ToloParamsJS.enhanceEffects = [{ getName: function(){ return ToloI18n.getMsg("ToloParamsJS.effectbrightness");}, key: 'effectbrightness', cssProp: 'brightness', cssUm:'', defaultValue: 1, minValue: 0, maxValue: 3, scale: 100},
                      { getName: function(){ return ToloI18n.getMsg("ToloParamsJS.effectcontrast");}, key: 'effectcontrast', cssProp: 'contrast', cssUm:'', defaultValue: 1, minValue: 0, maxValue: 3, scale: 100},
                      { getName: function(){ return ToloI18n.getMsg("ToloParamsJS.effectsaturate");}, key: 'effectsaturate', cssProp: 'saturate', cssUm:'', defaultValue: 1, minValue: 0, maxValue: 3, scale: 100},
                      { getName: function(){ return ToloI18n.getMsg("ToloParamsJS.effectblur");}, key: 'effectblur', cssProp: 'blur', cssUm:'px', defaultValue: 0, minValue: 0, maxValue: 10, scale: 1},
                      { getName: function(){ return ToloI18n.getMsg("ToloParamsJS.effectsepia");}, key: 'effectsepia', cssProp: 'sepia', cssUm:'', defaultValue: 0, minValue: 0, maxValue: 1, scale: 100},
                      { getName: function(){ return ToloI18n.getMsg("ToloParamsJS.effectinvert");}, key: 'effectinvert', cssProp: 'invert', cssUm:'', defaultValue: 0, minValue: 0, maxValue: 1, scale: 100},
                      { getName: function(){ return ToloI18n.getMsg("ToloParamsJS.effectgrayscale");}, key: 'effectgrayscale', cssProp: 'grayscale', cssUm:'', defaultValue: 0, minValue: 0, maxValue: 1, scale: 100}];


/**
 * @method getSelectableCodTPN
 * Restituisce la lista dei codiciTPN dei layer che risultano selezionabili.
 * 
 * @param {Boolean} forceRefresh
 * 
 * 
 * @return {Array}
 * Lista dei codiciTPN dei layer che risultano selezionabili
 * 
 */
ToloParamsJS.prototype.getSelectableCodTPN = function (forceRefresh) {
	if(forceRefresh || !this.selectableCodTPN){		
		this.selectableCodTPN = [];
		if (!!this.azioniApertura.modoEditingSingolo)  {
			this.selectableCodTPN.push(this.azioniApertura.modoEditingSingolo.layerCODTPN);	
		} else {
			for(var index=0; index<this.azioniEventi.eventiLayerList.length; index++) {
				var evl = this.azioniEventi.eventiLayerList[index];
				if (evl.interactable) {
					this.selectableCodTPN.push(evl.codTPN);
				}
			}
		}
	}
	return this.selectableCodTPN;
}

/**
 * @method isSelectable
 * Restituisce true sel il codTPN passato è quello di uno dei layer
 * 
 * @param {Number} codTPN
 * 
 * 
 * @return {Boolean}
 * 
 * 
 */
ToloParamsJS.prototype.isSelectable = function (codTPN) {	
	var selectableCodTPN = this.getSelectableCodTPN();
	for(var i=0; i < selectableCodTPN.length; i++){
		if(selectableCodTPN[i]==codTPN) return true;
	}
	return false;
}

/**
 * @method getParamJSLayer
 * 
 * 
 * @param {Number} codTPN
 * 
 * 
 * @return {Object}
 * I parametri JS del layer.
 * 
 */
ToloParamsJS.prototype.getParamJSLayer = function (codTPN) {
	var evl = null;	
	for(var index=0; index<this.azioniEventi.eventiLayerList.length; index++) {
		evl = this.azioniEventi.eventiLayerList[index];
		if (evl.codTPN==codTPN) {
			return evl;
		}
	}
	return null;
}

/**
 * @method getLayerSfondoString
 * Ritorna una stringa contenente l'elenco dei nomi dei layer di sfondo separati da saparator
 * 
 * @param {Object} mappaCurr
 * oggetto mappa corrente
 * 
 * @param {String} separator
 * separatore
 * 
 * @return {String}
 * Ritorna una stringa contenente l'elenco dei nomi dei layer di sfondo separati da separator oppure null nel caso non ci sia nessun layer 
 * 
 */
ToloParamsJS.prototype.getLayerSfondoString = function(mappaCurr, separator) {
	var laystr = null;
	
	// Inserimento livelli predefiniti di sfondo
	if(mappaCurr.sfondo != null) {
		for (var j = 0; j < mappaCurr.sfondo.layerSfondoList.length; j++) {	
			var layerSfondo = mappaCurr.sfondo.layerSfondoList[j];
			if (laystr==null) laystr = '';
			laystr += (j!=0) ? separator : '';
			// Prendo il layer di sfondo corrente
			laystr += layerSfondo.name ;
		}
	}
	
	return laystr;
}

/**
 * @method getLayerSfondoStyleString
 * Ritorna una stringa contenente l'elenco degli stili dei layer di sfondo separati da saparator. Se uno stile non è definito comunque viene inserita una stringa vuota per indicare la necessità di utilizzare lo stile di default
 * Se non presenti layer di cui definire gli stili ritorna null
 * 
 * @param {Object} mappaCurr
 * oggetto mappa corrente
 * 
 * @param {String} separator
 * separatore
 *
 * @return {String}
 * Ritorna una stringa contenente l'elenco dei nomi dei layer di sfondo separati da saparator 
 * 
 */
ToloParamsJS.prototype.getLayerSfondoStyleString = function(mappaCurr, separator) {
	var laystr = null;
	
	// Inserimento livelli predefiniti di sfondo
	if(mappaCurr.sfondo != null) {
		for (var j = 0; j < mappaCurr.sfondo.layerSfondoList.length; j++) {	
			var layerSfondo = mappaCurr.sfondo.layerSfondoList[j];
			if (laystr==null) laystr = '';
			laystr += (j!=0) ? separator : '';
			// Prendo il layer di sfondo corrente
			laystr += (layerSfondo.stile ? layerSfondo.stile : '') ;
		}
	}
	
	return laystr;
}

/**
 * @method onEachLegendaCategory
 * Metodo per il lancio della funzione fn all'interno del contesto scope per ogni categoria  
 * Il metodo agisce in maniera ricorsiva se bRecurce non è definito o è true 
 * 
 * @param {Object} categ
 * 
 * 
 * @param {Function} fn
 * 
 * 
 * @param {Object} scope
 * 
 * 
 * @param {Boolean} bRecurse
 * 
 * 
 */
ToloParamsJS.prototype.onEachLegendaCategory= function(categ, fn, scope, bRecurse ) {
	var scopebuff = (scope) ? scope : this;
	var bRecurse = (bRecurse!=undefined && bRecurse !=null) ?  bRecurse : true;
	
	// Prima nelle categorie annidate perchè a meno di cambio ordine sono mostrate prima
	if (bRecurse && categ.categoryList) {
		for (var i=0; i<categ.categoryList.length; i++) {
			this.onEachLegendaCategory(categ.categoryList[i], fn, scope, bRecurse);
		}
	}
	
	// poi eseguo su questa categoria
	fn.call(scope, categ);
}

/**
 * @method getServer
 * 
 * 
 * @param {String} id
 * 
 * 
 * @param {Object} mappa
 * 
 * 
 * @return {Object}
 * 
 * 
 */
ToloParamsJS.prototype.getServer= function(id, mappa) {
	
	if (!id || id=="INLINESERVERID") {
	
		id = "INLINESERVERID";
		var server0 = new toloServerMappe();
		server0.nome 					= mappa.nome;
		server0.typeDescription       	= mappa.typeDescription;
		server0.typeCode             	= mappa.typeCode;
		server0.nomeCredenziale   		= mappa.nomeCredenziale;
		server0.allowServerConnection 	= mappa.allowServerConnection;
		server0.url          		  	= mappa.url;
		server0.serverOpts				= mappa.mapOptions;
		server0.tilesMultiple			= mappa.tilesMultiple;		
		server0.noTolomeoParams			= false;
		
		server0.tileStampaLarghezza		= (server0.tileStampaLarghezza ? inlineServerInfo.tileStampaLarghezza : 0);
		server0.tileStampaAltezza		= (server0.tileStampaAltezza ? inlineServerInfo.tileStampaAltezza : 0);									
		
		return server0;
	} else {
	
	
		if (this.serverPool!=undefined && this.serverPool!=null) {
			var listaServer = this.serverPool.serverList;
			
			for (var i=0; i<listaServer.length; i++ ) {
				if (listaServer[i].id==id) return new toloServerMappe(listaServer[i]);
			}
		}
	}
	return null;
	
}

/**
 * @method getLegendaCategory
 * Metodo privato per lo scorrimento ricorsivo delle categorie
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {String} idxs
 * 
 * 
 * @param {Object} baseCat
 * 
 * 
 * @return {Object}
 * 
 * 
 */
ToloParamsJS.prototype.getLegendaCategory=function(nMappa, idxs, baseCat) {
	
	var base = (baseCat==undefined || baseCat==null) ? this.mappe.mappaList[nMappa].legenda : baseCat;
	var idxArray;
	if (typeof idxs === "string") {
		idxArray=idxs.split("/");
	} else {
		// Copio per non modificare il valore passato
		idxArray=idxs.concat([]);
	}
	if (idxArray.length==1) return base.categoryList[idxArray[0]];
	else {
		var idx = idxArray.shift();
		return this.getLegendaCategory(nMappa, idxArray, base.categoryList[idx]);		
	}
}

/**
 * @method getLegendaParentCategoryIdx
 * Metodo per ottenere l'indice della categoria padre 
 * 
 * @param {String} catIdx
 * 
 * 
 * @return {Object}
 * 
 * 
 */
ToloParamsJS.prototype.getLegendaParentCategoryIdx=function(catIdx) {
	
	if (typeof catIdx === "string") {
		var idxs = catIdx.split("/");
		if (idxs.length<=1) return "";
		return idxs.slice(0,idxs.length-1);	
	} else {
		if (catIdx.length<=1) return "";
		return catIdx.slice(0,catIdx.length-1);
	}
	
}

/**
 * @method onEachLegendaParentCategory
 * 
 * 
 * @param {Function} fn
 * 
 * 
 * @param {Object} scope
 * 
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {String} startCatIdx
 * 
 * 
 */
ToloParamsJS.prototype.onEachLegendaParentCategory=function(fn, scope, nMappa, startCatIdx) {
	
	var parentIdx = this.getLegendaParentCategoryIdx(startCatIdx);
	while (parentIdx!="") {
		var cat = this.getLegendaCategory(nMappa, parentIdx);
		fn.call(scope, cat, parentIdx);
		parentIdx = this.getLegendaParentCategoryIdx(parentIdx);
	}
	
}

/**
 * @method areAllLegendaParentCatDefaultCategory
 * Metodo che verifica se tutte le categorie padre hanno il flag defaultCategory==true
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {String} catIdx
 * 
 * 
 * @return {Boolean}
 * 
 * 
 */
ToloParamsJS.prototype.areAllLegendaParentCatDefaultCategory=function(nMappa, catIdx) {
	var retObj1 = { bAllChecked: true };
	this.onEachLegendaParentCategory(
			function(parentCat, parentCatIdx) {
				if (!parentCat.defaultCategory) this.bAllChecked = false;
			}, 
			retObj1,
			nMappa,
			catIdx
	);
	return retObj1.bAllChecked;
}

/**
 * @method onEachCategoryOrLayerOrder
 * Scorre tutto il tag legenda della mappa nMappa a partire da:
 * - categ se tocInfo non è definito o nullo o ha layerOrder.length=0
 * - se tocInfo è definito e non nullo tutto tocInfo.layerOrder ignorando categ
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {Object} categ
 * 
 * 
 * @param {Function} fn
 * 
 * 
 * @param {Object} scope
 * 
 * 
 * @param {Boolean} bRecurse
 * 
 * 
 * @param {Object} tocInfo
 * 
 * 
 * @param {Array} filterItemType
 * 
 * 
 */
ToloParamsJS.prototype.onEachCategoryOrLayerOrder=function(nMappa, categ, fn, scope, bRecurse, tocInfo, filterItemType ) {
	
	var withFilter = (filterItemType &&  filterItemType.length>0); 
	
	if (tocInfo!=undefined && tocInfo!=null && tocInfo.layerOrder && tocInfo.layerOrder.length>0) {
		for (var i = 0; i < tocInfo.layerOrder.length; i++) {
			var laypos = tocInfo.layerOrder[i];
			var catInfo = tocInfo.getCategoryInfo(laypos.cat);
			var layInfo = catInfo.layers[laypos.lay];
			var cat = this.getLegendaCategory(nMappa, layInfo.catTreeIdx);
			var lay = cat.layerList[layInfo.layTreeIdx];
			if (!withFilter || Ext.Array.contains(filterItemType, lay.itemType)) {
				fn.call(scope, cat, lay, catInfo, layInfo);
			}
		}
	} else {
		this.onEachLegendaCategory(categ, 
				function(categ) {
					if (categ.layerList!=undefined) {
						var catInfo = null;
						if (tocInfo) {
							catInfo = tocInfo.getCategoryInfo(categ.catTreeIdx);
						}
						
						for (var i=0; i<categ.layerList.length; i++) {
							var layInfo = null;
							var layer = categ.layerList[i];
							if (catInfo) layInfo = catInfo.layers[layer.layTreeIdx];
							if (!withFilter || Ext.Array.contains(filterItemType, layer.itemType)) {
								fn.call(scope, categ, layer, catInfo, layInfo);
							}
							
						}
					}
				},
				scope, bRecurse);	
	}
	
}

/**
 * @method createMapDefinition
 * 
 * 
 * @param {Object} tocInfo
 * 
 * 
 */
ToloParamsJS.prototype.createMapDefinition = function(tocInfo) {

	var aggregationAttributes = ['serverID','opacity'];
	
	// Aggiunge tutti i filtri all'elenco degli attributi che provocano la rottura
	for (var i=0; i<ToloParamsJS.enhanceEffects.length; i++ ){
		aggregationAttributes.push(ToloParamsJS.enhanceEffects[i].key);
	}
	
	var prevAttributeValue = {};
	var prevAttributeValueInTocInfo = {};
	this.mapDefinitions = [];	
	
	for (var nMappa=0; nMappa<this.mappe.mappaList.length; nMappa++) {
		
		var mappa = this.mappe.mappaList[nMappa];
		var mapDefinition = new TolomeoExt.ToloMapDefinition();
	
		/*
		var server0 = new toloServerMappe();
		server0.nome 					= mappa.nome;
		server0.typeDescription       	= mappa.typeDescription;
		server0.typeCode             	= mappa.typeCode;
		server0.nomeCredenziale   		= mappa.nomeCredenziale;
		server0.allowServerConnection 	= mappa.allowServerConnection;
		server0.url          		  	= mappa.url;
		server0.serverOpts				= mappa.mapOptions;
		server0.tilesMultiple			= mappa.tilesMultiple;		
		server0.noTolomeoParams			= false;
		*/
		
		//inlineServerInfo = this.getServer("INLINESERVERID");
		server0 = this.getServer("INLINESERVERID", mappa);
		
		/*
		if (inlineServerInfo) {
			server0.tileStampaLarghezza		= inlineServerInfo.tileStampaLarghezza;
			server0.tileStampaAltezza		= inlineServerInfo.tileStampaAltezza;									
		
		} else {
			server0.tileStampaLarghezza		= 0;
			server0.tileStampaAltezza		= 0;			
		}*/								
		
		var idServerPrev = "";
		var layViewAggCurr = null;
		var bPrecCompleto = false;
	
		this.onEachCategoryOrLayerOrder(nMappa, mappa.legenda, 
		
				function(categ, layer, catInfo, layInfo) {
					
							var breakingTime = false;
							// Verifico il breaking time ovvero se è il momento di passare ad una nuova aggregatio perchè è 
							// cambiato un qualche attributo che la identifica.
							for(var i in aggregationAttributes){
								var attrName = aggregationAttributes[i];
								if(layer[attrName] != prevAttributeValue[attrName] ){									
									breakingTime = true;
								}
								prevAttributeValue[attrName] = layer[attrName]; 
							}
							
							// Rottura anche se il gruppo precedente è stato stabilito di essere "completo"
							if (bPrecCompleto) {
								breakingTime = true;
								bPrecCompleto = false;
							}
							
							//ALE DA RIPETERE IN SFONDO?
							// Se definita TOCInfo
							if (tocInfo) {
								//Rottura anche quando opacity settata da utente su legenda != 1
								if (layInfo['opacity']!=undefined && layInfo['opacity']!=null && layInfo['opacity']!=1) {
									breakingTime = true;
									bPrecCompleto = true;
								} else {
									// Rottura anche se valori diversi da default per qualcuno dei filtri
									for (var i=0; i<ToloParamsJS.enhanceEffects.length; i++ ){
										var effect = ToloParamsJS.enhanceEffects[i];
										if (layInfo[effect.key]!=undefined && layInfo[effect.key]!=null && layInfo[effect.key]!=effect.defaultValue) {
											breakingTime = true;
											bPrecCompleto = true;
											break;
										}
									}
								} 
							}
							
							if (layViewAggCurr==null || breakingTime) {
								
									if (layViewAggCurr!=null) mapDefinition.addLayerAggregation(layViewAggCurr);

									var defaultServer = !layer.serverID;
									var serverCurr = defaultServer ? server0 : this.getServer(layer.serverID);		
									
									var aggregationOption = {
										'tilesMultiple': serverCurr.tilesMultiple,
										'overlay': true, //mappa.overlay;
										'SRID': mappa.SRID,
										'units': mappa.units,
										'mostraInLegenda': mappa.mostraInLegenda,
										'layerOptions': mappa.viewerOptions,
										'imagetype': mappa.imagetype,
										'server': serverCurr
									};
									
									// Aggiungo alle proprietà di aggregazione quelle eventualmente definite sul layer o sul server, dando precedenza a quelle del layer 
									// Sulla Layer Aggregation mi trovero gli attributi ch ne hanno dato origine
									for(var i in aggregationAttributes){
										var attrName = aggregationAttributes[i];
										aggregationOption[attrName] = layer[attrName] || (defaultServer ? mappa[attrName] : serverCurr[attrName]);
									}	
									if (tocInfo && layInfo.opacity!=undefined && layInfo.opacity!=null) aggregationOption.opacity = layInfo.opacity; 
									
									// Aggiungo anche i valori degli effetti presi da preset (se presenti)
									for (var i=0; i<ToloParamsJS.enhanceEffects.length; i++ ){
										var effect = ToloParamsJS.enhanceEffects[i];
										
										// Se valori di tocInfo disponibili
										if (tocInfo && layInfo[effect.key]!=undefined && layInfo[effect.key]!=null) {
											aggregationOption[effect.key] = layInfo[effect.key];
										} else {
											// ..altrimenti se disponibili su preset
											if (layer[effect.key]!=undefined && layer[effect.key]!=null){
												aggregationOption[effect.key] = layer[effect.key];
											} else {
												// se non presenti nemmeno su preset utilizzo default
												aggregationOption[effect.key] = effect.defaultValue;
											}
										}
									}
									
									
									// TODO verificare attributi e se ha senso che siano qua
									// TODO forzare trasparenza o no?
									layViewAggCurr = Ext.create('TolomeoExt.ToloLayerViewerAggregation',aggregationOption);
								}
								if (layInfo) {
									var bAttivo = tocInfo.areAllParentCatChecked(categ.catTreeIdx) && catInfo.checked && layInfo.checked;
									layer.checked = bAttivo;
								} else {
									var bAttivo = this.areAllLegendaParentCatDefaultCategory(nMappa, categ.catTreeIdx) && categ.defaultCategory && layer.defaultLayer;
									layer.checked = bAttivo;
									layer.style   = layer.defaultStyle; 
								}
								layViewAggCurr.layers.push(layer);
				}, 
				this, true, tocInfo, ['layer'] );
		
		if (layViewAggCurr!=null) mapDefinition.addLayerAggregation(layViewAggCurr); // layViews.push(layViewAggCurr);
		
		// Aggiunta sfondo
		if (mappa.sfondo) {
			idServerPrev = "";
			layViewAggCurr = null;
			
			prevAttributeValue = {};					
			
			for (var nSfondo=0; nSfondo<mappa.sfondo.layerSfondoList.length; nSfondo++) {
				
				var layer = mappa.sfondo.layerSfondoList[nSfondo];
				
				var breakingTime = false;
			
				for(var i in aggregationAttributes){
					var attrName = aggregationAttributes[i];
					if(layer[attrName] != prevAttributeValue[attrName] ){									
						breakingTime = true;
					}
					prevAttributeValue[attrName] = layer[attrName]; 
				}
				
				if (bPrecCompleto) {
					breakingTime = true;
					bPrecCompleto = false;
				}
				
				if (layViewAggCurr==null || breakingTime) {
					
					if (layViewAggCurr!=null) mapDefinition.addLayerAggregation(layViewAggCurr);
					
					var serverCurr = layer.serverID ? this.getServer(layer.serverID) : server0;		
					
					var aggregationOption = {
						'tilesMultiple': serverCurr.tilesMultiple,
						'overlay': true, //mappa.overlay;
						'SRID': mappa.SRID,
						'units': mappa.units,
						'mostraInLegenda': mappa.mostraInLegenda,
						'layerOptions': mappa.viewerOptions,
						'imagetype': mappa.imagetype,
						'server': serverCurr//,
					};
					
					// Aggiungo alle proprietà di aggregazione quelle eventualmente definite sul layer o sul server, dando precedenza a quelle del layer 
					// Sulla Layer Aggregation mi trovero gli attributi ch ne hanno dato origine
					for(var i in aggregationAttributes){
						var attrName = aggregationAttributes[i];
						aggregationOption[attrName] = layer[attrName] || serverCurr[attrName];
					}										
									
					// TODO verificare attributi e se ha senso che siano qua
					// TODO forzare trasparenza o no?
					layViewAggCurr = Ext.create( 'TolomeoExt.ToloLayerViewerAggregation', aggregationOption);
				}

				layer.checked = true;
				layViewAggCurr.layers.push(layer);
			}
			if (layViewAggCurr!=null) mapDefinition.addLayerAggregation(layViewAggCurr);
		}
		
		mapDefinition.reverseLayerAggregationsOrder();
		// metto il primo come baselayer
		//mapDefinition.getLayerAggregation(0).overlay=false;
		this.mapDefinitions.push(mapDefinition);

	}
	
},


ToloParamsJS.prototype.getLegendaLayerInfoByCodTPN = function(codTPN, nMappa, tocInfo){
	var retVal = {
			presetLayerInfo: null,
			tocInfoLayerInfo: null
	};
	var mappa = this.mappe.mappaList[nMappa];
	
	this.onEachCategoryOrLayerOrder(nMappa, mappa.legenda, 
			
			function(categ, layer, catInfo, layInfo) {
				if (layer.codTPN==codTPN){
					retVal.presetLayerInfo = layer;
					if (tocInfo){
						retVal.tocInfoLayerInfo = layInfo;	
					}	
				}
				
			}, 
			this, true, tocInfo, ['layer'] );
	
	return retVal ;
	
},

/**
 * @method updateMapDefinitionLayerCheckState
 * 
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {Object} layerInfo
 * 
 * 
 * @param {Boolean} forcedState
 * 
 * 
 * @return {Object}
 * 
 * 
 */
ToloParamsJS.prototype.updateMapDefinitionLayerCheckState = function(nMappa, layerInfo, forcedState) {
	
	var layerAggreg = this.mapDefinitions[nMappa].whichLayerAggregationContains(layerInfo.catTreeIdx, layerInfo.layTreeIdx);
	
	if (layerAggreg) {
		for (var i=0; i<layerAggreg.layers.length; i++) {
			var layer = layerAggreg.layers[i];
			if ((layer.catTreeIdx==layerInfo.catTreeIdx) && (layer.layTreeIdx==layerInfo.layTreeIdx)) {
				layer.checked = (forcedState!=undefined && forcedState!=null) ? forcedState : layerInfo.checked;
			}	
		}
	}
	return layerAggreg;
}

/**
 * @method updateMapDefinitionLayerStyle
 * 
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {Object} layerInfo
 * 
 * 
 * @param {Object} style
 * 
 * 
 */
ToloParamsJS.prototype.updateMapDefinitionLayerStyle= function(nMappa, layerInfo, style) {
	
	var layerAggreg = this.mapDefinitions[nMappa].whichLayerAggregationContains(layerInfo.catTreeIdx, layerInfo.layTreeIdx);
	
	if (layerAggreg) {
		for (var i=0; i<layerAggreg.layers.length; i++) {
			var layer = layerAggreg.layers[i];
			if ((layer.catTreeIdx==layerInfo.catTreeIdx) && (layer.layTreeIdx==layerInfo.layTreeIdx)) {
				layer.style = style;
			}	
		}
	}
	return layerAggreg;
}

/**
 * @method updateMapDefinitionLayerAttribution Aggiorna l'attribuzione di un layer  
 * 
 * @param {Number} nMappa Numero della mappa, attualmente gestita solo mappa 0
 * @param {Object} layerInfo Informazioni del layer, contenenti la nuova attribuzione
 */
ToloParamsJS.prototype.updateMapDefinitionLayerAttribution= function(nMappa, layerInfo) {
	
	var layerAggreg = this.mapDefinitions[nMappa].whichLayerAggregationContains(layerInfo.catTreeIdx, layerInfo.layTreeIdx);
	
	if (layerAggreg) {
		for (var i=0; i<layerAggreg.layers.length; i++) {
			var layer = layerAggreg.layers[i];
			if ((layer.catTreeIdx==layerInfo.catTreeIdx) && (layer.layTreeIdx==layerInfo.layTreeIdx)) {
				layer.attribution = layerInfo.attribution;
			}	
		}
	}
	return layerAggreg;
}

/**
 * @method updateMapDefinitionAttributions Aggiorna le attribuzioni.  
 * 
 * @param {Number} nMappa Numero della mappa, attualmente gestita solo mappa 0
 * @param {Object} tocInfo Informazioni legenda, contenenti le nuove attribuzione
 */
ToloParamsJS.prototype.updateMapDefinitionAttributions= function(nMappa, tocInfo) {
	
	for (var i=0; i<this.mapDefinitions[nMappa].getLayerAggregationCount(); i++) {
		var layerAggreg = this.mapDefinitions[nMappa].getLayerAggregation(i);
		for (var j=0; j < layerAggreg.layers.length; j++) {
			var layer = layerAggreg.layers[j];
			// Controllo se definita posizione in legenda per evitare errori con in layer di sfondo
			if (layer.catTreeIdx!=undefined && layer.layTreeIdx ) {
				var layerInfo = tocInfo.getCategoryInfo(layer.catTreeIdx).layers[layer.layTreeIdx];
				layer.attribution = layerInfo.attribution;	
			}
		}
	}
	
}

/**
 * @method updateMapDefinitionCategoryCheckState
 * 
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {Object} tocInfo
 * 
 * 
 * @param {Object} catInfo
 * 
 * 
 */
ToloParamsJS.prototype.updateMapDefinitionCategoryCheckState= function(nMappa, tocInfo, catInfo) {
	var retVal = [];
	
	if (tocInfo.layerOrder && tocInfo.layerOrder.length>0) {
		for (var i = 0; i < tocInfo.layerOrder.length; i++) {
			var laypos = tocInfo.layerOrder[i];
			var cat = tocInfo.getCategoryInfo(laypos.cat);
			var lay = cat.layers[laypos.lay];
			if (lay.itemType=='layer') {
				var bAttivo = tocInfo.areAllParentCatChecked(laypos.cat) && 
							cat.checked && lay.checked;
			
				var layAggr = this.updateMapDefinitionLayerCheckState(nMappa, lay, bAttivo);
				if (retVal.indexOf(layAggr)==-1) retVal.push(layAggr);
			}
			
		}
	} else {
		tocInfo.onEachLayer(
				function (cat,lay,catIdx,layIdx) {
					if (lay.itemType=='layer') {
						var bAttivo = tocInfo.areAllParentCatChecked(catIdx) && cat.checked && lay.checked;
						var layAggr = this.updateMapDefinitionLayerCheckState(nMappa, lay, bAttivo);
						if (retVal.indexOf(layAggr)==-1) retVal.push(layAggr);
					}
				},
				this,
				catInfo.catTreeIdx,
				true
			);
	}
	return retVal;
}

/**
 * @method getLayerAggregLayersAndStylesStrings
 * 
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {Number} layerAggregIndex
 * 
 * 
 * @param {String} sep
 * 
 * 
 * @param {Number} actualZoom
 * 
 * 
 */
ToloParamsJS.prototype.getLayerAggregLayersAndStylesStrings=function(nMappa, layerAggregIndex, sep, actualZoom) {
	
	var layers = "";
	var stili = "";
	var attribution = [];
	var layerAggreg = this.mapDefinitions[nMappa].getLayerAggregation(layerAggregIndex);
	var count = layerAggreg.layers.length-1;
	for (var j=0; j<layerAggreg.layers.length; j++) {
		var i = count-j;
		if ((layerAggreg.layers[i].checked)) {
			
			// Verifica se il layer è presente a questo livello di zoom
			var visActual = (actualZoom==undefined) || (actualZoom==null) || this.checkZoomVisibility(layerAggreg.layers[i], actualZoom);
			
			if (visActual) {
				stili  += ((layers!="") ? sep : "" ) + ((layerAggreg.layers[i].style!=undefined && layerAggreg.layers[i].style!=null) ? layerAggreg.layers[i].style : "");
				if (layerAggreg.layers[i].attribution) {
					// Verifica se c'e' già
					var bFlag = true;
					for (var k = 0; k<attribution.length; k++) {
						if (attribution[k]==layerAggreg.layers[i].attribution) {
							bFlag = false;
							break;
						}
					}
					if (bFlag) attribution.push(layerAggreg.layers[i].attribution);
				}
				layers += ((layers!="") ? sep : "" ) + layerAggreg.layers[i].name;
			}
		}
	}
	
	return { layers: layers, stili: stili, attribution: attribution};
}, 

/**
 * @method checkZoomVisibility
 * 
 * 
 * @param {Object} layer
 * 
 * 
 * @param {Number} zoom
 * 
 * 
 * @return {Boolean}
 */
ToloParamsJS.prototype.checkZoomVisibility=function(layer, zoom) {
	var scalaMinima  = layer.scalaMinima;
	var scalaMassima = layer.scalaMassima;
	
	if (((scalaMinima ==undefined)||(scalaMinima ==null)||(scalaMinima ==-1)||(scalaMinima <= zoom)) &&
	    ((scalaMassima==undefined)||(scalaMassima==null)||(scalaMassima==-1)||(zoom <= scalaMassima))) {
		visible = true;
	} else {
		visible = false;
	}
	
	return visible; 
	
}

/**
 * @method addCategory
 * Aggiunge una categoria nella posizione indicata dai parametri.
 * 
 * @param {Number} nMappa
 * numero di mappa all'interno del preset
 * 
 * @param {Object} catInfo
 * Categoria da aggiungere
 * 
 * @param {String} addPointCatIdx
 * categoria prima o dopo della quale va aggiunta la roba
 * 
 * @param {Boolean} bBefore
 * indica se aggiungere prima o dopo
 * 
 */
ToloParamsJS.prototype.addCategory=function(nMappa, catInfo, addPointCatIdx, bBefore) {
		
	// Cerca addPoint
	var addPoint = this.getLegendaCategory(nMappa, addPointCatIdx);
	
	// Cerca il parent
	//TODO
	var addPointParentIdx = this.getLegendaParentCategoryIdx(addPointCatIdx);
	var addPointParent = null;
	
	// Se parent == null sono sul primo livello
	if (addPointParentIdx == "") {
		addPointParent = this.mappe.mappaList[nMappa].legenda;
	} else {
		addPointParent = this.getLegendaCategory(nMappa, addPointParentIdx);
	}
	
	var c = (addPointParent.catTreeIdx) ? addPointParent.catTreeIdx : ""; 
	
	// Calcolo la posizione di inserimento
	var idxs = addPointCatIdx.split("/");
	var pos  = parseInt(idxs[idxs.length-1]) + ((bBefore) ? 0 : 1);
	
	// Aggiorno indice cat
	catInfo.catTreeIdx = c + ((c!="") ?  "/" : "") + pos; 
	
	//Inserisco nella giusta posizione
	if (pos > 0) {
		addPointParent.categoryList.splice(pos, 0, catInfo);
	} else {
		addPointParent.categoryList.unshift(catInfo);
	}
	
	// rinumero indici
	this.updateIdxs(addPointParent, c);
	
}
	
/**
 * @method updateIdxs
 * Aggiorna tutti gli indici catTreeIdx e layTreeIdx.
 * 
 * @param {Object} cat
 * categoria dalla quale iniziare ad aggiornare gli indici
 * 
 * @param {String} newCatTreeIdx
 * nuovo valore di treeIdx
 * 
 */
ToloParamsJS.prototype.updateIdxs=function(cat, newCatTreeIdx) {
		
	if (newCatTreeIdx!="") {
		// Aggiorna questa categoria
		var oldCatTreeIdx = cat.catTreeIdx;
		cat.catTreeIdx = newCatTreeIdx;
		//this.fireEvent("catTreeIdxUpdate", cat.catId, oldCatTreeIdx, newCatTreeIdx);
	
		// Aggiorna tutti i layer
		var layArray = cat.layerList;
		if (layArray) {
			for (var i=0; i<layArray.length; i++) {
				layArray[i].catTreeIdx = newCatTreeIdx;
				// Utilizza i il nuovo catTreeIdx perchè ha già rinumerato la categoria con l'evento precedente
				//this.fireEvent("catLayIdxUpdate", layArray[i].layId, newCatTreeIdx, layArray[i].layTreeIdx, layArray[i].layTreeIdx);
			}
		}
	} 
	
	// Cicla sulle categorie figlie
	var catArray = cat.categoryList;
	if (catArray) {
		for (var i=0; i<catArray.length; i++) {
			var newPrefix = newCatTreeIdx + ((newCatTreeIdx != "") ? "/" : "") + i ;				
			this.updateIdxs(catArray[i],newPrefix);		
		}
	}
	
}
	
/**
 * @method addLayer
 * Aggiunge un layer nella posizione indicata dai parametri.
 * 
 * @param {Number} nMappa
 * numero di mappa all'interno del preset
 * 
 * @param {Object} layInfo
 * Layer da aggiungere
 * 
 * @param {String} addPointCatIdx
 * Indice della categoria nella quale viene aggiunto il layer
 * 
 * @param {String} addPointLayIdx
 * Indice layer prima o dopo del quale aggiungere il nuovo layer
 * 
 * @param {Boolean} bBefore
 * indica se aggiungere prima o dopo
 * 
 */
ToloParamsJS.prototype.addLayer=function(nMappa, layInfo, addPointCatIdx, addPointLayIdx, bBefore) {
	
	// Cerca addPoint
	var addPoint = this.getLegendaCategory(nMappa, addPointCatIdx);
	
	// Calcolo la posizione di inserimento
	if (addPointLayIdx) {
		pos = parseInt(addPointLayIdx) + ((bBefore) ? 0 : 1);
	} else { // se non è definito addPointLayIdx inserire come primo elemento della categoria
		pos = addPoint.layerList.length-1;
	}
	
	// Aggiorno indice cat
	layInfo.catTreeIdx = addPointCatIdx; 
	layInfo.layTreeIdx = "" + pos;
	
	//Inserisco nella giusta posizione
	if (pos > 0) {
		addPoint.layerList.splice(pos, 0, layInfo);
	} else {
		addPoint.layerList.unshift(layInfo);
	}
	
	// rinumero indici
	for (var i=0; i<addPoint.layerList.length; i++) {
		var l = addPoint.layerList[i];
		var oldLayTreeIdx = l.layTreeIdx
		l.layTreeIdx = i;
		//this.fireEvent("catLayIdxUpdate", l.layId, addPointCatIdx, oldLayTreeIdx, l.layTreeIdx);
	}
	
	if (layInfo.queryable) {
	
		// Aggiunta azione vis per identify
		var eventiLayer = {
			interactable: true,
			codTPN: layInfo.codTPN,
			copertura: false,
			descrizioneLayer: layInfo.descrizione, 
			interactable: true,
			nomeLayer: layInfo.descrizione,
			tipoGeometria: 3,   // ????
			azioniEventiVis: {
				autoVisOnSelect: true,
				azioneList: [{
						ajaxCall: false,
						useWMSGetFeatureInfo: true,
						target: 'pannello'
					}
				]
			},
			azioniEventiCanc: {
				azioneList: []
				},
			azioniEventiIns: {
				azioneList: []
				},
			azioniEventiUpdateGeom: {
				azioneList: []
			},
			azioniEventiUpdateAlpha: {
				azioneList: []
			}
			
		};
		
		this.azioniEventi.eventiLayerList.push(eventiLayer);
		// forza l'aggiornamento della tabella dei selezionabili
		this.getSelectableCodTPN(true);
		
	}
	
		
}

/**
 * @method addServer
 * Aggiunge un server alla lista di quelli disponibili
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {Object} serverInfo
 * 
 * 
 * @return {Object}
 * 
 * 
 */
ToloParamsJS.prototype.addServer=function(nMappa, serverInfo) {
	
	// Controlla se esiste serverPool
	if (this.serverPool!=undefined && this.serverPool!=null) {
		// Scorre la lista dei server gia' definiti per vedere se ce ne è già uno adatto
		var listaServer = this.serverPool.serverList;
		
		for (var i=0; i<listaServer.length; i++ ) {
			if (listaServer[i].url==serverInfo.url &&
				listaServer[i].typeDescription==serverInfo.typeDescription &&
				listaServer[i].typeCode==serverInfo.typeCode &&
				listaServer[i].allowServerConnection==serverInfo.allowServerConnection &&
				listaServer[i].tilesMultiple==serverInfo.tilesMultiple &&
				listaServer[i].tileStampaAltezza==serverInfo.tileStampaAltezza &&
				listaServer[i].tileStampaLarghezza==serverInfo.tileStampaLarghezza &&
				listaServer[i].opacity==serverInfo.opacity) {
					return listaServer[i].id;
			} 
		}
		// Se non c'e' genero un SERVERID
		serverInfo.id = "AUTOGENERATED" + Math.random();
		listaServer.push(serverInfo);
		return serverInfo.id;
		
	} else {
		this.serverPool = [];
		serverInfo.id = "AUTOGENERATED" + Math.random();
		listaServer.push(serverInfo);
		return serverInfo.id;
	}
	
}

/**
 * @method isQGISExportable
 * 
 * 
 * @param {Number} nMappa
 * 
 * 
 * @param {String} catTreeIdx
 * 
 * 
 * @param {String} layTreeIdx
 * 
 * 
 * @return {Boolean}
 * 
 * 
 */
ToloParamsJS.prototype.isQGISExportable=function(nMappa, catTreeIdx, layTreeIdx) {

	var mappa = this.mappe.mappaList[nMappa];
	var cat = this.getLegendaCategory(nMappa, catTreeIdx);
	var lay = cat.layerList[layTreeIdx];
	var serverID = lay.serverID;
	var server = this.getServer(serverID, mappa);
	
	return (server.typeCode=='11');
}

/**
 * @method isQueryBuilder
 *  
 * @return {Boolean}
 */
ToloParamsJS.prototype.isQueryBuilder=function() {
	var qb = 0;
	for(var i=0; i<this.azioniEventi.eventiLayerList.length; i++) {
		var eventiLayer = this.azioniEventi.eventiLayerList[i];
		if (eventiLayer.queryBuilder) {
			qb++;
			break;
		}
	}
	return (qb>0) ? true : false;
}


ToloParamsJS.prototype.withSpatialiteExporter=function(codTPN) {	
	for(var i=0; i<this.azioniEventi.eventiLayerList.length; i++) {
		var eventiLayer = this.azioniEventi.eventiLayerList[i];
		if (eventiLayer.codTPN == codTPN) {			
			if(eventiLayer.queryBuilder){
				return !!eventiLayer.queryBuilder.conEsportaSpatialite;
			} else {
				return false;
			}
		}
	}	
}

ToloParamsJS.prototype.withCodeless=function() {	
	for(var i=0; i<this.azioniEventi.eventiLayerList.length; i++) {
		var eventiLayer = this.azioniEventi.eventiLayerList[i];
		if(eventiLayer.codeless){
			return true;
		}		
	}
	return false;
}


ToloParamsJS.prototype.withViewCodeless=function() {	
	for(var i=0; i<this.azioniEventi.eventiLayerList.length; i++) {
		var eventiLayer = this.azioniEventi.eventiLayerList[i];
		if(eventiLayer.viewCodeless){
			return true;
		}		
	}
	return false;
}/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloCrossAjaxUtil
 * 
 * 
 */
TolomeoExt.ToloCrossAjaxUtil = {
	
		/**
		 * @property {Object} DEFAULT_PORTS
		 * 
		 * 
		 */
		DEFAULT_PORTS : {
			'http:' : 80,
			'https:': 443
		},

		/**
		 * @method urlIsCrossDomain
		 * 
		 * 
		 * @param {String} u
		 * 
		 * 
		 */
		urlIsCrossDomain: function(u) {
			var match = /(?:(\w*:)\/\/)?([\w\.]*)(?::(\d*))?/.exec(u);
			var protocol = match[1], hostname = match[2], port = match[3];
			
			if (!protocol) return false; // No protocol, not cross-domain
			if(protocol!=location.protocol) return true;
			
			if(!port) port = this.DEFAULT_PORTS[protocol];
			var locationPort = location.port? location.port : this.DEFAULT_PORTS[location.protocol];
			
			if(port != locationPort) return true;			
			return hostname != location.hostname;
			//return (match[1] != location.protocol) || (match[2] != location.host);
		}, 

		/**
		 * @method getProxy
		 * 
		 * 
		 * @param {String} scriptTag
		 * 
		 * 
		 * @param {String} url
		 * 
		 * 
		 * @param {String} method
		 * 
		 * 
		 */
		getProxy: function(scriptTag, url, method) {
			var proxy=null;
			var reqMethod = (method || 'POST').toUpperCase();
			
			if ((scriptTag != undefined) && (scriptTag != null)) { 
				proxy = (scriptTag) ?  Ext.create('Ext.data.proxy.JsonP',{
						url: url,
						reader: 'json'
					}) : Ext.create('Ext.data.proxy.Ajax',{ 
					api: {
						'read': url, // {'url': url,'method': reqMethod}
						actionsMethods: {read: reqMethod }
					}, 
					reader: {
						readRecordsOnFailure: false
					}
				}); 
			} else {
				proxy = (TolomeoExt.ToloCrossAjaxUtil.urlIsCrossDomain(url)) ? Ext.create('Ext.data.proxy.JsonP',{
						url: url,
						reader: 'json'
					}) : Ext.create('Ext.data.proxy.Ajax',{
					timeout: Ext.Ajax.timeout,
					api: {
						'read': url,
						actionsMethods: {read: reqMethod }
					}, 
					reader: {
						readRecordsOnFailure: false
					}
				});
			}
			return proxy;
		}

}

/**
 * @class TolomeoExt.ToloCrossAjax
 * @extends Ext.util.Observable
 * 
 * 
 */
Ext.define('TolomeoExt.ToloCrossAjax', {
	
	extend: 'Ext.util.Observable',
	
	/** 
	 * @property {Object} store
	 * 
	 * 
	 */
	store: null,
	
	/*
	urlIsCrossDomain: function(u) {
		var match = /(?:(\w*:)\/\/)?([\w\.]*(?::\d*)?)/.exec(u);
		if (!match[1]) return false; // No protocol, not cross-domain
		return (match[1] != location.protocol) || (match[2] != location.host);
	},
	*/
	
	/**
	 * @method request
	 * 
	 * 
	 * @param {Object} options
	 * opzioni della richiesta.
	 * 
	 */
	request: function(options) {
		var me= this;
		var proxy = TolomeoExt.ToloCrossAjaxUtil.getProxy(options.scriptTag, options.url, options.method);
				
		// Creazione Store 
		 this.store = new Ext.data.JsonStore({
									    // store configs
									    autoDestroy: false,
									    proxy: proxy
									});
		
		 //this.store.on('exception', this.storeException);
		 proxy.on('exception', this.storeException);
		 
		// Lettura Dati
		this.store.load({
			params: options.params,
			callback: function(records, loadOptions, success) {
									me.loadCallback(records, loadOptions, success, options);
									},
			scope: me
		});		
	},
	
	/**
	 * @method loadCallback
	 * 
	 * 
	 * @param {Object} records
	 * record caricati.
	 * 
	 * @param {Object} loadOptions
	 * opzioni di caricamento.
	 * 
	 * @param {Object} success
	 * indica se l'operazione ha avuto successo.
	 * 
	 * @param {Object} originalOptions
	 * opzioni di caricamento originali. 
	 * 
	 */
	loadCallback: function(records, loadOptions, success, originalOptions) {

		if (success) {
			originalOptions.success.call(originalOptions.scope, records, this.store, originalOptions);
		} else {
			if (originalOptions.failure) originalOptions.failure.call(originalOptions.scope, this.store, originalOptions); 
		}

	},
	
	/**
	 * @method storeException
	 * 
	 * 
	 * @param {Object} dataProxy
	 * il proxy.
	 * 
	 * @param {Object} type
	 * il tipo dell'eccezione.
	 * 
	 * @param {Object} action
	 * l'azione associata.
	 * 
	 * @param {Object} options
	 * le opzioni . 
	 * 
	 * @param {Object} response
	 * la risposta ricevuta.
	 * 
	 * @param {Object} arg
	 * gli argomenti.
	 * 
	 */
	//  Ext.data.proxy.Proxy this, Object response, Ext.data.Operation operation, Object eOpts
	//storeException: function(dataProxy, type, action, options, response, arg) {
	storeException: function(dataProxy, response, operation, eopts) {
		
		var msg = "";
		
		/*
		if (type=='remote')  {
			msg = response.responseText;
	
		} else {
			//server ok
			if (response && response.status == 200) {
				var decodedResponse = (response.responseText == undefined) ? response : Ext.decode(response.responseText);
				msg = decodedResponse.msgErrore;
				stack = decodedResponse.msgStackTrace;
			//server ko
			} else {
				msg = 'Risorsa chiamata: ' + options.url + '<br/><br/>Risposta: ' + response.status + ' ' + response.statusText;
				stack = null;
			}
		}
		*/
		//server ok
		if (response && response.status == 200) {
			var decodedResponse = (response.responseText == undefined) ? response : Ext.decode(response.responseText);
			//msg = decodedResponse.msgErrore;
			//stack = decodedResponse.msgStackTrace;
			
			msg = decodedResponse.rows[0].msgErrore;
			stack = decodedResponse.rows[0].msgStackTrace;
		//server ko
		} else {
			msg = ToloI18n.getMsg("ToloCrossAjax.ErrMsg",{ URL: operation.request.url, STATUS: response.status, STATUSTEXT: response.statusText});
			stack = null;
		}
		
		this.stackDisabled = (stack == '' || stack == undefined) ? true : false; 
		
		this.errorWin = new Ext.Window({
			title: ToloI18n.getMsg("ToloCrossAjax.winError.title"),
			layout: 'anchor',
			defaultAnchor : '0',
			//bodyStyle:'background-color:white',
			plain: true,
			border: false,
			modal: true,
			padding: 5,
			width: 500,
			autoHeight: true,
			autoScroll: true,
			buttonAlign: 'center',
			buttons: [{
				text: 'OK',
					listeners: {click: {fn: function() {
	       			this.errorWin.hide();
	       		},scope: this}}	
			}],
			items: [{
				xtype: 'box',
				html: '<div class="finestraIcona finestraIconaErrore"></div><div class="finestraIconaTesto">' + msg + '</div>',
				//bodyStyle: 'padding:5px',
				style: {
		            paddingBottom: '25px'
		        }
			},{
				xtype: 'panel',
				layout: 'fit',
				title: 'Stacktrace',
				collapsible: true,
				collapsed: true,
				titleCollapse: true,
				border: true,
				disabled: this.stackDisabled,
				items: [{
					xtype: 'textarea',
					//anchor : '0',
					hideLabel: true,
					readOnly: true,
					height: 300,
					autoScroll: true,
					style: {
						font: '11px courier new'
					},
		            value: stack
				}],
				listeners: {
					'expand' : {
						fn: function() {
							this.errorWin.setWidth(800);
							this.errorWin.center();
						},
						scope: this
					},
					'collapse' : {
						fn: function() {
							if (this.errorWin) {
								this.errorWin.setWidth(500);
								this.errorWin.center();
							}
						},
						scope: this
					}
				}
			}]
		}).show();
	},
	
	/**
	 * @method onDestroy
	 * 
	 * 
	 */
	onDestroy: function() {
		if (store) store.destroy();
		this.callParent();
	}
	
});


/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * @class TolomeoExt.ToloMeasurePanelExt
 * @extends Ext.Window
 *
 */
Ext.define('TolomeoExt.ToloMeasurePanelExt', {
	extend: 'Ext.Window',	
 
	/** 
	 * @property {Boolean} [closable=false]
	 * 
	 * 
	 */
	closable: false,

	/** 
	 * @property {Boolean} [bodyBorder=false]
	 * 
	 * 
	 */
	bodyBorder: false,

	/** 
	 * @property {Boolean} [border=false]
	 * 
	 * 
	 */
	border: false,

	/** 
	 * @property {Boolean} [frame=true]
	 * 
	 * 
	 */
	frame: true,
	
	/** 
	 * @property {Boolean} [header=false]
	 * 
	 * 
	 */
	header: false,

	/** 
	 * @property {Boolean} [resizable=false]
	 * 
	 * 
	 */
	resizable: false,

	/** 
	 * @property {Boolean} [constraint=true]
	 * 
	 * 
	 */
	constrain: true,

	/*
	 * @property {Boolean} [monitorResize=true]
	 * 
	 * 
	 */
//	monitorResize: true,

	/*
	 * @property {Number} [width=180]
	 * 
	 */
	//width: 180,

	/** 
	 * @property {String} [bodyStyle='background-color:white;padding: 2px 5px 2px 5px;']
	 * 
	 */
	bodyStyle: 'background-color:white;padding: 2px 5px 2px 5px;',

	/**
	 * @method initComponent
	 * Create a new TolomeoExt.ToloMeasurePanelExt
	 * 
	 */	
	initComponent: function() {
		
		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);

		this.callParent(arguments);
		this.doLayout();
	},
   
	/**
	 * @method showMeasure
	 * 
	 * 
	 * @param {Object} tipo
	 * tipo.
	 * 
	 */ 
	showMeasure: function (tipo) {
		this.update(ToloI18n.getMsg("ToloMeasurePanelExt.showMeasure.msg"));
		this.setVisible(true);
		this.setPosition(0,0);
		//this.toFront();		
	},

	/**
	 * @method hideMeasure
	 * 
	 * 
	 */ 
	hideMeasure: function () {
		this.setVisible(false);
	},
	
	/**
	 * @method displayMeasure
	 * 
	 * 
	 * @param {Object} measureObj
	 * Object with following properties 
	 * dimension: 1 = line, 2 = polygon
	 * length: object with "measure" and "units" properties
	 * area: object with "measure" and "units" properties
 	 */ 
	displayMeasure: function (measureObj) {
		if(measureObj.dimension == 1){
			this.update(ToloI18n.getMsg("ToloMeasurePanelExt.displayMeasure.lung", {LENGTH: Ext.util.Format.number(measureObj.length.measure,"0,000.000"), UNITS: measureObj.length.units}));
		} else {
			this.update(ToloI18n.getMsg("ToloMeasurePanelExt.displayMeasure.area", {LENGTH: Ext.util.Format.number(measureObj.length.measure,"0,000.000"),
																					 LENGTHUNITS: measureObj.length.units,
																					 AREA: Ext.util.Format.number(measureObj.area.measure,"0,000.000"),
																					 AREAUNITS: measureObj.area.units }));
		}
		this.setVisible(true);		
	},

	/**
	 * @method onMeasureEnd
	 * Chiamata quando il poligono di misura è finito
	 *
	 * @param {Object} geom
	 * geom.
	 * 
	 */ 
	onMeasureEnd: function(geom) {},

	/**
	 * @method bindToViewerPanel
	 * 
	 * 
	 * @param {Object} viewer
	 * viewer.
	 * 
	 */ 
	bindToViewerPanel: function(viewer) {
		if (viewer!=null) {
			// Registrazione in viewerPanel
			viewer.on('onMeasureStart', this.showMeasure, this );
			viewer.on('onMeasureStop', this.hideMeasure, this);
			viewer.on('onMeasureChanging', this.displayMeasure, this);
			viewer.on('onMeasureChanged', this.displayMeasure, this);
		}
	}


});
 /* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloPanelBase
 * @extends Ext.Panel
 * 
 * 
 */
Ext.define('TolomeoExt.ToloPanelBase', {

	extend: 'Ext.Panel',
	
	alias: 'tx_ToloPanelBase',
	
	/** 
	 * @property {Object} paramsJS
	 * 
	 * 
	 */
	paramsJS: null,

	/** 
	 * @property {String} TOLOMEOServer
	 * 
	 * 
	 */
	TOLOMEOServer: null,

	/** 
	 * @property {String} TOLOMEOContext
	 * 
	 * 
	 */
	TOLOMEOContext: null,
	
	/** 
	 * @property {String} TOLOMEOStaticRoot
	 * 
	 * 
	 */
	TOLOMEOStaticRoot: null,

	/** 
	 * @property {Object} toolbarOpt
	 * Parametri di configurazione per la toolbar 
	 * 
	 */
	toolbarOpt: null,

	/** 
	 * @property {TolomeoExt.ToloButtonPanelExt} toolbar
	 * 
	 * 
	 */
	toolbar: null,
	
	/** 
	 * @property {Object} statusbar
	 * 
	 * 
	 */
	statusbar: null,

	/** 
	 * @property {Object} mapPanel
	 * 
	 * 
	 */
	mapPanel: null,

	/** 
	 * @property {Object} ricercaPanel
	 * 
	 * 
	 */
	ricercaPanel: null,

	/** 
	 * Property: queryBuilderPanel
	 * {} 
	 */
	queryBuilderPanel: null,
	
	/**
	* Property: olsPanel
	* {} 
	*/
	olsPanel: null,
	
	/** 
	 * Property: codeLessPanel
	 * {} 
	 */
	codeLessPanel: null,
	
	/** 
	 * Property: viewCodeLessPanel
	 * {} 
	 */
	viewCodeLessPanel: null,
	
	/** 
	 * Property: legendaPanel
	 * {} 
	 */
	legendaPanel: null,

	/**
	 * @property {TolomeoExt.ToloStylePanel} stylePanel
	 * Pannello di gestione degli stili 
	 * 
	 */
	stylePanel: null,
	
	/** 
	 * @property {Object} viewerConfig
	 * configurazione che sarà utilizzata per il viewer
	 * 
	 */
	viewerConfig: null,

	/** 
	 * @property {Object} APIConfig
	 * configurazione che sarà utilizzata per il viewer
	 * 
	 */
	APIConfig: null,

	/** 
	 * @property {Object} api
	 * 
	 * 
	 */
	api: null,

	/** 
	 * @property {Object} ricercaPanelOpt
	 * 
	 * 
	 */
	ricercaPanelOpt: null,
	
	/** 
	 * Property: queryBuilderPanelOpt
	 * {}
	 */
	queryBuilderPanelOpt: null,
	
	/** 
	 * Property: featureGridPanelOpt
	 * {}
	 */
	featureGridPanelOpt: null,

	/** 
	 * Property: formCodelessPanelOpt
	 * {}
	 */
	formCodelessPanelOpt: null,

	/** 
	 * Property: formViewCodelessPanelOpt
	 * {}
	 */
	formViewCodelessPanelOpt: null,

	/** 
	 * @property {Object} legendaPanelOpt
	 * 
	 * 
	 */
	legendaPanelOpt: null,
	
	/** 
	 * Property: olsPanelOpt
	 * {}
	 */
	olsPanelOpt: null,

	/**
	 * @property {Object} stylePanelOpt
	 * Opzioni di configurazione dell'eventuale pannello di gestione degli stili. Se non definito non viene attivata la funzionalità di gestione stili
	 * 
	 */
	stylePanelOpt: null,
	
	
	/** 
	 * @property {Object} mapPanelOpt
	 * 
	 * 
	 */
	mapPanelOpt: null,
	
	/** 
	 * @property {Object} toolsPanelOpt
	 * 
	 * 
	 */
	toolsPanelOpt: null,
	
	/** 
	 * @property {Object} timeMachinePanelOpt
	 * 
	 * 
	 */
	timeMachinePanelOpt: null,
	
	/** 
	 * @property {String} [titoloMappa='Mappa di Prato']
	 * 
	 * 
	 */
	titoloMappa: null,
	
	/** 
	 * @property {String} descrizioneMappa
	 * 
	 * 
	 */
	descrizioneMappa: null,
	
	/** 
	 * @property {Boolean} [stampaReferer=true]
	 * 
	 * 
	 */
	stampaReferer: true,
	
	 * @property {String} [urlLogo=""]
	 * 
	 * 
	 */
	urlLogo: "",
	
	/** 
	 * @property {String} [urlLogoSecondario=""]
	 * 
	 * 
	 */
	urlLogoSecondario: "",
	
	/** 
	 * @property {Boolean} withDefaultToolbar
	 * Impostare a false se non si desidera che il pannello base imposti la propria tbar
	 * 
	 */
	withDefaultToolbar: true,
	
	/** 
	 * @property {Boolean} withDefaultStatusbar
	 * Impostare a false se non si desidera avere la statusBar
	 * 
	 */
	withDefaultStatusbar: true,

	/**
	 * @method initComponent
	 * Create a new TolomeoExt.ToloPanelBase
	 *
	 * Returns:
	 * {<TolomeoExt.ToloPanelBase>} un nuovo TolomeoExt.ToloPanelBase.
	 */
	initComponent: function() {
		
		if (this.titoloMappa==null) {
			this.titoloMappa = ToloI18n.getMsg("ToloPanelBase.titoloMappa");
		}
		
		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		//this.monitorResize=true;
		
		if (this.toolbarOpt) {
			TolomeoExt.applyIfEmpty(this.toolbarOpt, {
				paramsJS: this.paramsJS, 
				items   : []
			});
			this.toolbar = new TolomeoExt.ToloButtonPanelExt(this.toolbarOpt);
			
			if(this.withDefaultToolbar) {
				this.tbar = this.toolbar;
			}
		}
		
		if(!this.statusbar){
			this.statusbar = new Ext.ux.StatusBar({
	            defaultText: '',
	            statusAlign: 'left',
	            items: []
	        });
	        
	        if(this.withDefaultStatusbar){			
				this.bbar = this.statusbar;
			}
		}		
		
		this.callParent(arguments);
		
		if (this.ricercaPanelOpt) {
    		TolomeoExt.applyIfEmpty(this.ricercaPanelOpt, {
    			title     : ToloI18n.getMsg("ToloPanelBase.ricercaPanelOpt.title"),
			    autoScroll: 'true',
			    iconCls   : 'iconQuery',
			    paramsJS  : this.paramsJS, 
			    items     : new Array()
    		});
			this.ricercaPanel = new TolomeoExt.ToloQueryPanelExt( this.ricercaPanelOpt );
		}
		
		if (this.paramsJS.isQueryBuilder() && this.queryBuilderPanelOpt) {
			
			var qbEventManager = Ext.create('TolomeoExt.events.ToloQueryBuilderEvtManager');
			
			var qbFeatureManager = Ext.create('TolomeoExt.ToloFeatureManager', {
				TOLOMEOServer: this.TOLOMEOServer,
				TOLOMEOContext: this.TOLOMEOContext
			});
			
    		TolomeoExt.applyIfEmpty(this.queryBuilderPanelOpt, {
    			title     : ToloI18n.getMsg("ToloPanelBase.querybuilder.title"),
			    autoScroll: 'true',
			    iconCls   : 'iconQuery',
			    paramsJS  : this.paramsJS, 
				TOLOMEOServer : this.TOLOMEOServer,
				TOLOMEOContext: this.TOLOMEOContext,
				caseInsensitiveMatch: false,
				qbFeatureManager: qbFeatureManager,
				qbEventManager: qbEventManager,
				
				autoCompleteCfg: {
					url: this.TOLOMEOServer + this.TOLOMEOContext + '/UniqueValueServlet',
					pageSize: 10
				},				
				autoComplete: true,
			    items     : new Array()
				//,ogcFilterVersion : '1.0.0'
    		});
			this.queryBuilderPanel = Ext.create('TolomeoExt.ToloQueryBuilderExt',  this.queryBuilderPanelOpt);
			
    		TolomeoExt.applyIfEmpty(this.featureGridPanelOpt, {
    			title     : ToloI18n.getMsg("ToloPanelBase.grigliaris.title"),
			    autoScroll: 'true',
				border: false,
				layout: 'fit',
			    paramsJS  : this.paramsJS, 
			    qbFeatureManager: qbFeatureManager,
			    qbEventManager: qbEventManager,
			    items     : new Array()
    		});
			this.featureGridPanel = Ext.create('TolomeoExt.ToloFeatureGridPanel',  this.featureGridPanelOpt);
		}
		
		if (this.paramsJS.layOut.ols && this.olsPanelOpt) {
		 		TolomeoExt.applyIfEmpty(this.olsPanelOpt, {
		   			title     : ToloI18n.getMsg("ToloPanelBase.ols.title"),
				    autoScroll: 'true',
				    iconCls   : 'iconQuery',
				    paramsJS  : this.paramsJS
		   		});
				this.olsPanel = new TolomeoExt.OLS.ToloOLSPanelExt( this.olsPanelOpt );
		}
		
		// /////////////////////
		// Codeless Panel
		// /////////////////////
		if(this.paramsJS.withCodeless() && this.formCodelessPanelOpt){
    		TolomeoExt.applyIfEmpty(this.formCodelessPanelOpt, {
    			//title     : 'Codeless Form',
    			header: false,
			    autoScroll: 'true',
			    iconCls   : 'iconCodelss',
			    paramsJS  : this.paramsJS, 
				TOLOMEOServer : this.TOLOMEOServer,
				TOLOMEOContext: this.TOLOMEOContext,
//				cmdToolbar: this.toolbar,
			    items     : new Array()
    		});
			this.codeLessPanel = Ext.create('TolomeoExt.ToloCodeLessPanel',  this.formCodelessPanelOpt);
		}
		
		if(this.paramsJS.withViewCodeless() && this.formViewCodelessPanelOpt){
    		TolomeoExt.applyIfEmpty(this.formViewCodelessPanelOpt, {
    			//title     : 'New Codeless Form',
    			header: false,
			    autoScroll: 'true',
			    iconCls   : 'iconCodelss',
			    paramsJS  : this.paramsJS, 
				TOLOMEOServer : this.TOLOMEOServer,
				TOLOMEOContext: this.TOLOMEOContext,
//				cmdToolbar: this.toolbar,
			    items     : new Array()
    		});
			this.viewCodeLessPanel = Ext.create('TolomeoExt.ToloViewCodeLessPanel',  this.formViewCodelessPanelOpt);
		}
		
		if (this.legendaPanelOpt) {
			TolomeoExt.applyIfEmpty(this.legendaPanelOpt, {
				title         : ToloI18n.getMsg("ToloPanelBase.legenda.title"),
				autoScroll    : 'true',
				cls           : 'clearCSS',
				iconCls       : 'iconToc',
				paramsJS      : this.paramsJS,
				TOLOMEOServer : this.TOLOMEOServer,
				TOLOMEOContext: this.TOLOMEOContext,
				TOLOMEOStaticRoot: this.TOLOMEOStaticRoot,
				xtype         : 'tx_toloTreeTOCPanelExt'
				
			});

			this.legendaPanel =  Ext.widget(this.legendaPanelOpt);
		}

		if (this.stylePanelOpt) {
			TolomeoExt.applyIfEmpty(this.stylePanelOpt, {
				closeAction: 'hide',
				TOLOMEOServer: this.TOLOMEOServer,
				TOLOMEOContext: this.TOLOMEOContext,
				TOLOMEOStaticRoot: this.TOLOMEOStaticRoot,
				width: 500,
				height: 250
			});
			this.stylePanelOpt.closeAction = 'hide';
			
			this.stylePanel = new TolomeoExt.ToloStylePanel(this.stylePanelOpt); 
		}
		
		
		if (this.timeMachinePanelOpt) {
			
			if (this.timeMachinePanelOpt.carouselConfig) {
				
				TolomeoExt.applyIfEmpty(this.timeMachinePanelOpt.carouselConfig, {
					interval: 3,
				    autoPlay: true,
				    showPlayButton: true,
				    pauseOnNavigate: true,
				    freezeOnHover: true,
				    transitionType: 'fade',
				    transitionEasing: 'fadeIn',    
				    navigationOnHover: false    
					
					});
			} else {
				this.timeMachinePanelOpt.carouselConfig = {
						interval: 3,
					    autoPlay: true,
					    showPlayButton: true,
					    pauseOnNavigate: true,
					    freezeOnHover: true,
					    transitionType: 'fade',
					    transitionEasing: 'fadeIn',    
					    navigationOnHover: false    
						
						};
			}
			
    		TolomeoExt.applyIfEmpty(this.timeMachinePanelOpt, {
    			title     : ToloI18n.getMsg("ToloPanelBase.timemachine.title"),
    			paramsJS      : this.paramsJS
    		});
			this.timeMachinePanel = new TolomeoExt.ToloTimeMachinePanel( this.timeMachinePanelOpt );
		}
		
	    var cfg = Ext.apply({}, this.viewerConfig);		
	    cfg.bOnOpenDrawMap = ((this.APIConfig!=null) && (this.APIConfig.openActionsJS!=null)) ? false : (this.viewerConfig!=null) ? this.viewerConfig.bOnOpenDrawMap : true;
		TolomeoExt.applyIfEmpty (cfg, {
	    	region    : 'center',
	        xtype     : "tx_toloviewerOLPanel",
		    "paramsJS": this.paramsJS
	    });
		
		this.mapPanel = new TolomeoExt.ToloViewerOLPanel(cfg);	 		
		
    },
    
    /**
     * @method afterRender
     * @private
     * Metodo privato invocato dopo che il pannello è stato renderizzato.
     * 
     */
    afterRender: function() {	
    	this.callParent(arguments);
    	if (this.api==null) {
	    	var cfg = Ext.apply({}, this.APIConfig);
	    	this.api = Ext.create('TolomeoExt.ToloMapAPIExt', Ext.apply(cfg,{
				"paramsJS"        : this.paramsJS,
				TOLOMEOServer	  : this.TOLOMEOServer,
				TOLOMEOContext	  : this.TOLOMEOContext,
				TOLOMEOStaticRoot : this.TOLOMEOStaticRoot,
				viewer            : this.mapPanel,
				buttonsPanel      : this.toolbar,
				TOCPanel          : this.legendaPanel,
				stylePanel		  : this.stylePanel,
				queryPanel        : this.ricercaPanel,
				olsPanel          : this.olsPanel,
				queryBuilderPanel : this.queryBuilderPanel,
				featureGridPanel  : this.featureGridPanel,
				codeLessPanel     : this.codeLessPanel,
				viewCodeLessPanel : this.viewCodeLessPanel,
				titoloMappa       : this.titoloMappa,
				descrizioneMappa  : this.descrizioneMappa,
				urlLogo           : this.urlLogo,
				urlLogoSecondario : this.urlLogoSecondario,
				stampaReferer     : this.stampaReferer,
				statusPanel       : this.statusbar
			}));		    	
		}
    }		
});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * @class TolomeoExt.ToloScaleComboExt
 * @extends Ext.form.ComboBox
 * Una combo box che consente di scegliere la scala della mappa
 *
 */ 
Ext.define('TolomeoExt.ToloScaleComboExt', {

	extend: 'Ext.form.ComboBox',
 
	/** 
	 * @property {Number} [width=180]
	 * 
	 * 
	 */
	width:180,
	
	/** 
	 * @property {Number} [labelWidth=60]
	 * 
	 * 
	 */
	labelWidth:60,

	/** 
	 * @property {String} [listClass='scaleListCss']
	 * 
	 * 
	 */
	formItemCls:'scaleListCss' , 
	
	/*
	 * @property {String} [listClass='scaleListCss']
	 * 
	 * 
	 */
	// listClass: 'scaleListCss' , 

	/** 
	 * @property {Boolean} [forceSelection=false]
	 * false, per non forzare la scelta dalla lista
	 * 
	 */
	forceSelection: false, 

	/** 
	 * @property {String} [fieldLabel='Scala ']
	 * 
	 * 
	 */
	fieldLabel: null,

	/** 
	 * @property {String} [labelSeparator='=']
	 * 
	 * 
	 */
	labelSeparator : '=',

	/** 
	 * @property {String} [labelStyle='font-weight:bold;text-align:right;width:60px;']
	 * 
	 * 
	 */
	labelStyle: 'font-weight:bold;text-align:right;width:60px;',

	/** 
	 * @property {String} [listAlign='bl-tl']
	 * 
	 * 
	 */
	listAlign: 'bl-tl',

	/** 
	 * @property {RegExp} [maskRe=/\d/]
	 * 
	 * 
	 */
	maskRe:/\d/,     

	/** 
	 * @property {String} [triggerAction='all']
	 * 
	 * 
	 */
	triggerAction: 'all',

	/** 
	 * @property {String} [emptyText='']
	 * 
	 * 
	 */
	emptyText: '',

	/** 
	 * @property {Boolean} [selectOnFocus=true]
	 * 
	 * 
	 */
	selectOnFocus: true,
	
	/** 
	 * @property {String} [displayTpl='<tpl for=".">1 : {[Ext.util.Format.number(parseInt(values.scaleValue),"0,000")]}</tpl>']
	 * 
	 * 
	 */
	displayTpl:'<tpl for=".">1 : {[Ext.util.Format.number(parseInt(values.scaleValue),"0,000")]}</tpl>',
        
	/** 
	 * @property {Object} listConfig
	 * 
	 * 
	 */
    listConfig: {
        // Custom rendering template for each item
        getInnerTpl: function() {    
            //return '1 : {[Ext.util.Format.number(parseInt(values.scaleValue),"0,000")]}';
        	
        	 return '{[values.custom ? "<span style=\\"color:red;\\">1 : " + Ext.util.Format.number(parseInt(values.scaleValue),"0,000") + "</span>": "1 : " + Ext.util.Format.number(parseInt(values.scaleValue),"0,000")]}';
        }
    },

	/*
	 * @property {Boolean} [editable=false]
	 * 
	 * 
	 */
    //editable : false,
    
	/*
	 * @property {Boolean} [forceSelection=true]
	 * 
	 * 
	 */
    //forceSelection : true,
    
	/*
	 * @property {Boolean} [hideTrigger=false]
	 * 
	 * 
	 */
    //hideTrigger:false,
    	
	/** 
	 * @property {String} [valueField='scaleValue']
	 * 
	 * 
	 */
	valueField: 'scaleValue',

	/** 
	 * @property {String} [displayField='scaleDescr']
	 * 
	 * 
	 */
	displayField: 'scaleDescr',

	/** 
	 * @property {Boolean} [enableKeyEvents=true]
	 * 
	 * 
	 */
	enableKeyEvents: true,
	
	/** 
	 * @property {String} [queryMode='local']
	 * 
	 * 
	 */
	queryMode: 'local',
	
	/** 
	 * @property {Object} listeners
	 * 
	 * 
	 */
	listeners : {
		
	    select: {
	        fn: function(){
	            this.notifyChange(this.getValue());
	            return false;
	        }
	    },	   	   
	    
	    specialkey: {	    	
	       fn: function(cb, e){
	       		var me = this;
	          	if (e.getKey() == e.ENTER) {	              	              
	              // if record is not present in the store we add it temporary to store to make manage the value at the combobox  	          	 
	          		var v = cb.getValue();	 	          		
			        cb.forceValue(v);
	             	cb.notifyChange(v);
	             	return false;
	          	}
	       }
	     },
	    
	     afterRender: {
	        fn: function() {
	            var me = this;                    
	            // Also if editable allows opening the picker by clicking the field
	            if (this.editable) {
	                 me.mon(me.inputEl, 'click', me.onTriggerClick, me);
	            }
	        }
	     },
	     
	     click: {
	     	fn: function(e){
	     		e.stopEvent();
	     	}
	     }
	},

	/**
	 * @method initComponent
	 * Crea un nuovo TolomeoExt.ToloScaleComboExt.
	 * 
	 */		
	initComponent: function() {
		
    	// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
	
		this.fieldLabel = ToloI18n.getMsg("ToloScaleComboExt.fieldLabel");
		
	    this.addEvents('scalechange');	    
		       
/*
	    this.plugins = [ new Ext.DomObserver({
          click: function(evt, comp) {
              comp.onLoad.defer(10,this);  
              comp.setRawValue(comp.getValue());    
              comp.selectByValue(comp.getValue());              
              comp.selectText();
          }
        })];	    
*/
        this.callParent(arguments);	
    
		Ext.util.CSS.createStyleSheet('.scaleListCss{text-align: right;}','scaleListCss');

	},
			
	/**
	 * @method cleanValue
	 * Clean the value of the scale from bad character
	 * 
	 * @param {String} val
	 * val.
	 * 
	 */
	cleanValue : function(val){
	    return (""+val).replace(/^(\s*1\s*:)?|[^0-9]/g,'');
	},
	  
	/**
	 * @method notifyChange
	 * Private method to notify a scale change
	 * 
	 * @param {String} val
	 * value
	 * 
	 */
	notifyChange : function(val){		
	    this.fireEvent('scalechange', this.cleanValue(val));	
	},
	
	/*
	 * @method forceValue
	 * Call this method to set the scale value desired
	 * 
	 * @param {Object} val
	 * 
	 * 
	forceValue : function(val){
        var v = parseInt(this.cleanValue(val)); 
        
        this.resetFilter();
        
        if(this.findRecordByValue(v)){
            this.select(v);
        } else {            
            var scaleStore = this.getStore();
            scaleStore.add({scaleValue : v, scaleDescr : '' + v});
            var r = this.findRecordByValue(v);
            this.setValue(v);
            scaleStore.rejectChanges();
            
        }
    },
    */
	
	/**
	 * @method forceValue
	 * Call this method to set the scale value desired
	 * 
	 * @param {Object} val
	 * 
	 * 
	 */
	forceValue : function(val){
		
            var v = parseInt(this.cleanValue(val)); 
            var scaleStore = this.getStore();
            
            this.resetFilter();
            var f = this.findRecordByValue(v);            
             
            if(f){                
                this.select(v);
                /*
                if(!f.get('custom')){
                    scaleStore.rejectChanges();
                }
                */
            } else {   
                scaleStore.rejectChanges();
                scaleStore.add({scaleValue : v, scaleDescr : '' + v, custom : true});
                var r = this.findRecordByValue(v);
                this.setValue(v);
                //scaleStore.rejectChanges();                
            }
        },
    
    /**
	   * @method resetFilter
	   * Clean the query filter to have the complete list of items
	   * 
  	 */
    resetFilter : function(){
    	var st = this.getStore();	          		
  		var filter = this.queryFilter;
  		
        // If filtered on typed value, unfilter.
        if (filter && !filter.disabled) {
            filter.disabled = true;
            st.filter();
        }	    
    }
});
 
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


 
/**
 * @class TolomeoExt.ToloScalePanelExt
 * @extends Ext.FormPanel
 * 
 * 
 */ 
Ext.define('TolomeoExt.ToloScalePanelExt', {

	extend: 'Ext.FormPanel',
 
 	/** 
	   * @property {Boolean} [autoHeight=true]
	   * 
	   * 
		 */
        autoHeight: true,
        
    	/** 
         * @property {String} [bodyCssClass='scaleFormCss']
         * 
         * 
      	 */
        bodyCls : 'scaleFormCss',  
        
    	/** 
         * @property {Number} [width=190]
         * 
         * 
      	 */
        width: 190,
        
    	/** 
         * @property {Boolean} [bodyBorder=true]
         * 
         * 
      	 */
        bodyBorder: true,
        
    	/** 
         * @property {Array} [defaultZoomLevels=[]]
         * 
         * 
      	 */
        defaultZoomLevels: [],
        
    	/** 
         * @property {Object} scaleCombo
         * 
         * 
      	 */
        selectorConfig : null,    
        
        /** 
         * @property {Boolean} [settableZoom=true]
         * 
      	 */
        settableZoom : true,
        
    	/**
         * @method initComponent
         * Crea un nuovo TolomeoExt.ToloScalePanelExt.
         * 
         */			
        initComponent: function() {
          
          this.addEvents('scalechange');
          
          this.callParent(arguments);
		 // Ext.util.CSS.createStyleSheet('.scaleFormCss{padding: 5px 0px 2px 0px; background-color: transparent;   }','scaleFormCss');
		  this.defaultZoomLevels.sort(function(a,b){return b - a});
		      
          var myData = new Array();		      
		      
		  for(var i = 0; i < this.defaultZoomLevels.length; i++){            
          	myData.push([this.defaultZoomLevels[i],'1 : ' + Ext.util.Format.number(this.defaultZoomLevels[i],'0,000',false)]);            
          }
		  
          var myStore = new Ext.data.ArrayStore({
              fields: [
                {name: 'scaleValue', type: 'number'},
	            {name: 'scaleDescr', type: 'string'},
	            {name: 'custom', type: 'boolean'}                 
              ],
              data : myData,
              sorters: [{
		         sorterFn: function(o1, o2){
		                
			         var v1 = o1.get('scaleValue');
			         var v2 = o2.get('scaleValue');
			    
			         if (v1 == v2) {
			         	return 0;
			         }		    
			         
			         return v1 < v2 ? 1 : -1;
		         }
		      }]
          });
          
          var selectorConfig = Ext.applyIf({
          	store:myStore,
          	editable: this.settableZoom          
          }, this.selectorConfig);
          
                    
          this.zoomSelector = new TolomeoExt.ToloScaleComboExt(selectorConfig);    
          this.zoomSelector.on('scalechange', function(val) {this.fireEvent('scalechange', val)} , this );      
          this.add(this.zoomSelector);                   
        
        },
        
        /**
         * @method bindToViewerPanel
         * 
         * 
         * @param {Object} viewer
         * viewer
         * 
         */
        bindToViewerPanel: function(viewer) { 
      		if (viewer!=null) {
      			// mi registro sulla variazione di scala del viewer
      			viewer.on('scalechange', this.setScale, this );      			
      			viewer.on('onAfterPostInit', 
      				function(){ this.on('afterlayout', function(){ 
									this.setScale(viewer.pluginGetCurrentZoom()); 
								}, 
					this, {single: true});}, this );       			
      			// se la combo non perde il focus firefox dopo il cambio di dimensione non sente il click
      			viewer.on('resize', function(){if(this.zoomSelector.getEl())this.zoomSelector.getEl().dom.blur()}, this );		      			
      		}
      	},
      	
      	/**
      	 * @method setScale
      	 * 
      	 * 
      	 * @param {Number} val
      	 * val
      	 * 
      	 */
      	setScale : function(val){      	      		
        	this.zoomSelector.forceValue(Math.round(val));          
        }                               
});    
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITA' o
 IDONEITA' PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 *   
 * @class Pannello ExtJs contenente una mappa visualizzata utilizzando openlayers. 
 * @param config {Object} Oggetto contenente i parametri di configurazione. Sono ammessi tutti i parametri previsti per ExtJS.Panel ed i seguenti parametri aggiuntivi
 * @param config.paramsJS {Object} Oggetto i parametri contenuti nel file di preset.
 * @param config.customQuery {String} [] Filtro di visualizzazione 
 * 
 */

/**
 * Class: TolomeoExt.ToloViewerOLPanel
 *
 * Inherits from:
 *  - <Ext.Panel>
 *
 */
Ext.define('TolomeoExt.ToloViewerOLPanel', {

	extend: 'Ext.Panel',	
	alias: 'tx_toloviewerOLPanel',
	
	statics: {
		getWKTParts: function(wktIn) {
			if (wktIn.indexOf('MULTI')==-1){
				return [ wktIn ];
			}
			var retVal = [];
			var wktParser = new OpenLayers.Format.WKT();
			var g = OpenLayers.Geometry.fromWKT(wktIn);
			
			var parts = g.components;
			if (parts) {
				if (parts.length==1){
					return [ wktIn ];
				} else {
					for (var i=0; i<parts.length; i++) {
						var p = parts[i];
						p.getBounds().toBBOX();
						retVal.push(wktParser.extractGeometry(p));	
					}
				}
			}
			
			return retVal;
		},
		
		customQueryForServer: function(customQuery, serverID){
			var retVal = {};
			
			for (var p in customQuery) {
				if (customQuery[p] instanceof Object) {
					if (p == serverID) {
						Ext.apply(retVal, customQuery[p]);
					}
				} else {
					retVal[p] = customQuery[p];
				}
			}
			return retVal;
		},
		
		customQueryToGeoserverObj: function(cqForServer){
			
			var wmsParams = {};
			for (var i in cqForServer) {
				if (wmsParams.viewparams==undefined) wmsParams.viewparams = '';
				wmsParams.viewparams += ((wmsParams.viewparams!='') ? ';' : '') + i + ':' + cqForServer[i];
			}
			return wmsParams;
			
		}, 
		
		customQueryToMapserverObj: function(cqForServer){
			return cqForServer;
		}
		
	},
	
	/** 
	 * Property: TOLOMEOServer
	 * {String}
	 */
	TOLOMEOServer: null,

	/** 
	 * Property: TOLOMEOContext
	 * {String}
	 */
	TOLOMEOContext: null,
	
	/** 
	 * Property: TOLOMEOStaticRoot
	 * {String}
	 */	
	TOLOMEOStaticRoot: null, 

	/** 
	 * @property {Boolean} withWmsCallUUID 
	 * controlla se deve essere aggiunta uuid ad ogni chiamata
	 * 
	 */	
	withWmsCallUUID: null, 
	
	/**
	 * UUID da aggiungere alle chiamate WMS
	 */
	wmscalluuid: null,
	
	
	/** 
	 * Property: digitizeOperationInsert
	 * {OpenLayers.Map} Mappa openlayers viasualizzata in questo pannello
	 */
	map: null,

	/** 
	 * Property: mapControls
	 * {Openlayer.Controls} Vettore contenente i controlli openlayers attivi su map
	 */
	mapControls: null,
	
	/** 
	
	/** 
	 * Property: drawLayer
	 * {} 
	 */
	drawLayer: null,

	/** 
	 * Property: selezioniLayer
	 * {} 
	 */
	selezioniLayer: null,

	/** 
	 * Property: evidenziazioniLayer
	 * {}
	 */
	evidenziazioniLayer: null,
	
	 /**
	 * Property: routingLayer
	 * {}
	 */
	 routingLayer: null,
	 
	 /** 
	 * Property: routingMarkersLayer
	 * {}
	 */
	 routingMarkersLayer: null,
	 	
	 /** 
	  * Property: startMarker
	  * {}
	  */
	 startMarker: null,
	 
	 /** 
	 * Property: endMarker
	 * {}
	 */
	 endMarker: null,
	 
	 /** 
	  * Property: viaMarkers
	  * {}
	  */
	 viaMarkers: [],

	 /** 
	/** 
	 * Property: paramsJS
	 * {JSONObject} Oggetto i parametri contenuti nel file di preset.
	 */
	paramsJS: null, 

	/** 
	 * Property: customQuery
	 * {Object} Oggetto
	 */
	customQuery: null,

	/** 
	 * Property: mapBusy
	 * {Integer}
	 */
	mapBusy: 0,

	/** 
	 * Property: myMask
	 * {}
	 */
	myMask: null,

	/** 
	 * Property: isAlreadyDrawn
	 * {} Variabile che indica se e' gia stata disegnata (utilizzato in preinit e postinit
	 */
	isAlreadyDrawn: null, 

	/** 
	 * Property: olViewerToolSelectSelectEventHandler
	 * {}
	 */
	olViewerToolSelectSelectEventHandler:  null, 

	/** 
	 * Property: mapPanel
	 * {} 
	 */
	mapPanel: null,

	/** 
	 * Property: measurePanel
	 * {}
	 */
	measurePanel: null, 

	/** 
	 * Property: timeMachinePanel
	 * {}
	 */
	timeMachinePanel: null, 
	
	/** 
	 * Property: scalePanel
	 * {}
	 */
	scalePanel: null, 
	
	/** 
	 * Property: withScaleLine
	 * {}
	 */
	withScaleLine: true,
	
	/** 
	 * Property: withScaleSelector
	 * {}
	 */
	withScaleSelector: true,

	/** 
	 * Property: monitoito
	 * {Boolean}
	 */
	//monitorResize: true,

	/** 
	 * Property: bOnOpenDrawMap
	 * {Boolean} Indica se disegnare la mappa in apertura 
	 */
	bOnOpenDrawMap: null,
	
	styleSelected: null,
	
	styleHighlighted: null,
	
	styleAutoidentify: null,
	
	styleSnap: null,
	
	styleMeasure: null,
	measurePointSymbolizer: null,
	measureLineSymbolizer: null,
	measurePolygonSymbolizer: null,
	
	layerTypePrereq: [	[""],  // 0 - Mapserver
					   	["http://maps.google.com/maps?file=api&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ",
					   	 "http://maps.google.com/maps/api/js?v=3.2&sensor=false"],  // case 1:	// Google Streets	
						["http://maps.google.com/maps?file=api&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ",
						 "http://maps.google.com/maps/api/js?v=3.2&sensor=false"],  // case 2:	// Google Physical
						["http://maps.google.com/maps?file=api&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ",
						 "http://maps.google.com/maps/api/js?v=3.2&sensor=false"],  // case 3:	// Google Satellite
						["http://maps.google.com/maps?file=api&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ",
						 "http://maps.google.com/maps/api/js?v=3.2&sensor=false"],  // case 4:	// Google Hybrid
						["http://api.maps.yahoo.com/ajaxymap?v=3.0&appid=euzuro-openlayers"],  // case 5:	// Yahoo Sat 
						["http://api.maps.yahoo.com/ajaxymap?v=3.0&appid=euzuro-openlayers"],  // case 6:	// Yahoo Reg
						["http://api.maps.yahoo.com/ajaxymap?v=3.0&appid=euzuro-openlayers"],  // case 7:	// Yahoo Hybrid
						["http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2&mkt=en-us"],  // case 8:	// Bing Aerial
						["http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2&mkt=en-us"],  // case 9:	// Bing Shaded
						["http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2&mkt=en-us"],  // case 10:	// Bing Hybrid
						[""],  // case 11:	// WMS 	    		
						[""]  // case 12:	// OpenStreetMap
					], 
	  
	streetViewMarkerLayer: null,
	streetViewDragControl: null,
	streetViewNavigationLinkLayer: null,
	streetviewSelectControl: null,
	streetviewHighlightCtrl: null,
					
	// Array contentente i layer di mappa (non quelli di servizio come i layer di evidenziazione, selezione etc.)
	layersMappa: null,
	
	/**
	 * Method: initComponent
	 * Metodo ExtJS initComponent 
	 */
	initComponent: function(){
		TolomeoExt.applyIfEmpty(this, {bOnOpenDrawMap: true});
		//Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);	
		// se non definiti definisco altezza e larghezza (altrimenti problemi con IE)
		TolomeoExt.applyIfEmpty(this, { width: 50, height: 50});
		
		if  (this.withWmsCallUUID==null) {
			if (this.paramsJS.comportamento && this.paramsJS.comportamento.withWmsCallUUID) {
				this.withWmsCallUUID = true;
			} else {
				this.withWmsCallUUID = false;
			}
		}
		
		if (this.withWmsCallUUID) {
			this.wmscalluuid = uuid.v4();
		}
		
		this.layersMappa = [];
		this.layout = 'absolute';
		//this.pluginPreInit();
				
		// add custom events
        this.addEvents('scalechange');
    
        this.addEvents('loadstart');
        this.addEvents('loadend');
        this.addEvents('loadcancel');
        
        this.addEvents('onAutoIdentify');
        this.addEvents('onAutoIdentifyCancel');

        this.addEvents('onDigitizeEndPoint');
        this.addEvents('onDigitizeEndLine');
        this.addEvents('onDigitizeEndPolygon');
        this.addEvents('onDigitizeEndVertexEditing');
        this.addEvents('onDigitizeEndDragDrop');
        this.addEvents('onMappaSelect');
        this.addEvents('onMeasureStart');
        this.addEvents('onMeasureStop');
        this.addEvents('onMeasureChanging');
        this.addEvents('onMeasureChanged');
        this.addEvents('onMeasureClear');
        
        this.addEvents('onBeforePreInit');
        this.addEvents('onBeforePostInit');
        this.addEvents('onAfterPreInit');
        this.addEvents('onAfterPostInit');
         				
        this.addEvents('onDigitizedFeatureDragDropEnd');
        this.addEvents('onDigitizedFeatureVertexEditingEnd');
        				
        this.addEvents('onMapMoveEnd');
        
        // L'evento trasmette l'url dell'immagine come primo parametro
        this.addEvents('onPrintMap');
        
        this.addEvents('onDrawDistanceFromRefChange');
        this.addEvents('onDrawFirstPointFromRef');
        this.addEvents('onDigitizePointFromRefEnd');
        this.addEvents('onDrawFirstPointByCAD');
        this.addEvents('onDrawDistanceByCADChange');
        this.addEvents('onDrawAreaByCADChange');
        this.addEvents('onDigitizePointByCADEnd');
        this.addEvents('onDigitizeLineByCADEnd');
        this.addEvents('onDigitizePolygonByCADEnd');
        
        //this.addEvents('onCoordinateChange');
        
        this.addEvents('onStreetviewDropComplete');
        this.addEvents('onStreetviewNavLinkClick');
        
        this.addEvents('onTimeMachineShow');
        this.addEvents('onTimeMachineHide');
        
        this.addEvents('onMouseCoordChange');
        this.addEvents('popupClicked');
        
        // Eventi realtivi a routing
        this.addEvents('startPointMoved');
        this.addEvents('endPointMoved');
        this.addEvents('viaPointMoved');
        this.addEvents('routingInformationSelect');
        this.addEvents('routingInformationUnSelect');
                
        this.addEvents('previousNavigationStateAvailable');
        this.addEvents('previousNavigationStateNotAvailable');
        this.addEvents('nextNavigationStateAvailable');
        this.addEvents('nextNavigationStateNotAvailable');        
        
        this.callParent(arguments);
        
        var thisPanel = this;
        
        
        /* Eventuale possibilita di pannello dei widget da valutare
        this.widgetPanel = Ext.create('Ext.panel.Panel',{
        	layout : 'auto',
			anchor : '100% 100%',
			html : 'eccomi',			 
			dockedItems: [{
		        xtype: 'toolbar',
		        dock: 'top',
		        style: 'background-image: none !important; background: transparent !important;',
		        items: [{
		            text: 'Docked to the top'
		            
		            //style: 'position: absolute; z-index: 100;'
		        }]
		    }]
		});
		*/
		
		this.add(this.widgetPanel);
        
        this.mapPanel = new Ext.Panel({	
        	style: 'z-index:0;',
			layout: 'fit',	
			bodyBorder: false,
			border: false,
			anchor: '100% 100%',			
		//	monitorResize: true,
			listeners: { 
        				resize: {
								fn: function() {
									this.updateMapSize();
								}
							},
						afterrender: { 
							fn: function(){
									if(!this.ownerCt) {
							            this.renderMap();
							        } else {
							            this.ownerCt.on("move", this.updateMapSize, this);
							            this.ownerCt.on({
							                "afterlayout": this.afterLayout,
							                scope: this
							            });
							        }
								} 
						} 
				
					},
			afterLayout: function() {
								var width = this.body.getWidth() - this.body.getBorderWidth("lr");
								var height = this.body.getWidth() - this.body.getBorderWidth("tb");
								if (width > 0 && height > 0) {
									// Non funziona quindi aggiunto if this.ownerCt.un("afterlayout", this.afterLayout, this);
									if (!this.bMapRendered) this.renderMap();
									this.bMapRendered=true;
								}
						
						},
		    renderMap: function() {
		    	thisPanel.pluginPreInit();
		    	var map = thisPanel.map;
		        map.render(this.body.dom);
		       // thisPanel.updateLayout();
		        thisPanel.pluginPostInit();
		    },
		    updateMapSize: function() {
		    	if ((thisPanel.map) ) {
		    		thisPanel.map.updateSize();
		    	} 
		    }
			
		});

		this.add(this.mapPanel);										
        
		if (!this.measurePanel) { 
	        this.measurePanel = new TolomeoExt.ToloMeasurePanelExt({style: 'z-index:1;', x: 10, y: 40});
	        this.measurePanel.bindToViewerPanel(this);
	        this.add(this.measurePanel);
	    }		

        if (!this.scalePanel) {
        	
        	var defaultZoomLevels = this.getDefaultZoomLevels(); 
        	
        	var scalePanelConfig = {
        		defaultZoomLevels: defaultZoomLevels,
	        	settableZoom : this.paramsJS.mappe.settableZoom
        	}
        	
        	// If without scale line just few params are necessary otherwise we need other params
	        if(!this.withScaleLine && this.withScaleSelector) {
        		scalePanelConfig = Ext.applyIf({
		          	style: 'z-index: 2; top:auto; left:auto; right: 0px; bottom: 0px'         
		        }, scalePanelConfig);
	        } else if (this.withScaleLine && !this.withScaleSelector) { 
	        
	        	this.scaleLineBox = Ext.create('Ext.Component',{										
				    autoEl:{
				        tag:"div",			       
				        style: 'font-family: tahoma,arial,helvetica,sans-serif; font-size: xx-small;'
				    },					    
				    flex: 1			    
				});
				
				var scaleWidget = Ext.create('Ext.Panel', {		        		        
		         	width: 50,
	                height: 32,
	                bodyCls : 'scaleFormCss',
	                bodyStyle : 'padding: 4px;',
	                layout: {
	                    type: 'hbox',
	                    pack: 'end'
	                },
	                
	                defaults: {
	                    border: false
	                },
	                style: 'position: absolute; right: 0px; bottom: 0px; z-index: 2;',                
	                items: [this.scaleLineBox]
			     });
			     
			     this.add(scaleWidget);
	        
	        } else if (this.withScaleLine && this.withScaleSelector) {
        		scalePanelConfig = Ext.applyIf({
		          	width: 130,
		        	bodyCls: '',
		        	border: 0,
		        	bodyBorder: false,
		        	selectorConfig : {
		        		labelWidth:0,
						fieldLabel:'',
						labelSeparator : '',
						width: 130
		        	}        
		        }, scalePanelConfig);        		
        	}
        	
        	this.scalePanel = new TolomeoExt.ToloScalePanelExt(scalePanelConfig);	 
	        this.scalePanel.on('scalechange',function(val){this.pluginZoomToScale(val);},this);    	 
	        
	        this.scalePanel.bindToViewerPanel(this);
	        
	        // If without scale line add just the scale panel otherwise create the right widget with Component the will contain the scaleline control of Openlayers
	        if(!this.withScaleLine) {
	        	
	        	this.add(this.scalePanel);
	        	
	        } else {
	        
		        this.scaleLineBox = Ext.create('Ext.Component',{										
				    autoEl:{
				        tag:"div",			       
				        style: 'font-family: tahoma,arial,helvetica,sans-serif; font-size: xx-small;'
				    },					    
				    flex: 1			    
				});
				
				var scaleWidget = Ext.create('Ext.Panel', {		        		        
		         	width:230,
	                height: 32,
	                bodyCls : 'scaleFormCss',
	                bodyStyle : 'padding: 4px;',
	                layout: {
	                    type: 'hbox',
	                    pack: 'end'
	                },
	                
	                defaults: {
	                    border: false
	                },
	                style: 'position: absolute; right: 0px; bottom: 0px; z-index: 2;',                
	                items: [this.scaleLineBox,this.scalePanel]
			     });
			     
			     this.add(scaleWidget);
	        }			
        }
        
        this.pointFromRefPanel = new TolomeoExt.ToloPointFromRefPanelExt({style: 'z-index:1;', x: 10, y: 40});
        this.pointFromRefPanel.on('pressSetDistance', function(distance){this.mapControlsDrawLayer['pointFromRef'].handler.setDistance(distance);}, this);
        this.pointFromRefPanel.bindToViewerPanel(this);
		this.add(this.pointFromRefPanel);
	
        this.cadPanel = new TolomeoExt.ToloCADPanelExt({style: 'z-index:1;', x: 10, y: 40});
        this.cadPanel.bindToViewerPanel(this);
        this.add(this.cadPanel);
        
        /*
		 * Method: runCommandOnPointByCAD
		 * Metodo privato che esegue un comando CAD sul layer di disegno del punto
		 *
		 * Parameters:
		 * command - {String} comando CAD
		 */
        this.runCommandOnPointByCAD = function(command){
			this.mapControlsDrawLayer['pointByCAD'].handler.runCommand(command);
		};
		
		/*
		 * Method: setRelativeAngleOnPointByCAD
		 * Metodo privato che imposta se il valore di un angolo è relativo al lato precedente 
		 * o all'orizzontale per mezzo dell'handler di disegno CAD con punti
		 *
		 * Parameters:
		 * value - {Number} 1 = true (relativo), 0 = false (assoluto)
		 */
		this.setRelativeAngleOnPointByCAD = function(value){
			this.mapControlsDrawLayer['pointByCAD'].handler.setRelativeAngle(Number(value))
		};
		
		/*
		 * Method: delFirstLineOnPointByCAD
		 * Metodo privato che esegue la cancellazione del primo punto per mezzo dell'handler di disegno CAD con punti
		 */
		this.delFirstLineOnPointByCAD = function(){
			this.mapControlsDrawLayer['pointByCAD'].handler.clearFirstPoint();
		};
        
        /*
		 * Method: runCommandOnLineByCAD
		 * Metodo privato che esegue un comando CAD sul layer di disegno della linea
		 *
		 * Parameters:
		 * command - {String} comando CAD
		 */
        this.runCommandOnLineByCAD = function(command){
			this.mapControlsDrawLayer['lineByCAD'].handler.runCommand(command);
		};
		
		/*
		 * Method: setRelativeAngleOnLineByCAD
		 * Metodo privato che imposta se il valore di un angolo è relativo al lato precedente 
		 * o all'orizzontale per mezzo dell'handler di disegno CAD con linee
		 *
		 * Parameters:
		 * value - {Number} 1 = true (relativo), 0 = false (assoluto)
		 */
		this.setRelativeAngleOnLineByCAD = function(value){
			this.mapControlsDrawLayer['lineByCAD'].handler.setRelativeAngle(Number(value))
		};
		
		/*
		 * Method: delFirstLineOnLineByCAD
		 * Metodo privato che esegue la cancellazione del primo punto per mezzo dell'handler di disegno CAD con linee
		 */
		this.delFirstLineOnLineByCAD = function(){
			this.mapControlsDrawLayer['lineByCAD'].handler.clearFirstPoint();
		};
        
		/*
		 * Method: runCommandOnLineByCAD
		 * Metodo privato che esegue un comando CAD sul layer di disegno del poligono
		 *
		 * Parameters:
		 * command - {String} comando CAD
		 */
		this.runCommandOnPolygonByCAD = function(command){
			this.mapControlsDrawLayer['polygonByCAD'].handler.runCommand(command);
		};
		
		/*
		 * Method: setRelativeAngleOnLineByCAD
		 * Metodo privato che imposta se il valore di un angolo è relativo al lato precedente 
		 * o all'orizzontale per mezzo dell'handler di disegno CAD con poligoni
		 *
		 * Parameters:
		 * value - {Number} 1 = true (relativo), 0 = false (assoluto)
		 */
		this.setRelativeAngleOnPolygonByCAD = function(value){
			this.mapControlsDrawLayer['polygonByCAD'].handler.setRelativeAngle(Number(value))
		};
		
		/*
		 * Method: delFirstLineOnLineByCAD
		 * Metodo privato che esegue la cancellazione del primo punto per mezzo dell'handler di disegno CAD con poligoni
		 */
		this.delFirstLineOnPolygonByCAD = function(){
			this.mapControlsDrawLayer['polygonByCAD'].handler.clearFirstPoint();
		};
		
    },
    
    /**
     * Method: updateMapSize
     * Comunica alla mappa che e' necessario ricalcolare dimensioni e posizione.
     * 
     */
    updateMapSize: function() {
    	if ((this.map) && (this.isAlreadyDrawn==true)) {
    		this.map.updateSize();
    	} else {
    		var thisObj = this;
    		//setTimeout(function() { thisObj.updateMapSize();}, 500);
    	}
    },

    
    /**
     * Method: afterRender
     * Metodo privato invocato dopo che il pannello è stato renderizzato.
     * 
     */
    afterRender: function() {    	
    	this.callParent(arguments);
		
    	this.myMask=new Ext.LoadMask(this.id, {msg:ToloI18n.getMsg("ToloViewerOLPanel.afterRender.attesa")});
    	this.refreshBusy();
    	
    },    

    /**
     * Method: onDestroy
     * Metodo privato invocato durantela sequenza di distruzione. 
     */
    onDestroy: function() {
        if(this.ownerCt) {
            this.ownerCt.un("move", this.updateMapSize, this);
        }
        this.callParent(arguments);
    },
    
	
	// Funzioni che devono essere implementate in un plugin dedicato ad un certo viewer
	// Tutte le funzioni sono prefissate con "plugin"
	
    /**
     * Method: pluginPostInit
     * Funzione chiamata da Tolomeo dopo inizializzazione 
     * (come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer).
     * 
     */
	pluginPostInit: function() {
		
		this.fireEvent("onBeforePostInit");
		// se non è già stato disegnato (per esempio da uno zoomTo) allora disegna tutto
		if ((!this.isAlreadyDrawn) && (this.bOnOpenDrawMap==true)) {
			this.map.zoomToMaxExtent();
			this.isAlreadyDrawn=true;
		}
		
		this.fireEvent("onAfterPostInit");
	},
	
	/**
	 * Method: pluginPreInit
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginPreInit: function() {
		var me = this;
		
		this.fireEvent("onBeforePreInit");
	
		// Valorizzazione giusta directory per icone openlayer
		
		OpenLayers.ImgPath = this.TOLOMEOServer + this.TOLOMEOStaticRoot + "js/ext/openlayers/img/";
		
		var layerOptions = new Object();
		
		layerOptions.projection = this.paramsJS.mappe.SRID;
		layerOptions.units = this.paramsJS.mappe.units;
		
		//layerOptions.maxExtent = new OpenLayers.Bounds(1657419, 4852076, 1676400, 4869227);
		
		layerOptions.maxExtent = new OpenLayers.Bounds(this.paramsJS.mappe.maxExtentLeft, this.paramsJS.mappe.maxExtentBottom, this.paramsJS.mappe.maxExtentRight, this.paramsJS.mappe.maxExtentTop);
		layerOptions.minScale = this.paramsJS.mappe.minScale;   //"105000";
		layerOptions.maxScale = this.paramsJS.mappe.maxScale; //="200";

		// Il parametro fallThrough: true è necessario perchè gli eventi di mousemove vengano passati al resto degli oggetti del DOM
		// Se non viene impostato le finestre ExtJS sovrapposte alla mappa si ridimensionano con difficoltà
		var mapOptions = OpenLayers.Util.extend({fallThrough: true}, layerOptions);
		
		mapOptions.controls = [
			//new OpenLayers.Control.Navigation(),
	        //new OpenLayers.Control.PanZoom(), //PanZoom OpenLayers
	        new OpenLayers.Control.ArgParser()
	    //    new OpenLayers.Control.AttributionMulti() // Controllo esteso parte della distribuzione tolomeo
	        //new OpenLayers.Control.OverviewMap(), //Mappa di riferimento
	        //new OpenLayers.Control.ScaleLine()
	        //new OpenLayers.Control.Scale()                            
		];
		
		if(this.withScaleLine){
			var scaleLineOptions = {};
			if(this.scaleLineBox){
				scaleLineOptions.div = this.scaleLineBox.getEl().dom;
			}
			mapOptions.controls.push(new OpenLayers.Control.ScaleLine(scaleLineOptions));
		}
		
	    //commentato perchè se presente con ie6 errore in apertura
	    //, new OpenLayers.Control.MousePosition()
	    
		//mapOptions.controls[2].mapOptions = layerOptions;
		if(this.paramsJS.mappe.settableZoom!=null){
			mapOptions.fractionalZoom = this.paramsJS.mappe.settableZoom;
		}
		
		//OpenLayers.ProxyHost = this.TOLOMEOServer + this.TOLOMEOContext + "/TolomeoProxyServlet?url=";		
		OpenLayers.ProxyHost = this.paramsJS.comportamento.defaultProxyUrl  + "?url=";
		
		mapOptions.allOverlays = true;
		
		// Aggiunto perchè il tile manager dalla 2.13 deve essere specificato anche nel caso si voglia il default, cioè null.		
		mapOptions.tileManager = null;
		
		var zs = this.getDefaultZoomLevels();
		if (zs!=null) {
			mapOptions.resolutions = [];
			for(var i = 0; i < zs.length; i++) {
				mapOptions.resolutions.push(OpenLayers.Util.getResolutionFromScale(zs[i], layerOptions.units));
			}
		} 

		//ALE this.mapPanel.body.id,
		this.map = new OpenLayers.Map( mapOptions);
	
		//TODO Funziona solo con mappa 0
		var nmappa = 0;
		if (this.paramsJS.mappe.mappaList[nmappa].overview) {
		
			var server = this.paramsJS.getServer(this.paramsJS.mappe.mappaList[nmappa].overview.serverID, nmappa);
			
			var ovlayers = new OpenLayers.Layer.WMS( "overview",
	                server.url, 
	                {layers: this.paramsJS.mappe.mappaList[nmappa].overview.layer,
	                 wmscalluuid: (this.withWmsCallUUID) ? this.wmscalluuid : undefined});
		
			// maximized non è stato parametrizzato perchè se aperto all'avvio la mappa di overview viene bianca fino al primo spostamento
			// anche se metto in postinit dopo zoomToMaxExtent
	        var overview1 = new OpenLayers.Control.OverviewMap({
	            //maximized: this.paramsJS.mappe.mappaList[nmappa].overview.maximized,
	        	maximized: false,
	            maximizeTitle: ToloI18n.getMsg("ToloViewerOLPanel.pluginPreInit.overview.max"),
	            minimizeTitle: ToloI18n.getMsg("ToloViewerOLPanel.pluginPreInit.overview.min"),
	            layers: [ovlayers]
	        });
	        this.map.addControl(overview1);
		}
	
		this.map.events.register('mousemove', this, function(e, xy) {
																var lonlat = this.map.getLonLatFromViewPortPx(e.xy);
																this.fireEvent('onMouseCoordChange', new Point(lonlat.lon, lonlat.lat), this.map.units, this.map.getProjection());
																
																});
		
		// registro sull'evento movestart dell'oggetto mappa perchè se lo registrassi sul layer non riceverei l'evento quando il layer è spento
		// (come accade per esempio se non ci sono layer WMS accesi)
		this.map.events.register('movestart', this,  function(obj, element)   { this.olLayerScaleFilter(obj , element);});
		this.map.events.register('preaddlayer', this,  function(obj, element) { this.olLayerScaleFilter(obj , element);});
		
		var elencoLayer = new Array();
		
		this.pluginAddAllMaps();
		
		// Cerca nelle mappe in this.paramsJS.azioniEventi.eventiLayerList
		for (var i = 0; i<this.paramsJS.azioniEventi.eventiLayerList.length; i++) {
			var evLayer = this.paramsJS.azioniEventi.eventiLayerList[i];
			
			if (evLayer.caricaLayerSeparato) {
				var layer = this.olViewerNewLayer (evLayer.mappaLayerSeparato, i+100);
				elencoLayer.push(layer);	
				this.olRegisterBusyEvents(layer);
			}
		}

		this.drawLayer = new OpenLayers.Layer.Vector("DrawLayer");

		
		var styleSelected = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']);
	    styleSelected.strokeColor = "blue"; 
	    styleSelected.fillColor   = "blue";
	    styleSelected.strokeWidth = 2;
	    styleSelected.fillOpacity = 0.4;
	    styleSelected = OpenLayers.Util.extend(styleSelected,this.styleSelected || {});
	  
		var styleHighlighted = {
			strokeColor  : "#FFCC00",
	        strokeOpacity: 1,
	        strokeWidth  : 2,
	        pointRadius  : 6,
	        pointerEvents: "visiblePainted",
	        fillColor    : "yellow",
	        fillOpacity  : 0.4
		};
		styleHighlighted = OpenLayers.Util.extend(styleHighlighted,this.styleHighlighted || {});
	            
	    var styleAutoidentify = {
			strokeColor  : "#00AA00",
	        strokeOpacity: 1,
	        strokeWidth  : 2,
	        pointRadius  : 6,
	        pointerEvents: "visiblePainted",
	        fillColor    : "#00FF00",
	        fillOpacity  : 0.4
		};
		styleAutoidentify = OpenLayers.Util.extend(styleAutoidentify,this.styleAutoidentify || {});
		
		var styleSnap = {
			strokeColor  : "#00AA00",
			strokeOpacity: 1,
			strokeWidth  : 2,
			pointRadius  : 6,
			pointerEvents: "visiblePainted",
			//fillColor : "#00FF00",
			fillOpacity  : 0
		};
		styleSnap = OpenLayers.Util.extend(styleSnap,this.styleSnap || {});
		
		 var styleRouting = {
				 	strokeColor  : "#00FF00",
				    strokeOpacity: 1,
				    strokeWidth  : 2,
				    pointRadius  : 6,
				    pointerEvents: "visiblePainted",
				    fillColor    : "#00FF00",
				    fillOpacity  : 0.4
		};
		styleRouting = OpenLayers.Util.extend(styleRouting, this.styleRouting || {});
		
	    var styleMeasure = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']);
	    styleMeasure.strokeColor = "red"; 
	    styleMeasure.fillColor = "green"; 
	    styleMeasure = OpenLayers.Util.extend(styleMeasure,this.styleMeasure || {});
		
		layerOptions.style = styleSelected;
		//coorrezione bug OL2.10
		var a = layerOptions;
		// 14/03/2018 riscontrato problema se a,maxscale<=20, ma non sono disponibili indicazioni sul bug che a suo tempo si è voluto correggere con questo codice
		// Per risolvere rompendo meno possibile la compatibilità con il passato viene imposto il minimo a 1
		//a.maxScale -= 20;
		a.maxScale = Math.max(a.maxScale-20, 1); 
		
		this.selezioniLayer = new OpenLayers.Layer.Vector("Selezione", a );
	    
	    layerOptions.style = styleHighlighted;	    
	    this.evidenziazioniLayer = new OpenLayers.Layer.Vector("Evidenziazioni", layerOptions );
	    
	    layerOptions.style = styleAutoidentify;	    
	    autoIdentifiedLayer = new OpenLayers.Layer.Vector("AutoIdentify", layerOptions );	    
	    
	    layerOptions.style = undefined;  
	    layerOptions.styleMap= new OpenLayers.StyleMap({
		            "default": styleRouting,
		            "select": {
					 	strokeColor  : "red",
					    strokeOpacity: 1,
					    strokeWidth  : 2,
					    pointRadius  : 6,
					    pointerEvents: "visiblePainted",
					    fillColor    : "red",
					    fillOpacity  : 0.4
		            	}
		        });
	    
	    
	    this.routingLayer = new OpenLayers.Layer.Vector("Routing",  {
            styleMap:new OpenLayers.StyleMap({
                "default":new OpenLayers.Style(OpenLayers.Util.applyDefaults({                    
                    strokeColor:"#00FF00",                    
                    strokeWidth  : 2                    
                }, OpenLayers.Feature.Vector.style["default"])),
                "select":new OpenLayers.Style(OpenLayers.Util.applyDefaults({
                	strokeColor:"green",                    
                    strokeWidth  : 2
                }, OpenLayers.Feature.Vector.style["default"])),
                "temporary":new OpenLayers.Style(OpenLayers.Util.applyDefaults({
                	strokeColor:"red",                    
                    strokeWidth  : 2
                }, OpenLayers.Feature.Vector.style["highlight"]))

            })
        } );
	    
	    this.routingMarkersLayer = new OpenLayers.Layer.Vector("Routing markers", layerOptions);		        
	    
	    markersLayer = new OpenLayers.Layer.Markers( "Markers" );
	    
		elencoLayer.push(this.drawLayer);	  
		elencoLayer.push(this.routingLayer);
	    elencoLayer.push(this.routingMarkersLayer);	    
		elencoLayer.push(this.selezioniLayer); 
		elencoLayer.push(this.evidenziazioniLayer);
		elencoLayer.push(autoIdentifiedLayer);		
		
	    elencoLayer.push(markersLayer);
	    
		this.map.addLayers(elencoLayer);

		
	    var options = {handlerOptions: {freehand: false}};
	    
	    var optionsMeasure = {	    
	    	handlerOptions: {
	    		
	    		freehand: false, 
	    		sides: 40
	    	} 
	    };
		    
	    var autoIdentifyOptions = {
            'delay': 500,
            'pixelTolerance': 3,
            'stopMove': false
        };
        
        // Opzioni pointFromRef                                			    
	    var pointFromRefOptions = {callbacks : {
	    	'refPoint' : function(point){	    		
							me.fireEvent('onDrawFirstPointFromRef', point);																																	
					   },
		    'distance' : function(distance){													
							me.fireEvent('onDrawDistanceFromRefChange', distance);
					   }				   
	    }};
	    
	    // Opzioni constrained line 
	    var pointByCADOptions = {callbacks : {
	    	'firstPoint' : function(point){
							me.fireEvent('onDrawFirstPointByCAD', point);																												
					   },
		    'distance' : function(distance){													
							me.fireEvent('onDrawDistanceByCADChange', distance);
					   }				   
	    }, handlerOptions:{infoOnMouse: true}};
	    
	    // Opzioni constrained line 
	    var lineByCADOptions = {callbacks : {
	    	'firstPoint' : function(point){
							me.fireEvent('onDrawFirstPointByCAD', point);																												
					   },
		    'distance' : function(distance){													
							me.fireEvent('onDrawDistanceByCADChange', distance);
					   }				   
	    }, handlerOptions:{ring:false, infoOnMouse: true}};
	    
	    // Opzioni constrained polygon
	    var polygonByCADOptions = {callbacks : {
			'firstPoint' : function(point){
						       me.fireEvent('onDrawFirstPointByCAD', point);																														
						   },
		    'distance'   : function(distance){													
						       me.fireEvent('onDrawDistanceByCADChange', distance);
					       }
					       ,
			'aftermodify'     : function(point,sketch){	
						       var area = sketch.geometry.getArea();
						
							  //Arrotondamento a 2 decimali
							  area = Math.round(area*100)/100;										
							  me .fireEvent('onDrawAreaByCADChange', area);
							  return true;
					       }		   				   
	    }, handlerOptions:{infoOnMouse: true}};
	    
		var autoIdentify = new OpenLayers.Control();
		
		autoIdentify.handler = new OpenLayers.Handler.Hover(
			autoIdentify,
	        {
	        	'pause': function(e) { me.olAutoIdentifyOnPause(e); },
	        	'move' : function(e) { me.olAutoIdentifyOnMove(e); }
	        },
	        autoIdentifyOptions
	    )
	    
	    // OpenLayers.Renderer.symbol.crossNarrow = [8,0, 9,0, 9,8, 17,8, 17,9, 9,9, 9,17, 8,17, 8,9, 0,9, 0,8, 8,8, 8,0];
	    
	    var measurePointSymbolizer = {
            pointRadius: 6,
            graphicName: "cross",
            fillColor: "white",
            fillOpacity: 1,
            strokeWidth: .5,
            strokeOpacity: 1,
            strokeColor: "#333333"
        }
        
        var measureLineSymbolizer = {
            strokeWidth: 1.5,
            strokeOpacity: 1,
            strokeColor: "#666666",
            strokeDashstyle: "solid" //"longdashdot"
        }
        
        var measurePolygonSymbolizer =  {
            strokeWidth: 1.5,
            strokeOpacity: 1,
            strokeColor: "#666666",
            fillColor: "white",
            fillOpacity: 0.3
        }
        
        // Override by generic styleMeasure and by single symbolyzer
        measurePointSymbolizer   = OpenLayers.Util.extend(measurePointSymbolizer,this.styleMeasure || {});
	    measurePointSymbolizer   = OpenLayers.Util.extend(measurePointSymbolizer,this.measurePointSymbolizer || {});
	    measureLineSymbolizer    = OpenLayers.Util.extend(measureLineSymbolizer,this.styleMeasure || {});
	    measureLineSymbolizer    = OpenLayers.Util.extend(measureLineSymbolizer,this.measureLineSymbolizer || {});
	    measurePolygonSymbolizer = OpenLayers.Util.extend(measurePolygonSymbolizer,this.styleMeasure || {});
	    measurePolygonSymbolizer = OpenLayers.Util.extend(measurePolygonSymbolizer,this.measurePolygonSymbolizer || {});
	    
	    // style the sketch fancy
        var measureSymbolizers = {
            "Point"  : measurePointSymbolizer,
            "Line"   : measureLineSymbolizer,
            "Polygon": measurePolygonSymbolizer
        };
        
        var  style = new OpenLayers.Style();
        style.addRules([
            new OpenLayers.Rule({symbolizer: measureSymbolizers})
        ]);
        var styleMap = new OpenLayers.StyleMap({"default": style});
        
        // allow testing of specific renderers via "?renderer=Canvas", etc
        var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
        renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
        
        var measureControls = {
            line: new OpenLayers.Control.Measure(
                OpenLayers.Handler.Path, {
                    persist: true,
                    immediate: true,
                    handlerOptions: {
                        layerOptions: {
                            renderers: renderer,
                            styleMap: styleMap
                        }
                    },
                    geodesic: true
                }
            ),
            polygon: new OpenLayers.Control.Measure(
                OpenLayers.Handler.Polygon, {
                    persist: true,
                    immediate: true,
                    handlerOptions: {
                        layerOptions: {
                            renderers: renderer,
                            styleMap: styleMap
                        }
                    },
                    geodesic: true
                }
            ),
            circle: new OpenLayers.Control.Measure(
                OpenLayers.Handler.RegularPolygon, {
                    persist: true,
                    immediate: true,
                    handlerOptions: {
                        layerOptions: {
                            renderers: renderer,
                            styleMap: styleMap
                        },
                        sides: 40
                    },
                    geodesic: true
                }
            )
        };
        
        for(var key in measureControls) {                
            measureControls[key].events.on({
                "measure": function(e){e.control = this; me.olHandleMeasurements(e,false)},
                "measurepartial": function(e){e.control = this; me.olHandleMeasurements(e,true)}
            });                
        }
        
        // Patch per risolvere problema dovuto a modifica delle dimenzioni della finestra, apertura debugger etc.
        // Quando centro e risoluzione non cambiano non vengono registrate variazioni nello stack di navigazione
        // ====== INIZIO PATCH
       	this.navh = new OpenLayers.Control.NavigationHistory (
	        	{
	        		listeners : {
	        			moveend : OpenLayers.Function.bind(function() {	        				
			                if(!this.navh.restoring) {
			                	
			                    var state = this.navh.registry['moveend'].apply(this.navh, arguments);
			                    
			                    if(this.navh.previousStack.length > 0 &&
			                       this.navh.previousStack[0].resolution == state.resolution &&
			                       this.navh.previousStack[0].center.lon == state.center.lon &&
			                       this.navh.previousStack[0].center.lat == state.center.lat) 
			                    {
			                		// Non registro nello stack della navigazione
			                		return true;
			                	}
			                	
			                    this.navh.previousStack.unshift(state);
			                    if(this.navh.previousStack.length > 1) {
			                        this.navh.onPreviousChange(
			                            this.navh.previousStack[1], this.navh.previousStack.length - 1
			                        );
			                    }
			                    if(this.navh.previousStack.length > (this.navh.limit + 1)) {
			                        this.navh.previousStack.pop();
			                    }
			                    if(this.navh.nextStack.length > 0) {
			                        this.navh.nextStack = [];
			                        this.navh.onNextChange(null, 0);
			                    }
			                }
			                return true;		              
			            },this)
	        		}
	        	}
	        );
	    
		this.navh.previous.events.register('activate', this, function(){this.fireEvent('previousNavigationStateAvailable');});
		this.navh.previous.events.register('deactivate', this, function(){this.fireEvent('previousNavigationStateNotAvailable');});
		this.navh.next.events.register('activate', this, function(){this.fireEvent('nextNavigationStateAvailable');});
		this.navh.next.events.register('deactivate', this, function(){this.fireEvent('nextNavigationStateNotAvailable');});
		// ===========  FINE PATCH
		
	    //{zoomBoxEnabled: true}
	    this.mapControls = {
	        navigation   : new OpenLayers.Control.Navigation(),
	        // N.B. Se alwaysZoom:true si verifica un bug che porta allo zoom non corretto (spostato e troppo ravvicinato) 
	        // solo quando si attiva lo strumento zoombox e non quando si fa shift-tasto muose
	        zoombox       : new OpenLayers.Control.ZoomBox({alwaysZoom:false, zoomOnClick: false}), 
	        vertexediting : new OpenLayers.Control.ModifyFeature(this.selezioniLayer, {standalone:true}),
	        dragdrop	  : new OpenLayers.Control.DragFeature(this.selezioniLayer),	        
	        measureLine   : measureControls['line'],
	        measurePolygon: measureControls['polygon'],                
	        measureCircle : measureControls['circle'],               	        
	        autoIdentify  : autoIdentify,
	        attribution   : new OpenLayers.Control.AttributionMulti(),
	        navhistory    : this.navh
		};
				 	    
	    // Se presenti più di una mappa inserisco anche il layerswitcher
	    if (this.paramsJS.mappe.mappaList.length>1) {
	    	this.mapControls.legend = new OpenLayers.Control.LayerSwitcher({activeColor: "#004000"});
	    }
	        
	    this.mapControls['dragdrop'].onComplete  = function (geom) { me.olViewerOnDragDropEnd(geom); }
	    
	    this.mapControlsDrawLayer = {
	    	point       : new OpenLayers.Control.DrawFeature(this.drawLayer, OpenLayers.Handler.Point),
	        pointFromRef: new OpenLayers.Control.DrawFeature(this.drawLayer, OpenLayers.Handler.PointFromRef,pointFromRefOptions),
	        pointByCAD  : new OpenLayers.Control.DrawFeature(this.drawLayer, OpenLayers.Handler.ConstrainedPoint,pointByCADOptions),
	        line        : new OpenLayers.Control.DrawFeature(this.drawLayer, OpenLayers.Handler.Path, options),
	        lineByCAD   : new OpenLayers.Control.DrawFeature(this.drawLayer, OpenLayers.Handler.ConstrainedPath,lineByCADOptions),
	        polygon     : new OpenLayers.Control.DrawFeature(this.drawLayer, OpenLayers.Handler.Polygon, options),
	        polygonByCAD: new OpenLayers.Control.DrawFeature(this.drawLayer, OpenLayers.Handler.ConstrainedPolygon,polygonByCADOptions),
	        modify      : new OpenLayers.Control.ModifyFeature(this.drawLayer),
	        dragdrop    : new OpenLayers.Control.DragFeature(this.drawLayer)   // , {mode: OpenLayers.Control.ModifyFeature.RESIZE | OpenLayers.Control.ModifyFeature.RESHAPE | OpenLayers.Control.ModifyFeature.DRAG }
	    };
	    
	    this.mapControlsDrawLayer['point'].events.register('featureadded', this, this.olViewerOnDigitizedPoint);
	    this.mapControlsDrawLayer['line'].events.register('featureadded', this, this.olViewerOnDigitizedLine);
	    this.mapControlsDrawLayer['polygon'].events.register('featureadded', this, this.olViewerOnDigitizedPolygon);
	    this.mapControlsDrawLayer['pointFromRef'].events.register('featureadded', this, this.olViewerOnDigitizedPointFromRef);
	    this.mapControlsDrawLayer['pointByCAD'].events.register('featureadded', this, this.olViewerOnDigitizedPointByCAD);
    	this.mapControlsDrawLayer['lineByCAD'].events.register('featureadded', this, this.olViewerOnDigitizedLineByCAD);
    	this.mapControlsDrawLayer['polygonByCAD'].events.register('featureadded', this, this.olViewerOnDigitizedPolygonByCAD);
	    
	    this.drawLayer.events.register('afterfeaturemodified', this, this.olViewerOnDigitizedFeatureVertexEditingEnd);
	    this.mapControlsDrawLayer['dragdrop'].onComplete  = function (geom) { me.olViewerOnDigitizedFeatureDragDropEnd(geom); }
	    
	    this.routingControlManager = Ext.create('TolomeoExt.ToloViewerOLPanel.SelectFeatureControlManager',
			{
				map: this.map,
				viewer: this	
									
			});	    
	    
	    var routingDragdropCtrl = new OpenLayers.Control.DragFeature(this.routingMarkersLayer);		   
	    routingDragdropCtrl.onComplete  = function (feature, pixel) { me.olViewerOnDragDropEndRouting(feature); };
	     	   	   
	    var routingDelegatorWidget = Ext.create('TolomeoExt.ToloViewerOLPanel.SelectFeatureControlManager.DelegatorWidget',{
	    	id : 'routing',
	    	layers : [this.routingLayer,this.routingMarkersLayer],
	    	callbacks : {
	    		onBeforeSelect : function(layerName, feature, viewer){return false;}, // per impedire la visualizazione della selezione		    		
		    	onHighlight : function(layerName,feature,viewer){viewer.fireEvent('routingInformationSelect', feature.attributes.instructionId);},
		    	onUnhighlight : function(layerName,feature,viewer){viewer.fireEvent('routingInformationUnSelect', feature.attributes.instructionId);}
	    	},
	    	moreControls : [routingDragdropCtrl]	    	
	    });
	    	    
	    this.routingControlManager.addDelegator(routingDelegatorWidget);	    

	    for(var key in this.mapControls) {
	        this.map.addControl(this.mapControls[key]);
	    }
	    
	    // @PATCH per evitare che tenendo premuto SHIFT faccia lo zoom 
	    // Sarebbe utile che Openlayers mettesse a disposizione un property zoomBoxOptions per il Navigation control, così come c'è la property
	    // pinchZoomOptions, invece di mettere a disposizione solo la proprietà zoomBoxKeyMask per impostare il codice del tasto con cui fare lo zoombox.
	    this.mapControls['navigation'].zoomBox.zoomOnClick = false;
	    
	    for(var key in this.mapControlsDrawLayer) {
	        this.map.addControl(this.mapControlsDrawLayer[key]);
	    }
	    	    
		var thisVar = this;
		
		this.map.events.register("zoomend", this, function(){ 
			this.fireEvent('scalechange', thisVar.pluginGetCurrentZoom());
		});
		
		this.map.events.register("preaddlayer", this, function(e){
			if(this.map.getLayer(e.layer.id)) return false;			
		});	
			
		
		this.olViewerToolSelectSelectEventHandler = function(e,visualize){				
			var lonlat = this.map.getLonLatFromViewPortPx(e.xy);
			var selMode = null;
			if (e.shiftKey) {
				selMode = (this.paramsJS.selectDefaultMode=='FIRSTONTOP') ?  'allStacked':'firstOnTop';
			} else {
				selMode = (this.paramsJS.selectDefaultMode=='FIRSTONTOP') ?  'firstOnTop' : 'allStacked';
			}
			
			this.fireEvent('onMappaSelect', new Point(lonlat.lon, lonlat.lat),selMode,e.ctrlKey,e.altKey, e.xy.x, e.xy.y);
						//OpenLayers.Event.stop(e);				
		}
		////////////////////////////////////////
		// Impostazione dei layer di snapping //
		////////////////////////////////////////
		this.snapControls = [];
		var snappedMapLayers = [this.selezioniLayer,this.drawLayer];
		
		for(var evtLi = 0; evtLi < this.paramsJS.azioniEventi.eventiLayerList.length; evtLi++){
			
			var evtL = this.paramsJS.azioniEventi.eventiLayerList[evtLi];
			
			if(evtL.snapping && evtL.snapping.snappingLayerList && evtL.snapping.snappingLayerList.length > 0){
								
				var targetList = [];
				var wfsLayerList = [];
				
				for(var sLi = 0; sLi < evtL.snapping.snappingLayerList.length; sLi++){
					
					var snappingLayer = evtL.snapping.snappingLayerList[sLi];
					
					var wfsLayer = new OpenLayers.Layer.Vector(snappingLayer.name,
					{
						style: styleSnap,
						minScale : snappingLayer.minScale,
						maxScale : snappingLayer.maxScale,
						strategies: [new OpenLayers.Strategy.BBOX()],
						projection: new OpenLayers.Projection(snappingLayer.srsName),
						protocol: new OpenLayers.Protocol.WFS({
							url: snappingLayer.url  + (snappingLayer.role ? ((snappingLayer.url.indexOf("?")==-1 ? "?" : "&" ) + "role="+snappingLayer.role) :""),		
							featureType: snappingLayer.featureType.split(","),		
							srsName: snappingLayer.srsName,
							version: "1.1.0",		
							featureNS: snappingLayer.featureNS ? snappingLayer.featureNS : null,
							featurePrefix: snappingLayer.featurePrefix ? snappingLayer.featurePrefix : null,
							propertyNames: snappingLayer.propertyNames ? snappingLayer.propertyNames.split(",") : null,
							geometryName: snappingLayer.geometryName,
							outputFormat: "JSON"
						}),
						visibility : false,
						isBaseLayer : false,
						eventListeners : {
							'loadend': function(e){
								if(e.object.features.length == 0){
									Ext.Msg.alert(ToloI18n.getMsg("ToloViewerOLPanel.pluginPreInit.caricSnap.msg"),ToloI18n.getMsg("ToloViewerOLPanel.pluginPreInit.caricSnap.msg"));
								}
							},
							scope: this
						}
					});
					
					/*
					if(snappingLayer.role){
						var newParam = {role: snappingLayer.role};
						wfsLayer.mergeNewParams(newParam);
					}
					*/
					
					wfsLayerList.push(wfsLayer);
					
					targetList.push({
						layer: wfsLayer,
						tolerance: snappingLayer.tolerance,
						edge: snappingLayer.edge
						//filter: new OpenLayers.Filter.Comparison({
				        //    type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
				        //    property: "surface",
				        //    value: "dirt"
				        //})
					});
					
				}
				
				this.map.addLayers(wfsLayerList);
				var layerMapSnapControls = [];
				
				for (var sml=0; sml < snappedMapLayers.length; sml++){
					var snapControl = new OpenLayers.Control.Snapping({					
						layer: snappedMapLayers[sml],					
						targets: targetList,
						greedy: false,
						eventListeners: {
							// fa in modo di non snappare se il layer non è visibile o si è fuori dal range di scale previsto
							"beforesnap" : function(evt){		
								return evt.layer.getVisibility() && evt.layer.calculateInRange();	
							},
							scope: this
						}
					});
					
					snapControl.layerList = wfsLayerList;
					layerMapSnapControls.push(snapControl);
					
					this.map.addControl(snapControl);
				}
				/*
				// fa in modo di non snappare se il layer non è visibile o si è fuori dal range di scale previsto
				snapControl.events.register("beforesnap", this, function(evt){		
					return evt.layer.getVisibility() && evt.layer.calculateInRange();	
				});
				*/
				
				this.snapControls[evtL.codTPN] = layerMapSnapControls;				
				
				// snapControl.activate();
			}	
		}	   
								
	   this.isAlreadyDrawn = false;
	   this.fireEvent("onAfterPreInit");
	},

	pluginShowTimeMachine: function(show) {
		if (show) {
			// TODO  generalizzare adesso considera solo mappa[0] gestisce solo prima mappa
			if (this.timeMachinePanel==null && (this.paramsJS.mappe.mappaList[0].timeMachineList!=null && this.paramsJS.mappe.mappaList[0].timeMachineList.length>0)) {
		        var pnl = null;
				if (this.paramsJS.mappe.mappaList[0].timeMachineList.length==1) {
					pnl = new TolomeoExt.ToloTimeMachinePanel({
						viewer: this,
						paramsJS: this.paramsJS,
						timeMachineToShow: 0,
						cls: 'clsTimeMachinePanel'
					});
					
				} else {
					pnl = new TolomeoExt.ToloTimeMachineMultiPanel({
						viewer: this,
						paramsJS: this.paramsJS,
						timeMachineToShow: 0,
						cls: 'clsTimeMachinePanel'
					});
				}
			
	        	this.timeMachinePanel = new Ext.Window({
	        		//title: "",  
					layout: 'fit',
					maximizable: true,
					constrain: true,
					collapsible: true,
					border: false,
					shadow: false,
					height: 270,
					width: 370,
					autoScroll: true,
					//constrainTo: this.body,
					//renderTo: this.body,
					liveDrag: true,
					items: [pnl],
					cls: 'clsTimeMachineWindow'
				}).show();
	    
	        		
	        	//this.add(this.timeMachinePanel);
	        	this.timeMachinePanel.show(this);
	        	this.timeMachinePanel.on('close', function() {this.timeMachinePanel = null;this.fireEvent("onTimeMachineHide");}, this);
	        	
	        	if (this.paramsJS.mappe.mappaList[0].timeMachineList.length==1) {
	        		pnl.calculateAutoOffset();
	        		pnl.reloadItems();
	        	}
	        	//pnl.a.calculateAutoOffset();
	        	//pnl.a.reloadItems();
	        	
	        	//pnl.calculateAutoOffset();
	        	//pnl.reloadItems();
	        	
	        	this.fireEvent("onTimeMachineShow");
		   	} 
		}  else {
		   if (this.timeMachinePanel)  {
			   
			   this.timeMachinePanel.close();
			   this.remove(this.timeMachinePanel);
			   this.fireEvent("onTimeMachineHide");
			   //this.timeMachinePanel = null;
			// this.timeMachinePanel.hide(this);
		   }
	    }
	},
	
	/**
	 * Method: pluginMeasureClear
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginMeasureClear: function() {
		this.fireEvent('onMeasureClear');
	},
	
	/**
	 * Method: pluginMeasureStop
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginMeasureStop: function() {
		this.pluginMeasureClear();
		this.deactivateControl('measurePolygon');
		this.deactivateControl('measureCircle');
		this.deactivateControl('measureLine');
		this.fireEvent('onMeasureStop');
	},

	/**
	 * Method: pluginSetLegendWidth
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginSetLegendWidth : function () {},

	pluginStartDigitizedFeatureModify: function(mode) {
		
		switch (mode) {
			case 0: // niente da fare
				break;
			case 1: this.activateControlDrawLayer('modify');
				break;
			case 2: this.activateControlDrawLayer('dragdrop');
				break;	
		}

	},
	
	pluginStopDigitizedFeatureModify: function(mode) {

		switch (mode) {
			case 0: // niente da fare
				break;
			case 1: this.deactivateControlDrawLayer('modify');
				break;
			case 2: this.deactivateControlDrawLayer('dragdrop');
				break;	
		}
		
	},
	
	/**
	 * Method: pluginStartDigitizePoint
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartDigitizePoint: function () {
		this.activateControlDrawLayer('point');
	},
	
	/**
	 * Method: pluginStopDigitizePoint
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopDigitizePoint: function () {
		this.deactivateControlDrawLayer('point');				
	},
	
	/**
	 * Method: pluginStartDigitizePointFromRef
	 * Funzione che inizia la digitalizzazione (disegno) di un punto con la modalità CAD
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartDigitizePointFromRef: function () {
		this.activateControlDrawLayer('pointFromRef');
	},
	
	/**
	 * Method: pluginStopDigitizePointFromRef
	 * Funzione che interrompe la digitalizzazione (disegno) di un punto con la modalità CAD
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopDigitizePointFromRef: function () {		
		this.deactivateControlDrawLayer('pointFromRef');
		this.fireEvent('onDigitizePointFromRefEnd');		
	},
	
	/**
	 * Method: pluginStartDigitizeLineByCAD
	 * Funzione che inizia la digitalizzazione (disegno) di una linea con la modalità CAD
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartDigitizePointByCAD: function () {
		this.cadPanel.on('pressSetCommand',this.runCommandOnPointByCAD, this)
		this.cadPanel.on('changeAngleSetting',this.setRelativeAngleOnPointByCAD, this);
		this.cadPanel.on('pressDeleteFirstLine',this.delFirstLineOnPointByCAD, this);
		this.activateControlDrawLayer('pointByCAD');
	},
			
	/**
	 * Method: pluginStartDigitizeLineByCAD
	 * Funzione che interrompe la digitalizzazione (disegno) di una linea con la modalità CAD
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopDigitizePointByCAD: function () {
		this.cadPanel.un('pressSetCommand',this.runCommandOnPointByCAD, this)
		this.cadPanel.un('changeAngleSetting',this.setRelativeAngleOnPointByCAD, this);
		this.cadPanel.un('pressDeleteFirstLine',this.delFirstLineOnPointByCAD, this);
		this.deactivateControlDrawLayer('pointByCAD');
		this.fireEvent('onDigitizePointByCADEnd');
		
	},		
	
	/**
	 * Method: pluginStartDigitizeLineByCAD
	 * Funzione che inizia la digitalizzazione (disegno) di una linea con la modalità CAD
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartDigitizeLineByCAD: function () {
		this.cadPanel.on('pressSetCommand',this.runCommandOnLineByCAD, this)
		this.cadPanel.on('changeAngleSetting',this.setRelativeAngleOnLineByCAD, this);
		this.cadPanel.on('pressDeleteFirstLine',this.delFirstLineOnLineByCAD, this);
		this.activateControlDrawLayer('lineByCAD');
	},
			
	/**
	 * Method: pluginStartDigitizeLineByCAD
	 * Funzione che interrompe la digitalizzazione (disegno) di una linea con la modalità CAD
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopDigitizeLineByCAD: function () {
		this.cadPanel.un('pressSetCommand',this.runCommandOnLineByCAD, this)
		this.cadPanel.un('changeAngleSetting',this.setRelativeAngleOnLineByCAD, this);
		this.cadPanel.un('pressDeleteFirstLine',this.delFirstLineOnLineByCAD, this);
		this.deactivateControlDrawLayer('lineByCAD');
		this.fireEvent('onDigitizeLineByCADEnd');
		
	},		
	
	/**
	 * Method: pluginStartDigitizePolygonByCAD
	 * Funzione che inizia la digitalizzazione (disegno) di un poligono con la modalità CAD
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartDigitizePolygonByCAD: function () {
		this.cadPanel.on('pressSetCommand',this.runCommandOnPolygonByCAD, this)
		this.cadPanel.on('changeAngleSetting',this.setRelativeAngleOnPolygonByCAD, this);
		this.cadPanel.on('pressDeleteFirstLine',this.delFirstLineOnPolygonByCAD, this);
		this.activateControlDrawLayer('polygonByCAD');
	},
	
	/**
	 * Method: pluginStartDigitizePolygonByCAD
	 * Funzione che interrompe la digitalizzazione (disegno) di un poligono con la modalità CAD
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopDigitizePolygonByCAD: function () {
		this.cadPanel.un('pressSetCommand',this.runCommandOnPolygonByCAD, this)
		this.cadPanel.un('changeAngleSetting',this.setRelativeAngleOnPolygonByCAD, this);
		this.cadPanel.un('pressDeleteFirstLine',this.delFirstLineOnPolygonByCAD, this);
		this.deactivateControlDrawLayer('polygonByCAD');
		this.fireEvent('onDigitizePolygonByCADEnd');		
	},		
		
	/**
	 * Method: pluginStartDigitizeLine
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartDigitizeLine: function () {
		this.activateControlDrawLayer('line');
	},
	
	/**
	 * Method: pluginStopDigitizeLine
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopDigitizeLine: function () {
		this.deactivateControlDrawLayer('line');
	},

	/**
	 * Method: pluginStartDigitizePolygon
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartDigitizePolygon: function () {				
		this.activateControlDrawLayer('polygon');
	},
	
	/**
	 * Method: pluginStopDigitizePolygon
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopDigitizePolygon: function () {
		this.deactivateControlDrawLayer('polygon');
	},

	/**
	 * Method: pluginStartDigitizeCircle
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartDigitizeCircle: function () {},

	/**
	 * Method: pluginStopDigitizeCircle
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopDigitizeCircle: function () {},
	
	/**
	 * Method: pluginStartVertexEditing
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartVertexEditing: function (geomType, codTPN) {			
		this.selezioniLayer.events.register('afterfeaturemodified', this, this.olViewerOnVertexEditingEnd);
				
		this.activateControl('vertexediting');

		//this.mapControls['vertexediting'].selectControl.select(this.selezioniLayer.features[0]);
		var currentCodTPN = codTPN;
		for(var i=0; i < this.selezioniLayer.features.length; i++){
			if(this.selezioniLayer.features[i].codTPN == currentCodTPN){				
				//this.mapControls['vertexediting'].selectControl.select(this.selezioniLayer.features[i]);
				this.mapControls['vertexediting'].selectFeature(this.selezioniLayer.features[i]);
				break;
			}
		}
	},

	/**
	 * Method: pluginStopVertexEditing
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopVertexEditing: function () {
		this.selezioniLayer.events.unregister('afterfeaturemodified', this, this.olViewerOnVertexEditingEnd);
		this.deactivateControl('vertexediting');
	},

	/**
	 * Method: pluginStartDragDrop
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStartDragDrop: function (geomType) {			
		this.firstSelectionPermitted = false;		
		this.activateControl('dragdrop');		
	},
	
	/*
	 * Method: pluginStopDragDrop
	 * Funzione che inizia la digitalizzazione (disegno)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginStopDragDrop: function (geomType) {
		this.deactivateControl('dragdrop');
	},

	/**
	 * Method: pluginDigitizeLayerClear
	 * Funzione che cancella gli oggetti presenti sul layer utilizzato per la digitalizzazione
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginDigitizeLayerClear: function (){
		this.drawLayer.destroyFeatures();
	},

	/**
	 * Method: pluginToolSelectZoomIn
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectZoomIn: function() {
		this.map.zoomIn();
		//if(this.map.getScale()<this.paramsJS.mappe.maxScale){this.map.zoomToScale(this.paramsJS.mappe.maxScale);}
	},

	pluginToolSelectZoomOut: function()  {this.map.zoomOut();},
	
	/**
	 * Method: pluginToolSelectZoomPrev
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le pluginToolSelectZoomOut: function() {this.map.zoomOut();}, plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectZoomPrev: function() {},
	
	/**
	 * Method: pluginToolSelectZoomAll
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectZoomAll: function() {this.map.zoomToMaxExtent();},
	
	/**
	 * Method: pluginToolSelectZoomBox
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectZoomBoxActivate: function (activate) {
											this.activateControl('zoombox');
											},
	pluginToolSelectZoomBoxDeactivate: function (activate) {this.deactivateControl('zoombox')},
	
	/**
	 * Method: pluginToolSelectPan
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectPan: function () {this.activateControl('navigation');},
	
	/**
	 * Method: pluginToolSelectPanStop
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectPanStop: function () {/*this.deactivateControl('navigation');*/},
	
	/**
	 * Method: pluginToolSelectPrint
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectPrint: function () 	{
		var urlArr = [];
		for (var i=0; i<this.map.layers.length; i++) {
			var currLayer = this.map.layers[i];
			if (currLayer.getVisibility() &&
					((currLayer.CLASS_NAME.indexOf("MapServer")!=-1) || (currLayer.CLASS_NAME.indexOf("WMS")!=-1))) {
					var typeCode = (currLayer.CLASS_NAME.indexOf("MapServer")!=-1) ? 0 : 11;
					urlArr.push({url: currLayer.getURL(currLayer.getExtent()), nPluginLayer: i, typeCode: typeCode });	
			}
			
		}
		//this.map.layers[0].getURL(this.map.layers[0].getExtent())
		this.fireEvent('onPrintMap', urlArr);
			
	},
		
	/**
	 * Method: pluginToolSelectClipboardCopy
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectClipboardCopy: function () 	{},
		
	/**
	 * Method: pluginToolSelectSelect
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectSelect: function () 	{
		//modificato da click a mouseup perch? click non funzionava correttamente in ie
		//da fare meglio e verificare su ie che ha comportamento diverso da firefox
				
		// this.map.events.register("click", this, this.olViewerToolSelectSelectEventHandler);						
		
		if(!this.selectHandler){
			var me = this;
			// usato per evitare l'evento click sul pan della geometria
			me.firstSelectionPermitted = true;
			this.selectHandler = new OpenLayers.Handler.Click(this.mapControls['navigation'], 
				{'click' : function(e){
						if(me.firstSelectionPermitted){
							me.olViewerToolSelectSelectEventHandler.call(me,e);
						} else {
							me.firstSelectionPermitted = true;
						}
					}
				}/*, {stopSingle : true}*/ );
		}
		
		this.selectHandler.activate();
			 
	},
	
	
	
	/**
	 * Method: pluginToolSelectSelectStop
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectSelectStop: function () 	{
		//modificato da click a mouseup perch? click non funzionava correttamente in ie
		//da fare meglio e verificare su ie che ha comportamento diverso da firefox
		/*
		this.map.events.unregister("click", this, this.olViewerToolSelectSelectEventHandler);		
		this.selectKeymapIn.disable();
		this.selectKeymapOut.disable();
		*/
		if(this.selectHandler){
			this.selectHandler.deactivate();
		}
	},
	
	/**
	 * Method: pluginMeasureToolSelect
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginMeasureToolSelect: function (tipo) 	{ 
		var controlName = "";
		switch (tipo) {
			case 0: controlName = 'measurePolygon'; 
				break;
			case 1: controlName = 'measureCircle'; 
				break;
			case 2: controlName = 'measureLine'; 
				break;	
		}
		
		this.activateControl(controlName);
		this.fireEvent('onMeasureStart', tipo);
	},
	
	/**
	 * Method: pluginToolSelectBuffer
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectBuffer: function () {},
	
	/**
	 * Method: pluginToolSelectInfoMappa
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectInfoMappa: function () {},
	
	/**
	 * Method: pluginToolSelectLegendaToggle
	 * Funzione per la gestione "tool" (zoom, pan etc)
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginToolSelectLegendaToggle: function (larghezza) {},

	/**
	 * Method: pluginGotoPosition
	 * Funzione di posizionamento della mappa
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 */
	pluginGotoPosition: function (coordX, coordY, zoomFactor, withMarker, iconPath) {  
		var res = OpenLayers.Util.getResolutionFromScale(zoomFactor, this.map.baseLayer.units);
		var lonlat = new OpenLayers.LonLat(coordX,coordY);
		this.map.setCenter(lonlat, this.map.getZoomForResolution(res), false, true);
		this.isAlreadyDrawn=true;
		if (withMarker) this.pluginAddMarker(coordX, coordY,iconPath,true);
	},

	/**
	 * Method: pluginGetSelectedKeys
	 * Funzione di selezione oggetti.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicatoad un certo viewer
	 *
	 * Parameters:
	 * layerName - {String} layerName.
	 *
	 * Returns:
	 *    {Array} - ritorna un Array contenente le chiavi degli oggetti correntemente selezionati ed appartenenti a layerName.
	 *    N.B. Non è stata fatta una funzione che ritorna tutti gli oggetti 
	 *    perchè un oggetto potrebbe essere in un gruppo che ha il nome del layer ma che viene tematizzato più volte con nomi diversi
	 */
	pluginGetSelectedKeys: function  (layerName) {
			var retVal = new Array();
			//TODO
			return retVal;
	},

	/**
	 * Ritorna la url dalla quale è ricavata l'immagine relativa al layer attualmente visualizzato utilizzando il bound passato
	 * 
	 * @param bounds - bounds che identificano la zona da mostrare nella forma  {left: bounds.left, bottom: bounds.bottom, right: bounds.right, top: bounds.top} 
	 * 
	 */
	pluginGetMapUrl: function(bounds) {
		
		var bounds = new OpenLayers.Bounds(bounds.left, bounds.bottom, bounds.right, bounds.top);
		
		return this.map.baseLayer.getURL(bounds);
		
	},
	
	/**
	 * Method: pluginRefreshMap
	 * Funzione per ridisegnare la mappa.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *	
	 * Parameters:
	 * layers 		- nomi layer separati dal separatore
	 * stili  		- nomi stili separati dal separatore
	 * separatore  	- separatore (default ' ')
	 * nLayer	    - numero del layer sul quale fare il refresh
	 */
	pluginRefreshMap: function (layers, stili, separatore, nLayer) {
		
		var sep = separatore ? separatore : ' '; 
		
		if (arguments.length==0) {
			for(var index=0; index<this.map.layers.length; index++) {
				var currLayer = this.map.layers[index];
				if (currLayer.getVisibility()==true) {
					if (currLayer.CLASS_NAME.indexOf("MapServer")!=-1 ||
						currLayer.CLASS_NAME.indexOf("WMS")!=-1	) {
							currLayer.mergeNewParams({random: Math.random()});
					} else {
						if (currLayer.redraw) currLayer.redraw();
					}
				}
			}
		} else {
			for(var index=0; index<this.map.layers.length; index++) {
				if ((nLayer==undefined || nLayer==null) ||
					(nLayer!=undefined && nLayer!=null && nLayer==index)) {
					var currLayer = this.map.layers[index];
					
					if (currLayer.CLASS_NAME.indexOf("MapServer")!=-1) {
						// in caso di layer mapserver 
						// fatto cosÌ su consiglio di openlayers dopo avere aperto bug 
						// perchè la cache del browser non ricaricava e non sentiva la differenza.
						// Andrebbe fatto leggermente più intelligente per ricaricare solo i layer cambiati
						
						
						if (layers != undefined && layers!=null) {
							if (layers=="") {
								currLayer.setVisibility(false);
							} else {
								var opt = new Object();
								opt.random= Math.random();
							
								var buff = layers;
								if (sep!=' ') {
									// sostituisce il separatore con quello giusto per Mapserver
									var pattern = new RegExp(sep,'g');
									buff = buff.replace(pattern, ' ');
								}
								opt.layers=buff;
								currLayer.mergeNewParams(opt);
								currLayer.setVisibility(true);
							}
						}
					} else {
						if (currLayer.CLASS_NAME.indexOf("WMS")!=-1) {
							
							if (layers != undefined && layers!=null) {
								if (layers=="") {
									currLayer.setVisibility(false);
								} else {
									var opt = new Object();
									opt.random= Math.random();
									var buff = layers;
									if (sep!=',') {
										// sostituisce il separatore con quello giusto per WMS
										var pattern = new RegExp(sep,'g');
										buff = buff.replace(pattern, ',');
									} 
									opt.layers=buff;
									opt.styles='';
									if (stili) {
										var buff = stili;
										if (sep!=',') {
											// sostituisce il separatore con quello giusto per WMS
											var pattern = new RegExp(sep,'g');
											buff = buff.replace(pattern, ',');
										}
										opt.styles = buff
									}
									currLayer.mergeNewParams(opt);
									currLayer.setVisibility(true);
								}
							}
						} else {
						if (currLayer.redraw) currLayer.redraw();
						}
					}
				}	
			}	
		}
		
		this.pluginRefreshAttribution();
	},
	
	/**
	 * Method: pluginRefreshAttribution
	 * Funzione per aggiornare le attribuzioni di un layer.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *	
	 * Parameters:
	 * Attribution - {Object|Object[]}  Oggetto o array contenente le attribuzioni ognuna delle quali caratterizzata con in campi title, logoUrl, OnlineResource
	 */
	pluginRefreshAttribution: function(attribution, nPluginLayer) {
		if (attribution) {
			this.map.layers[nPluginLayer].attribution = attribution;
		}
		this.mapControls.attribution.updateAttribution();
		
	},
	
	//function pluginRefreshMapAndGoto(coordX, coordY, zoomFactor) {};
	
	/**
	 * Method: pluginRefreshLayer
	 * Funzione per ridisegnare il layer.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *	
	 * Parameters:
	 * layerName - {String} layerName
	 */
	pluginRefreshLayer: function (layerName) {
		// implementato con un refresh totale
	 	this.pluginRefreshMap() ;
	},

	/**
	 * Method: pluginGetMapExtent
	 * Ritorna l'extent del layer di base attualmente attivo
	 *
	 * Returns:
	 *    	Ritorna l'estensione totale della mappa  in un oggetto {left: bounds.left, bottom: bounds.bottom, right: bounds.right, top: bounds.top} 
	 */  
	pluginGetMapExtent: function () {
		bounds = this.map.baseLayer.getExtent();
		
		return {left: bounds.left, bottom: bounds.bottom, right: bounds.right, top: bounds.top};
		
	},
	
	/**
	 * Method: pluginGetMapFullExtent
	 * Ritorna l'extent del layer di base attualmente attivo compreso l'eventuale aggiunta ai bordi dovuta a ratio
	 *
	 * Returns:
	 *    	Ritorna l'estensione totale della mappa  in un oggetto {left: bounds.left, bottom: bounds.bottom, right: bounds.right, top: bounds.top}
	 */  
	pluginGetMapFullExtent: function(){
		
		var bounds = this.pluginGetMapExtent();
		var ratio = this.pluginGetMapRatio();
		
		  //determine new tile bounds
	    var center = { lon: bounds.left + (bounds.right - bounds.left) / 2,
	    			   lat: bounds.bottom + (bounds.top - bounds.bottom) / 2};
	    
	    var boundsWidth = (bounds.right - bounds.left) * ratio;
	    var boundsHeight = (bounds.top - bounds.bottom) * ratio;
	        
	    var fullBounds = 
	        new OpenLayers.Bounds(center.lon - (boundsWidth/2),
	                              center.lat - (boundsHeight/2),
	                              center.lon + (boundsWidth/2),
	                              center.lat + (boundsHeight/2));
	    
		return {left: fullBounds.left, bottom: fullBounds.bottom, right: fullBounds.right, top: fullBounds.top};
	},

	/**
	 * Method: pluginGetCurrentX
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Returns:
	 *    	Ritorna il valore corrente del centro della mappa visualizzata.
	 */ 
	pluginGetCurrentX: function () { return this.map.getCenter().lon; },
	
	/**
	 * Method: pluginGetCurrentY
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Returns:
	 *    	Ritorna il valore corrente del centro della mappa visualizzata.
	 */ 
	pluginGetCurrentY: function () { return this.map.getCenter().lat; },

	/**
	 * Method: pluginGetCurrentZoom
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Returns:
	 *    	Ritorna il valore corrente dello zoom della mappa visualizzata.
	 */ 
	pluginGetCurrentZoom: function () { return this.map.getScale(); },

	/**
	 * Method: pluginGetResolution
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicatoad un certo viewer
	 *
	 * Returns:
	 *    	Ritorna la risoluzione attuale (map units per pixel) della mappa
	 */ 
	pluginGetResolution: function () { return this.map.getResolution(); },

	/**
	 * 
	 * @param {} scala
	 * @param {} unita
	 */
	pluginGetResolutionFromScale: function (scala, unita) { return OpenLayers.Util.getResolutionFromScale(scala, unita); },
	
	/**
	 * Method: pluginGetSRID
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicatoad un certo viewer
	 *
	 * Returns:
	 *    	Ritorna il sistema di riferimento (map units per pixel) della mappa
	 */ 
	pluginGetSRID: function () { return this.paramsJS.mappe.SRID;},
	
	/**
	 * Method: pluginGetUnits
	 * come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicatoad un certo viewer
	 * 
	 * Returns:
	 *    	Ritorna il sistema di unità della mappa
	 */
	pluginGetUnits: function () { return this.paramsJS.mappe.units;},
	
	/**
	 * 
	 * Returns:
	 *    	Ritorna true se l'unita usata dalla mappa è in gradi altrimenti false
	 */
	isDregreesUnits: function() {
		units = this.pluginGetUnits();
		if (units=="dd" || units=="degrees")
			return true;
		else
			return false;
	},
	
	/**
	 * 
	 * Returns:
	 *    	Ritorna true se l'unita usata dalla mappa è in metri o kilometri altrimenti false
	 */
	isMetresUnits: function() {
		units = this.pluginGetUnits();
		if (units=="m" || units=="km")
			return true;
		else
			return false;
	},

	/**
	 * Method: pluginGetDotsPerInch
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicatoad un certo viewer
	 *
	 * Returns:
	 *    	Ritorna i dots per inch utilizzati dalla mappa
	 */ 
	pluginGetDotsPerInch: function() {
		return OpenLayers.DOTS_PER_INCH;
	},
	
	/**
	 * Method: pluginSetDotsPerInch
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicatoad un certo viewer
	 * 
	 * @param {} dpi
	 */
	pluginSetDotPerInch: function(dpi) {
		OpenLayers.DOTS_PER_INCH = dpi;
	},
	
	/**
	 * Method: pluginAddSelected
	 * Funzione che viene chiamata per aggiungere un oggetto geometrico a quelli selezionati
	 * Per adesso solo un oggetto e' selezionabile, quindi cancella eventuali oggetto selezionati in precedenza.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 * 
	 * Parameters:
	 * jsGeometry - {} jsGeometry
	 * style - {} style
	 * 
	 */ 
	pluginAddSelected: function (jsGeometry, style) {
		var wkt = new OpenLayers.Format.WKT();
		var feature = wkt.read(jsGeometry.geometry);
		feature.codTPN = jsGeometry.codTPN;
		if (style) feature.style = style;
		this.selezioniLayer.addFeatures([feature]);
	},

	/**
	 * Method: pluginClearSelected
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 *	Returns:
	 *  {Boolean}
	 */ 
	pluginClearSelected: function (bRedraw, codTPN) {
		if (codTPN) {
			var buff=new Array();
			for (var i=0;i<this.selezioniLayer.features.length;i++) {
				if (this.selezioniLayer.features[i].codTPN == codTPN) buff.push(this.selezioniLayer.features[i]); 
			}
			for (var i=0;i<buff.length;i++) {
				 this.selezioniLayer.destroyFeatures(buff); 
			}
		} else {
			// non viene gestito bRedraw perche' in questo viewer la cancellazione non richiede il ridisegno
		    this.selezioniLayer.destroyFeatures();  
		}
		// ritorna sempre false perch? il redraw non e' mai necessario
		return false;
	},

	/**
	 * Method: pluginZoomToSelected
	 * Esegue lo zoom to selected.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * zoom - {Number} zoom, se valorizzato viene fatto lo zoom alla data scala
	 * buffer - {Number} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	 */
	pluginZoomToSelected: function (zoom, buffer) {
			//var bounds = this.selezioniLayer.features[0].geometry.getBounds();
		if(!(this.selezioniLayer.features && this.selezioniLayer.features.length>0)) return;
		var bounds = this.olGetExtents(this.selezioniLayer.features);
		if (!zoom) {
			if (buffer && this.isMetresUnits()) {
				var boundsBuffered = new OpenLayers.Bounds();
			    boundsBuffered.extend(new OpenLayers.LonLat(bounds.left-buffer/2,bounds.bottom-buffer/2));
			    boundsBuffered.extend(new OpenLayers.LonLat(bounds.right+buffer/2,bounds.top+buffer/2));
			    this.map.zoomToExtent(boundsBuffered);
			} else {
				if (bounds.top==bounds.bottom && bounds.right==bounds.left) {
					var lonLat = bounds.getCenterLonLat();
					this.pluginGotoPosition(lonLat.lon, lonLat.lat, this.paramsJS.mappe.maxScale, false);
				} else {
					this.map.zoomToExtent(bounds);
				}
			}
		} else {
			var lonLat = bounds.getCenterLonLat();
			this.pluginGotoPosition(lonLat.lon, lonLat.lat, zoom, false);	
		}
		this.isAlreadyDrawn=true;
	},
	
	/**
	 * @method pluginGetSelectedFeaturesBounds
	 * Restituisce il bounding box delle features selezionate compreso il buffer se passato in metri.
	 * Null se non ci sono features.
	 * 
	 * @param {String} buffer
 	 * Codice EPSG del sistema di riferimento di partenza (Es. EPSG:26591) 
 	 * 
	 * @return {TolomeoExt.BBox}
	 * Bounding box delle features selezione compreso il buffer se passato in metri
	 * 
	 */
	pluginGetSelectedFeaturesBounds: function(buffer){
		if(!(this.selezioniLayer.features && this.selezioniLayer.features.length>0)) return null;
		var bounds = this.olGetExtents(this.selezioniLayer.features);
		if(buffer && this.isMetresUnits()){
			var boundsBuffered = new OpenLayers.Bounds();
		    boundsBuffered.extend(new OpenLayers.LonLat(bounds.left-buffer/2,bounds.bottom-buffer/2));
		    boundsBuffered.extend(new OpenLayers.LonLat(bounds.right+buffer/2,bounds.top+buffer/2));
		    return BBox.create(boundsBuffered);
		}
		return new BBox.create(bounds);
	},	
	
	/**
	 * @method pluginGetHighlightedFeaturesBounds
	 * Restituisce il bounding box delle features evidenziate compreso il buffer se passato in metri.
	 * Null se non ci sono features.
	 * 
	 * @param {String} buffer
 	 * Codice EPSG del sistema di riferimento di partenza (Es. EPSG:26591) 
 	 * 
	 * @return {TolomeoExt.BBox}
	 * Bounding box delle features selezione compreso il buffer se passato in metri
	 * 
	 */
	pluginGetHighlightedFeaturesBounds: function(buffer){				
		if(!(this.evidenziazioniLayer.features && this.evidenziazioniLayer.features.length>0)) return null;
		var bounds = this.olGetExtents(this.evidenziazioniLayer.features);
		
		if(buffer && this.isMetresUnits()){
			var boundsBuffered = new OpenLayers.Bounds();
		    boundsBuffered.extend(new OpenLayers.LonLat(bounds.left-buffer/2,bounds.bottom-buffer/2));
		    boundsBuffered.extend(new OpenLayers.LonLat(bounds.right+buffer/2,bounds.top+buffer/2));
		    return BBox.create(boundsBuffered);
		}
		return new BBox.create(bounds);
	},
	
	/**
	 * Method: pluginAddHighlighted
	 * Funzione che viene chiamata per aggiungere un oggetto geometrico a quelli selezionati.
	 * Se bMulti non definito o false un solo oggetto e' evidenzxiabile, quindi cancella eventuali oggetto selezionati in precedenza
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicatoad un certo viewer.
	 *
	 * Parameters:
	 * jsGeometry - {JSGeometryArray o JSGeometry} Oggetto da evidenziare, se passato un JSGeometryArray viene utilizzato il primo
	 * bMulti - {Boolean} bMulti Se non definito o false non è consentita la presenza di più di un oggetto, se True è consentita.
	 */
	pluginAddHighlighted: function (jsGeometry, bMulti, style) {
	    //Vecchia versione monoblocco 
		if (!bMulti) this.evidenziazioniLayer.destroyFeatures();
		var geoms = new JSGeometryArray();
		
		if(jsGeometry instanceof JSGeometryArray){
			geoms = jsGeometry;
		}else if(jsGeometry instanceof JSGeometry){
			geoms.add(jsGeometry);
		}else{
			alert("tipo sconosciuto " + jsGeometry);
			return;
		}
		
		/*var style_evidenziato = {
	                strokeColor: "#FFCC00",
	                strokeOpacity: 1,
	                strokeWidth: 2,
	                pointRadius: 6,
	                pointerEvents: "visiblePainted",
	                fillColor : "yellow",
	                fillOpacity: 0.7,
	                externalGraphic: "http://localhost:8080/commonintra2-0/img/icone/hand.gif",
	                graphicWidth: 20,
					graphicHeight: 20,
					graphicOpacity: 1,
					graphicXOffset: -10,
					graphicYOffset: -10
	            };*/
		
		var feats = new Array();
		
		for (var i=0; i<geoms.geometries.length; i++) {
			var wkt = new OpenLayers.Format.WKT();
			var feat = wkt.read(geoms.geometries[i].geometry);
			if (style) feat.style = style;
		    feats.push( feat);
		}
		
		this.evidenziazioniLayer.addFeatures(feats);
		//this.map.zoomToExtent(feature.geometry.getBounds());
	},
	
	/**
	 * Method: pluginClearHighlighted
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * bRedraw - {} bRedraw
	 * 
	 * Returns:
	 *     {Boolean}
	 */
	pluginClearHighlighted: function (bRedraw) {
		// non viene gestito bRedraw perche' in questo viewer la cancellazione non richiede il ridisegno
	    this.evidenziazioniLayer.destroyFeatures();
	    // ritorna sempre false perch? il redraw non e' mai necessario
	    return false;
	},

	/**
	 * Method: olGetExtents
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * features - {OpenLayers.Fetures} features
	 * 
	 * Returns:
	 *     {OpenLayers.Bounds}
	 */
	olGetExtents: function(features) {
		var bounds = features[0].geometry.getBounds().clone();
		for(var i=1;i<features.length;i++)
	    	bounds.extend(features[i].geometry.getBounds());
	   	return bounds;
	},

	/**
	 * Method: pluginZoomToHighlighted
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * zoom - {} zoom
	 * buffer - {Number} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	 */
	pluginZoomToHighlighted: function (zoom, buffer) {
		//var bounds = this.evidenziazioniLayer.features[0].geometry.getBounds();
		if(!(this.evidenziazioniLayer.features && this.evidenziazioniLayer.features.length>0)) return;
		var bounds = this.olGetExtents(this.evidenziazioniLayer.features);
		if (!zoom) {
			if (buffer && this.isMetresUnits()) {
				var boundsBuffered = new OpenLayers.Bounds();
			    boundsBuffered.extend(new OpenLayers.LonLat(bounds.left-buffer/2,bounds.bottom-buffer/2));
			    boundsBuffered.extend(new OpenLayers.LonLat(bounds.right+buffer/2,bounds.top+buffer/2));
			    this.map.zoomToExtent(boundsBuffered);
			} else {
				this.map.zoomToExtent(bounds);
			}
		} else {
			var lonLat = bounds.getCenterLonLat();
			this.pluginGotoPosition(lonLat.lon, lonLat.lat, zoom, false);	
		}
		
		this.isAlreadyDrawn=true;	
	},

	/**
	 * Method: pluginZoomToExtent
	 * Esegue lo zoom ad uno specifico extent.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * geometry - {Mixed} Stringa wkt della geometria o oggetto di tipo BBox
	 * buffer - {Number} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	 */
	pluginZoomToExtent: function (geometry, buffer) {
		
		var bounds;
		
		if (typeof(geometry) == 'string') {
			var wkt     = new OpenLayers.Format.WKT();
			var feature = wkt.read(geometry);
			bounds = feature.geometry.getBounds();
		} else {
			bounds = new OpenLayers.Bounds(geometry.left,geometry.bottom,geometry.right,geometry.top);
		} 
		
		/*
		if (!buffer) {
			this.map.zoomToExtent(bounds);
		} else {
			boundsBuffered = new OpenLayers.Bounds();
		    boundsBuffered.extend(new OpenLayers.LonLat(bounds.left-buffer/2,bounds.bottom-buffer/2));
		    boundsBuffered.extend(new OpenLayers.LonLat(bounds.right+buffer/2,bounds.top+buffer/2));
		    this.map.zoomToExtent(boundsBuffered);
		}
		*/
		
		if (buffer && this.isMetresUnits()) {
			var boundsBuffered = new OpenLayers.Bounds();
		    boundsBuffered.extend(new OpenLayers.LonLat(bounds.left-buffer/2,bounds.bottom-buffer/2));
		    boundsBuffered.extend(new OpenLayers.LonLat(bounds.right+buffer/2,bounds.top+buffer/2));
		    this.map.zoomToExtent(boundsBuffered);
		} else {
			this.map.zoomToExtent(bounds);
		}
	},

	/**
	 * Method: pluginZoomToScale
	 * Esegue lo zoom ad una specifica scala.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * scale - {Number} scale
	 */
	pluginZoomToScale: function (scale) {
		this.map.zoomToScale(scale,true);
	},

	/**
	 * Method: pluginUpdateCustomQuery
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * customQuery - {} customQuery
	 */
	pluginUpdateCustomQuery: function (customQuery ) {
		
		this.customQuery = customQuery;
		
		//TODO Funziona solo con mappa 0
		var nmappa = 0;
		var layAggregCount = this.paramsJS.mapDefinitions[0].getLayerAggregationCount();
		
		for(var index = 0; index < layAggregCount; index++) {
		
			var layViewAggreg = this.paramsJS.mapDefinitions[nmappa].getLayerAggregation(index);
			var bNoTolomeoParams = layViewAggreg.server.noTolomeoParams;
			
			if (!bNoTolomeoParams) {
				var currLayer = this.map.layers[layViewAggreg.nPluginLayer];
			
				var cqBuff = TolomeoExt.ToloViewerOLPanel.customQueryForServer(customQuery[nmappa], layViewAggreg.serverID);
				
				if (currLayer.CLASS_NAME.indexOf("MapServer")!=-1) {
					// in caso di layer mapserver 
					var msParams = TolomeoExt.ToloViewerOLPanel.customQueryToMapserverObj(cqBuff);
					msParams.random= Math.random();
					currLayer.mergeNewParams(msParams);
				} else {
					if (currLayer.CLASS_NAME.indexOf("WMS")!=-1) {
						// in caso di WMS 
						var allParams = TolomeoExt.ToloViewerOLPanel.customQueryToGeoserverObj(cqBuff);
						allParams.random= Math.random();
						// Aggiungo anche parametri secondo standard mapserver per casi WMS servito da mapserver
						var msParams = TolomeoExt.ToloViewerOLPanel.customQueryToMapserverObj(cqBuff);
						Ext.apply(allParams, msParams) 
						currLayer.mergeNewParams(allParams);
					} else {
						// altri cosa fare?	
					}
				}
			}
		}
	},

	
	/**
	 * Method: pluginSetLayerOpacity
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * nomeLayerQuery - {String} nomeLayer
	 * opacity - {float} opacity
	 */
	pluginSetLayerOpacity: function (nPluginLayer, opacity) {
		 this.map.layers[nPluginLayer].setOpacity(opacity);
		/*
		 var param = new Object();
		param["map.layer["+nomeLayer+"]"] = "opacity "+opacity;
		
		this.map.baseLayer.mergeNewParams(param);
		*/
	},

	/**
	 * @method pluginSetLayerEnhance
	 * Modifica i parametri di miglioramento immagine come brightness, contrast etc.
	 *
	 * @params effect {Object} 
	 *
	 */
	pluginSetLayerEnhance: function (effect, nPluginLayer, newValue) {
		var layer = this.map.layers[nPluginLayer];
		this._setLayerEffect(effect, layer, newValue);
		
	},
	
	
    /**
     * 
     * @method _setLayerEffect
     * applica un effetto css (derivato da metodo setOpacity di openlayers)
     * @private
     *
     * @param effect
     * @param layer
     * @param newValue
     */
    _setLayerEffect: function(effect, layer, newValue) {
        
    	var reStr = effect.cssProp + " *\\([^\\)]*\\)";
    	var re = new RegExp(reStr, "i");
    	
    	
    	element=layer.div;
        
        var effectcssval = "";
        if (newValue!=null && newValue != undefined) {
        	effectcssval = effect.cssProp + '(' + newValue + effect.cssUm + ')';
        } else {
        	effectcssval = effect.cssProp + '(' + effect.defaultValue + effect.cssUm + ')';
        }
        if (re.test(element.style['filter'])) {
        	element.style['filter'] =  element.style['filter'].replace(re, effectcssval)  ;
        } else {
        	element.style['filter'] += " "  + effectcssval  ;
        }
        if (re.test(element.style['-webkit-filter'])) {
        	element.style['-webkit-filter'] =  element.style['-webkit-filter'].replace(re, effectcssval)  ;
        } else {
        	element.style['-webkit-filter'] += " "  + effectcssval  ;
        }
  
    },
		
	/**
	 * Method: pluginAddMarker
	 * Permette di aggiungere un marker. Attualmente gestisce un solo marker, quindi quando ne viene aggiunto uno nuovo viene cancellato il precedente.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * x - {float} x
	 * y - {float} y
	 * isUnique - {boolean} Se impostato a true vengono rimossi gli altri markers prima di aggiungere questo
	 */
	pluginAddMarker: function (x, y, iconPath, isUnique) {
		if(isUnique){
			markersLayer.clearMarkers();
		}
		var size = new OpenLayers.Size(20,34);
	    var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
	    var icon = new OpenLayers.Icon(iconPath ? iconPath : this.TOLOMEOServer+ this.TOLOMEOStaticRoot+'img/markers/red_PMarker.png',size,offset);
	    markersLayer.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(x,y),icon));
	},
	
	/**
	 * Method: pluginAddPopup
	 * Permette di aggiungere un popup con pulsante di chiusura.
	 *
	 * Parameters:
	 * x - {float} x
	 * y - {float} y
	 * htmlText - {String} testo del popup in formato html
	 * isUnique - {boolean} se true rimuove tutti gli altri popup presenti
	 * editable - {boolean} se true il popup è cliccabile/editabile
	 * 
	 */
	pluginAddPopup: function (x, y, htmlText, isUnique, editable) {		
		
		var me = this;
		var p = new OpenLayers.Popup.FramedCloud(
                        "popup" + Ext.id(), 
                        new OpenLayers.LonLat(x,y),
                        null,
                        htmlText,
                        null,
                        true,
                        this._onPopupClose
                    );
		p.editable = !!editable;	
        p.closed = false;        
        
        // if editable we have to mange click on contentDiv to edit
        if(p.editable){
        	p.contentDivEvents = new OpenLayers.Events(this, p.contentDiv, null, true);
			p.contentDiv.title = ToloI18n.getMsg("ToloViewerOLPanel.pluginAddPopup.title");
	        function onClick(evt) {
	            me._onPopupUpdate(this);
	            OpenLayers.Event.stop(evt, true);
	        }
	        
	        p.contentDivEvents.on({
	            "click": onClick,
	            scope: p
	        });
	        
	        OpenLayers.Element.addClass(p.contentDiv,'editableNote');        
        }
        
		this.map.addPopup(p, isUnique);	    
		return p;
	},
	
	/**
	 * Method: pluginUpdatePopup
	 * Permatte di modifica il testo di un popup per mezzo del suo id e del nuovo testo
	 *
	 * Parameters:
	 * idPopup - {String} identificativo del popup
	 * htmlText - {String} testo del popup in formato html
	 */
	pluginUpdatePopup: function (idPopup, htmlText) {		
		for(var i in this.map.popups){
			var popup = this.map.popups[i];
			if(popup.id == idPopup){
				popup.setContentHTML(htmlText);
			}
		}   
	},
	
	_onPopupClose: function(e) {
		
		var popup = this;

		// If editable we have to clean event management
		if(popup.editable){
			popup.contentDivEvents.destroy();
        	popup.contentDivEvents = null;
		}
		
		popup.destroy();
		return true;
	},
	
	_onPopupUpdate: function(popup) {
		this.fireEvent("popupClicked",popup.id,popup.contentHTML,popup.lonlat.lon,popup.lonlat.lat);
	},
	
	pluginGetOpenedPopups: function() {
		return this.map.popups;
	},
	
	/**
	 * Method: pluginClearMarkers
	 * Permette di eliminare i marker.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 */
	pluginClearMarkers: function () {
		markersLayer.clearMarkers();
	},

	/**
	 * Method: pluginAutoIdentifyEnable
	 * Permette di aggiungere un marker. Attualmente gestisce un solo marker, quindi quando ne viene aggiunto uno nuovo viene cancellato il precedente.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * bEnable - {Boolean} bEnable
	 */
	pluginAutoIdentifyEnable: function (bEnable) {
		if (bEnable) {
			this.mapControls['autoIdentify'].activate();
		} else {
			this.mapControls['autoIdentify'].deactivate();
		}
	},
	
	/**
	 * Method: pluginAddAutoidentified
	 * Funzione che viene chiamata per aggiungere un oggetto geometrico a quelli selezionati.
	 * Per adesso solo un oggetto e' selezionabile, quindi cancella eventuali oggetto selezionati in precedenza.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicatoad un certo viewer.
	 *
	 * Parameters:
	 * jsGeometry - {JSGeometryArray o JSGeometry} jsGeometry
	 */
	pluginAddAutoidentified: function (jsGeometry) {
		var wkt = new OpenLayers.Format.WKT();
		
		for (var i=0; i<jsGeometry.geometries.length; i++) {
			var feature = wkt.read(jsGeometry.geometries[i].geometry);
			autoIdentifiedLayer.addFeatures([feature]);
		}
	},

	/**
	 * Method: pluginClearAutoidentified
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * bRedraw - {Boolean} bRedraw
	 */
	pluginClearAutoidentified: function (bRedraw) {
		// non viene gestito bRedraw perche' in questo viewer la cancellazione non richiede il ridisegno
	    autoIdentifiedLayer.destroyFeatures();
	    // ritorna sempre false perch? il redraw non e' mai necessario
	    return false;
	},

	/**
	 * Method: pluginZoomToAutoidentified
	 * Esegue lo zoom to selected.
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * zoom - {Number} zoom
	 */
	pluginZoomToAutoidentified: function (zoom) {
		if (autoIdentifiedLayer.features.length==1) {
			var bounds = autoIdentifiedLayer.features[0].geometry.getBounds();
			if (!zoom) {
				this.map.zoomToExtent(bounds);
			} else {
				var lonLat = bounds.getCenterLonLat();
				this.pluginGotoPosition(lonLat.lon, lonLat.lat, zoom, false);	
			}
			
			this.isAlreadyDrawn=true;
		}
	},
		
	/**
	 * Method: pluginPan
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * direction - {} direction
	 * slideFactor - {} slideFactor
	 */
    pluginPan: function(direction, slideFactor){
		 slideFactor = slideFactor || 50;
		 switch (direction) {
            case "N": 
                this.map.pan(0, -slideFactor);
                break;
            case "S": 
                this.map.pan(0, slideFactor);
                break;
            case "O": 
                this.map.pan(-slideFactor, 0);
                break;
            case "E": 
                this.map.pan(slideFactor, 0);
                break;
        }
	},
	
	pluginHistoryPrev: function(){
		if (this.mapControls.navhistory) {
			this.mapControls.navhistory.previous.trigger();
		}
	},
	
	pluginHistoryNext: function(){
		if (this.mapControls.navhistory) {
			this.mapControls.navhistory.next.trigger();
		}
	},
	
	/**
	 * Method: pluginSnapClear
	 * Disattiva tutti i controlli di snap e rende invisibili i relativi layers
	 */
	pluginSnapClear: function(){
		for(var codTPN in this.snapControls){			
			this.pluginSnapDeactivate(codTPN);			
		}		
	},
	
	/**
	 * Method: pluginSnapActivate
	 * Attiva il controllo di snap per il layer relativo al codTPN passato e visualizza tutti i layer
	 * per i quali è possibile fare lo snap.
	 *
	 * Parameters:
	 * codTPN - {int} codice del layer su cui attivare lo snap 
	 */
	pluginSnapActivate: function(codTPN){
		if(this.snapControls[codTPN]){
			for(var l = 0; l < this.snapControls[codTPN][0].layerList.length; l++){
				this.snapControls[codTPN][0].layerList[l].setVisibility(true);
			}
			for(var sc = 0; sc < this.snapControls[codTPN].length; sc++){
				this.snapControls[codTPN][sc].activate();
			}
		}
	},
	
	/**
	 * Method: pluginSnapDeactivate
	 * Disattiva il controllo di snap per il layer relativo al codTPN passato e rende invisibili tutti i layer
	 * per i quali è possibile fare lo snap.
	 *
	 * Parameters:
	 * codTPN - {int} codice del layer su cui disattivare lo snap 
	 */
	pluginSnapDeactivate: function(codTPN){
		if(this.snapControls[codTPN]){
			//if(this.snapControls[codTPN].deactivate){
				for(var sc = 0; sc < this.snapControls[codTPN].length; sc++){
					if(this.snapControls[codTPN][sc] && this.snapControls[codTPN][sc].deactivate){
						this.snapControls[codTPN][sc].deactivate();
						for(var l = 0; l < this.snapControls[codTPN][sc].layerList.length; l++){
							this.snapControls[codTPN][sc].layerList[l].setVisibility(false);
						}
					}
				}
			//}
		}
	},
	
	pluginRemoveAllLayers: function() {
		 var num = this.layersMappa.length;
		 for (var j=0; j<num; j++) {
			 this.map.removeLayer( this.layersMappa[j], false );
		  }  
		 this.layersMappa.length=0;
	},
	
	/**
	 * Method: isBusy
	 * Funzione di busy.
	 *
	 * Parameters:
	 * scale - {Number} scale
	 */
	isBusy: function (){ return this.mapBusy != 0;},
	
	/**
	 * Method: onBusy
	 * Funzione di busy.
	 *
	 * Parameters:
	 * areYouBusy - {Boolean} areYouBusy
	 */
	onBusy: function (areYouBusy){
			if(areYouBusy){
				this.mapBusy++;
				this.refreshBusy();
			}else{
				if (this.mapBusy>0) this.mapBusy--;
				if(!this.isBusy()){
					this.refreshBusy();;
				}
			}
		},
		
	/**
	 * Method: noneBusy
	 * Funzione di busy.
	 * 
	 */	
	noneBusy: function (){
		this.mapBusy = 0;
		if (this.myMask) this.myMask.hide();
	},

	/**
	 * Method: refreshBusy
	 * Funzione di busy
	 */
	refreshBusy: function() {
		if (this.myMask) 
			if (this.mapBusy) this.myMask.show();
			else this.myMask.hide();
	},

	
	/**
	 * Method: relayContainerMove
	 *
	 * Parameters:
	 * ctl - {} ctlBusy

		relayContainerMove: function (ctl) {
			var a= null;
			if(ctl.ownerCt) {
				a= this.relayContainerMove(ctl.ownerCt);
				if (!a) return ctl.ownerCt;
				else return a;
			}
		},*/
	
	getGoogleApiVersion: function() {
		
		return (typeof GMap2 === "function" ? "2" : "3");
		
	},
	
	/** TODO Per adesso non è supportato il meccanismo del caricamento on demand
	 * 
	 */
	loadLayerPrereq: function(tipoLayer, callbackFn) {
		/* TODO
		var urls = this.layerTypePrereq[tipoLayer];
		var url="";
		
		// ALE Da fare scelta per adesso carica la 3
		if (tipoLayer>0 && tipoLayer<5) { // Layer Google
		//	url = (getGoogleApiVersion==2) ? urls[0] : urls[1]; 
			url = urls[1];
		} else {
			url = urls[0];
		}
		if (url!="") loadScript(url, function() { 
										//alert('ciao'); 
										callbackFn(tipoLayer);
										} );
		else callbackFn(tipoLayer);
		*/
	},

	pluginAddAllMaps: function() {
		// Cerca tra le mappe in this.paramsJS.mappe
		for (var i = 0; i<this.paramsJS.mappe.mappaList.length; i++) {
			var mappa = this.paramsJS.mappe.mappaList[i];
			this.pluginAddLayer(mappa, i, this.customQuery, this.map.getNumLayers(), false);
		}
	},
	
	/**
	 * Aggiunge un layer in mappa. Non è supportato l'inserimento di una mappa di un tipo per il quale non sia già stata inclusa l'api se necessaria  
	 * (per esempio una mappa google se non è stato già incluso l'apposito javascript <br/>
	 * Esempio di utilizzo: <br/>
	   <pre>
	  		var mappa = new Object();
			mappa.mapOptions = "layers: 'WMS_PRATO'";
			mappa.overlay = false;
			mappa.SRID = "EPSG:3003";
			mappa.units = "m";
			mappa.mostraInLegenda=true;
			mappa.viewerOptions = "";
			mappa.typeCode = 11;		// WMS
			mappa.nome = "WMS Prato";
			mappa.url = "http://geoserver.comune.prato.it/geoserver/wms";
			tolomeoPnl.api.addLayer(mappa);
	   </pre>
	 * 
	 * @param {} mappa - Oggetto javascript corrispondente alla serializzazione di un tag mappa del file di preset
	 * @param {} nMappa - posizione nel layer switcher
	 * @param {} customQuery - come analogo parametro in olViewerNewLayer
	 */
	pluginAddLayer: function(mappa, nMappa, customQuery, showLayerSwitcher) {
		var me = this;
		//this.loadLayerPrereq(mappa.typeCode,
		//			function() { 
						me.loadLayerCallback(mappa, nMappa, customQuery, showLayerSwitcher) //}
		//			);
		
	},
	
	loadLayerCallback: function(mappa, nMappa, customQuery, showLayerSwitcher) {
		var layer = this.olViewerNewLayer(mappa, nMappa, customQuery);
		  
		if (layer instanceof Array) {
			for (var i=0; i<layer.length; i++) {
				var pos = (layer[i].toloViewerLayerIndex!=undefined && layer[i].toloViewerLayerIndex!=null) ? layer[i].toloViewerLayerIndex : null;
				this._addLayerInMap(layer[i], nMappa, pos, showLayerSwitcher);	
			}
		} else {
			var pos = (layer.toloViewerLayerIndex) ? layer.toloViewerLayerIndex : null;
			this._addLayerInMap(layer, nMappa, pos, showLayerSwitcher);
		}
	},
	
	_addLayerInMap: function(layer, nMappa, pos, showLayerSwitcher) {
		this.layersMappa.push(layer);
		this.map.addLayer(layer);
		if (pos!=undefined && pos!=null) {
			this.map.setLayerIndex(layer, pos);
		}
		var controlId = 'layerSwitcher';
		if ((showLayerSwitcher==undefined || showLayerSwitcher==null || showLayerSwitcher==true) && this.map.getNumLayers()>1 && !this.map.getControl(controlId)) {
			var control = new OpenLayers.Control.LayerSwitcher({activeColor: "#004000", ascending: false});
			control.id = controlId;
			this.map.addControl(control);
		}
		// Se layer.singleTile è definito ed è true
		this.olRegisterLoadEvents(layer);
		if (layer.toloViewerWithBusy) 	this.olRegisterBusyEvents(layer);		
	},
	
	olViewerNewLayer: function (mappa, nMappa, customQuery, positionBase) {
	
		if (positionBase==undefined || positionBase==null) positionBase=0;
		var retVal = [];
		for (var i=0; i<this.paramsJS.mapDefinitions[nMappa].getLayerAggregationCount(); i++) {
			this.paramsJS.mapDefinitions[nMappa].getLayerAggregation(i).nPluginLayer = positionBase+i;
			retVal.push(this.olViewerNewLayer1(nMappa,i, customQuery));
			//retVal.push(this.olViewerNewLayer1(this.paramsJS.mapDefinitions[nMappa].getLayerAggregation(i), customQuery));	
		}
		// inverto l'ordine per sequire la convenzione GIS (layer più in alto visibile sopra gli altri) che e' contraria a quella di openlayers
		return retVal;
	},
	
	//  mappa, nMappa,
	// server, serverOpts, layers, layerOpts
	olViewerNewLayer1: function (nMappa, layerAggregIndex, customQuery) {
		
		// mpOpt mappa.mapOptions --> server opt, parametri che vengono trasmessi al server in query string -> serverOpts
		// viewerOptions --> opzioni del layer openlayers
		
		// layers deve contenere 
		//	nomi layer
		//  stili layer
		
		//TODO LayerSfondo non sono più gestiti qua. li deve gestire il chiamante
	
		var layViewAggreg = this.paramsJS.mapDefinitions[nMappa].getLayerAggregation(layerAggregIndex);
		var server = layViewAggreg.server;
		
		var layersAggreg = layViewAggreg.layers;
		var layerSep;
		
		var layer = null;
		var layerOptions = null;
		//transparent: true
		var mpOpt = { 
				transparent: true,
				wmscalluuid: this.wmscalluuid
		};
		if (this.withWmsCallUUID) {
			mpOpt.wmscalluuid = this.wmscalluuid;
			
		}
		if (server.serverOpts!=null) {
			var buff =Ext.JSON.decode("{" +  server.serverOpts + "}"); // Ext.util.JSON.decode("{" + mappa.mapOptions + "}");
			OpenLayers.Util.extend(mpOpt, buff);
		}

		layerOptions = new Object();
		
		layerOptions.singleTile             = !layViewAggreg.tilesMultiple;
		layerOptions.isBaseLayer            = !layViewAggreg.overlay;
		layerOptions.projection             = layViewAggreg.SRID;
		layerOptions.units                  = layViewAggreg.units;
		layerOptions.displayInLayerSwitcher = layViewAggreg.mostraInLegenda;				
		layerOptions.opacity                = (layViewAggreg['opacity']!=undefined && layViewAggreg['opacity']!=null) ? layViewAggreg['opacity'] : 1;
		
		//layerOptions.transitionEffect       = 'resize';
		
		//ALE aggiunte per layer google che non sentiva quelli dell'oggetto mappa 
		layerOptions.maxExtent = new OpenLayers.Bounds(this.paramsJS.mappe.maxExtentLeft, this.paramsJS.mappe.maxExtentBottom, this.paramsJS.mappe.maxExtentRight, this.paramsJS.mappe.maxExtentTop);
		layerOptions.minScale = this.paramsJS.mappe.minScale;
		layerOptions.maxScale = this.paramsJS.mappe.maxScale;

		var zs = this.getDefaultZoomLevels();
		if (zs!=null) {
			layerOptions.resolutions = [];
			for(var i = 0; i < zs.length; i++) {
				layerOptions.resolutions.push(OpenLayers.Util.getResolutionFromScale(zs[i], layerOptions.units));
			}
		} 

		
		var layerOptionsDaParamsJS = Ext.JSON.decode("{" + layViewAggreg.layerOptions + "}"); // Ext.util.JSON.decode("{" + mappa.viewerOptions + "}");
		Ext.apply(layerOptions, layerOptionsDaParamsJS);
		
		switch (layViewAggreg.server.typeCode) {
			
			// Mapserver
			case 0:
				layerSep = " ";
								
				var layAndStyle = this.paramsJS.getLayerAggregLayersAndStylesStrings(nMappa, layViewAggreg.nPluginLayer, layerSep, this.pluginGetCurrentZoom());
				mpOpt.layers = layAndStyle.layers;
				
				mpOpt.map_imagetype = (layViewAggreg.imageType!= undefined && layViewAggreg.imageType!=null && layViewAggreg.imageType!="") ? layViewAggreg.imageType : "agga";
				mpOpt.map_resolution = OpenLayers.DOTS_PER_INCH;
				
				layerOptions.attribution = layAndStyle.attribution ;
				
				if (customQuery) {
					var cqBuff = TolomeoExt.ToloViewerOLPanel.customQueryForServer(customQuery[nMappa], layViewAggreg.serverID);
					var msParams = TolomeoExt.ToloViewerOLPanel.customQueryToMapserverObj(cqBuff);
					Ext.apply(mpOpt, msParams);
				}
				
				
				
			 	layer = new OpenLayers.Layer.MapServer(server.nome, server.url, mpOpt, layerOptions);	
	            
	            layer.toloViewerWithBusy = layer.singleTile;

				break;

			// Google Streets
			case 1:	
				layer = new OpenLayers.Layer.Google(server.nome,   layerOptions );
				layer.toloViewerWithBusy = false;
				break;

			// Google Physical
			case 2:
				Ext.apply(layerOptions, {  type: (this.getGoogleApiVersion() == "2") ? G_NORMAL_MAP : google.maps.MapTypeId.TERRAIN });
				layer = new OpenLayers.Layer.Google(server.nome,   layerOptions );
				layer.toloViewerWithBusy = false;
				break;
			
			// Google Satellite
			case 3:
				Ext.apply(layerOptions, {  type: (this.getGoogleApiVersion() == "2") ? G_SATELLITE_MAP : google.maps.MapTypeId.SATELLITE });
				layer = new OpenLayers.Layer.Google(server.nome,   layerOptions );
				layer.toloViewerWithBusy = false;
				break;
			
			// Google Hybrid
			case 4:
				Ext.apply(layerOptions, {  type: (this.getGoogleApiVersion() == "2") ? G_HYBRID_MAP : google.maps.MapTypeId.HYBRID });
				layer = new OpenLayers.Layer.Google(server.nome,   layerOptions );
				layer.toloViewerWithBusy = false;
				break;
			
			// Yahoo Sat
			case 5: 
				Ext.apply(layerOptions, {  type: YAHOO_MAP_SAT });
				layer = new OpenLayers.Layer.Yahoo(server.nome, layerOptions);
				layer.toloViewerWithBusy = false;
				break;
			
			// Yahoo Reg
			case 6:
				Ext.apply(layerOptions, {  type: YAHOO_MAP_REG });
				layer = new OpenLayers.Layer.Yahoo(server.nome, layerOptions);
				layer.toloViewerWithBusy = false;
				break;
			
			// Yahoo Hybrid
			case 7:	
				Ext.apply(layerOptions, {  type: YAHOO_MAP_HYB });
				layer = new OpenLayers.Layer.Yahoo(server.nome, layerOptions);
				layer.toloViewerWithBusy = false;
				break;
			
			// Bing Aerial
			case 8:
				Ext.apply(layerOptions, {  type: VEMapStyle.Aerial });
				layer = new OpenLayers.Layer.VirtualEarth(server.nome,   layerOptions );
				layer.toloViewerWithBusy = false;
				break;

			// Bing Shaded
			case 9:
				Ext.apply(layerOptions, {  type: VEMapStyle.Shaded });
				layer = new OpenLayers.Layer.VirtualEarth(server.nome,   layerOptions );
				layer.toloViewerWithBusy = false;
				break;
			
			// Bing Hybrid
			case 10:
				Ext.apply(layerOptions, {  type: VEMapStyle.Hybrid });
				layer = new OpenLayers.Layer.VirtualEarth(server.nome,   layerOptions );
				layer.toloViewerWithBusy = false;
				break;	
			
			// WMS
			case 11: 	    		
				mpOpt.minZoomLevel = 0;
				layerSep = ",";
				var layAndStyle = this.paramsJS.getLayerAggregLayersAndStylesStrings(nMappa, layViewAggreg.nPluginLayer, layerSep, this.pluginGetCurrentZoom()); 
				mpOpt.layers = layAndStyle.layers;
				mpOpt.styles = layAndStyle.stili;
				
				if (customQuery) {
					var cqBuff = TolomeoExt.ToloViewerOLPanel.customQueryForServer(customQuery[nMappa], layViewAggreg.serverID);
					var wmsParams = TolomeoExt.ToloViewerOLPanel.customQueryToGeoserverObj(cqBuff);
					Ext.apply(mpOpt, wmsParams);
					// Aggiungo anche parametri secondo standard mapserver per casi WMS servito da mapserver
					var msParams = TolomeoExt.ToloViewerOLPanel.customQueryToMapserverObj(cqBuff);
					Ext.apply(mpOpt, msParams);
				}
				
				// WMS 1.3.0 vuole CRS, versioni precedenti vogliono CRS. 
				// Aggiungo anche CRS per avere qualcosa che funzioni in entrambi i casi, per non dover prima fare la getcapabilities per stabilire la versione
				mpOpt.CRS = this.pluginGetProjectionCode();
				if (mpOpt.layers == undefined || mpOpt.layers==null || mpOpt.layers=="") {
					layerOptions.visibility=false;
				}				
				if (layViewAggreg.tilesMultiple) {
					mpOpt.tilesorigin = [this.paramsJS.mappe.maxExtentLeft, this.paramsJS.mappe.maxExtentBottom];
					mpOpt.tiled = true;
				}
				
				layerOptions.attribution = layAndStyle.attribution ;
    		    layer = new OpenLayers.Layer.WMS(server.nome, server.url, mpOpt, layerOptions);
    		    layer.id = Math.random();
    		    //layer.id = "OpenLayers.Layer.WMS_" + server.url + mpOpt.layers+Math.random();
    		    layer.toloViewerWithBusy = layer.singleTile;


    		    break;
			
			// OpenStreetMap
			case 12:
				layer = new OpenLayers.Layer.OSM(server.nome);
				layer.toloViewerWithBusy = layer.singleTile;
				break;
				
			default:
				break;
		}
		layer.events.register('moveend', this, this.olLayerMove);
		layer.toloViewerLayerIndex=layViewAggreg.nPluginLayer;
		layer.toloNMappa = nMappa;
		layer.toloLayerSep = layerSep;
		
		// Applica effetti miglioramento immagine
		if(Modernizr.cssfilters){
			for (var i=0; i<ToloParamsJS.enhanceEffects.length; i++ ){
				var effect = ToloParamsJS.enhanceEffects[i];
				var newValue = (layViewAggreg[effect.key]!=undefined && layViewAggreg[effect.key]!=null) ? layViewAggreg[effect.key] : effect.defaultValue;
				this._setLayerEffect(effect, layer, newValue);
			}
		}
		
		//layer.events.register('loadstart', this, function(object, element) { return this.aaaa(object, element,layer, layerSep);});
		
		// TODO ??? layer.numeroMappa = nMappa;
		return layer;
	},

	olLayerScaleFilter: function(object, element) {
		
		for (var i=0; i<this.layersMappa.length; i++) {
			var layer=this.layersMappa[i];
					
			var layAndStyle = this.paramsJS.getLayerAggregLayersAndStylesStrings(layer.toloNMappa, layer.toloViewerLayerIndex, layer.toloLayerSep, ((object && object.zoomChanged && object.zoom && this.map.baseLayer) ? OpenLayers.Util.getScaleFromResolution(this.map.getResolutionForZoom(object.zoom), this.map.baseLayer.units) : this.pluginGetCurrentZoom()));
			
			if (layAndStyle.layers=="") {
				layer.setVisibility(false);
			} else {
				
				// modifica con i nuovi parametri
				//layer.mergeNewParams({layers: layAndStyle.layers, styles:  layAndStyle.stili });
				// Non utilizzo la mergeNewParams perchè innesca un redraw. Modifico layer.params direttamente (funzione non documentata, visto dai sorgenti il funzionamento della mergeNewParams)							
				if(layer.CLASS_NAME.indexOf("WMS")!=-1){
					// Aggiunto controllo su WMS, perchè altrimenti per Mapserver vengono inviati due parametri: "layer" e "LAYER" ed il secondo sovrascrive il primo non facendo 
					// correttamente i soli layers che devono essere accesi e/o spenti									
					layer.params = OpenLayers.Util.extend(layer.params, {LAYERS: layAndStyle.layers, STYLES:  layAndStyle.stili });					
				} else {
					layer.params = OpenLayers.Util.extend(layer.params, {layers: layAndStyle.layers, styles:  layAndStyle.stili });
				}
				
				layer.setVisibility(true);
			}
		}
		return true;
	},
	
	olLayerMove: function(obj) {
		//if (obj.zoomChanged)
		this.fireEvent("onMapMoveEnd", obj);
		
	},
	
	/**
	 * Method: olAutoIdentifyOnPause
	 *
	 * Parameters:
	 * e - {} evento.
	 */
	olAutoIdentifyOnPause: function (e){
		var lonlat = this.map.getLonLatFromViewPortPx(e.xy);
		this.fireEvent('onAutoIdentify', new Point(lonlat.lon, lonlat.lat), e.clientX, e.clientY, e.xy.x, e.xy.y);
		//onAutoIdentify(new Point(lonlat.lon, lonlat.lat), e.clientX, e.clientY);
	},
		
	/**
	 * Method: olAutoIdentifyOnMove
	 *
	 */
	olAutoIdentifyOnMove: function olAutoIdentifyOnMove() {
		this.fireEvent('onAutoIdentifyCancel', null);
		//onAutoIdentifyCancel();
	},
	
	/**
	 * Method: olHandleMeasurements
	 *
	 * Parameters:
	 * e - {} event
	 * partial - Boolean tha specify if measure is partial or final
	 *
	 * Returns:
	 *     {Booelan} 
	 */
	olHandleMeasurements : function (event,partial) {
		
		var control = event.control;
        var geometry = event.geometry;
        var units = event.units;
        var order = event.order;
        var measure = event.measure;            
        var returnObj = {dimension: order};        
                    
        if(order == 1) {
        	
            returnObj.length = {
            	'units' : units,
            	'measure' : measure
            }            
            returnObj.area = {            
            	'units' : units,
            	'measure' : 0
            }
            
        } else {
        	
        	var length = control.getBestLength(geometry);
        	
        	returnObj.length = {
            	'units' : length[1],
            	'measure' : length[0]
            }            
            returnObj.area = {            
            	'units' : units,
            	'measure' : measure
            }                       
        }            
        
        if(partial){
        	this.fireEvent('onMeasureChanging', returnObj);
        } else {
        	this.fireEvent('onMeasureChanged', returnObj);
        }
    },

	/**
	 * Method: olRegisterBusyEvents
	 *
	 * Parameters:
	 * layer - {} layer.
	 *
	 * Returns:
	 *     {Booelan} 
	 */
	olRegisterBusyEvents: function (layer) {
			layer.events.register("loadstart",  this, function () { this.onBusy(true); } );
			layer.events.register("loadend",    this, function () { this.onBusy(false);} );
			layer.events.register("loadcancel", this, function () { this.onBusy(false);} );
			
			return true;
	},

	olRegisterLoadEvents: function (layer) {
		layer.events.register("loadstart",  this, function () { this.fireEvent('loadstart'); } );
		layer.events.register("loadend",    this, function () { this.fireEvent('loadend');  } );
		layer.events.register("loadcancel", this, function () { this.fireEvent('loadcancel'); } );
		
		return true;
	},
	
	
	/**
	 * Method: activateControl
	 * Attiva un controllo disattivando tutti gli altri. Agisce su tutti i controlli con esclusione di quelli relativi al drawlayer (vedi activateControlDrawLayer)
	 *
	 * Parameters:
	 * nome - {String} nome.
	 */
	activateControl: function (nome) {
	    for (var key in this.mapControls) {
	    	if ( (key!='legend') && (key!=nome)  && (key!='navigation') && (key!='navhistory')) { 
	    		// lancio timeout invece di fare direttamente perche' viene invocato all'interno di un evento e si verificava errore this.point has no properties
	        	//setTimeout( "this.mapControls['"+key+"'].deactivate()", 2500 );
	        	this.mapControls[key].deactivate();	        	
	    	}
	    }
	    
	    this.mapControls[nome].activate();	    
	},
	
	/**
	 * Method: activateControl
	 * Attiva un controllo disattivando tutti gli altri. Agisce esclusivamente sui controlli relativi al drawlayer (vedi anche activateControl)
	 *
	 * Parameters:
	 * nome - {String} nome.
	 */
	activateControlDrawLayer: function (nome) {
	    for (var key in this.mapControlsDrawLayer) {
	        	this.deactivateControlDrawLayer(key);
	    }
	    this.mapControlsDrawLayer[nome].activate();
	},

	
	/**
	 * Method: deactivateControl
	 * Disattiva un controllo.
	 *
	 * Parameters:
	 * nome - {String} nome
	 */
	deactivateControl: function (nome) {
		this.mapControls[nome].deactivate();
	},
	
	/**
	 * Method: deactivateControlDrawLayer
	 * Disattiva un controllo.
	 *
	 * Parameters:
	 * nome - {String} nome
	 */
	deactivateControlDrawLayer: function (nome) {
		this.mapControlsDrawLayer[nome].deactivate();
	},
	
	/**
	 * Method: olViewerGeometryToGeometry
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 *
	 * Returns:
	 *     {JSGeometryArray o JSGeometry} 
	 */
	olViewerGeometryToGeometry: function (geom) {
	    var jsGeometry = new JSGeometry();
	    
	    var wkt = new OpenLayers.Format.WKT();

	    if (geom.geometry == undefined) {
	    	jsGeometry.geometry = wkt.write(geom.feature);
	    } else { 
	    	jsGeometry.geometry = wkt.write(geom);
	    }
	    jsGeometry.SRID = this.paramsJS.mappe.SRID;
		
	    if (jsGeometry.geometry.indexOf("POINT")!=-1){
	    	jsGeometry.geometryIsValid = true;
	    } else {
	    	if (jsGeometry.geometry.indexOf("LINE")!=-1){
	    		var v = geom.feature.geometry.getVertices();
		    	if (v.length>2 || 
		    		(v.length==2 && (v[0].x!=v[1].x || v[0].y!=v[1].y))){
		    			jsGeometry.geometryIsValid = true;		
		    	} else {
		    		jsGeometry.geometryIsValid = false;
		    	}
		    } else {
		    	if (jsGeometry.geometry.indexOf("POLYGON")!=-1){
		    		var v = geom.feature.geometry.getVertices();
			    	if (v.length>3 || 
			    		(v.length==3 && (
			    				(v[0].x!=v[1].x || v[0].y!=v[1].y) &&
			    				(v[0].x!=v[2].x || v[0].y!=v[2].y) &&
			    				(v[1].x!=v[2].x || v[1].y!=v[2].y)))){
			    			jsGeometry.geometryIsValid = true;		
			    	} else {
			    		jsGeometry.geometryIsValid = false;
			    	}	
			    }		
		    }	
	    }
	    
		return jsGeometry;
	},

	/**
	 * Method: olViewerOnDigitizedPoint
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 * 
	 */
	olViewerOnDigitizedPoint: function (geom) {
		// Chiama la funzione di TolomeoIF
		this.fireEvent('onDigitizeEndPoint', this.olViewerGeometryToGeometry(geom));
		//onDigitizeEndPoint(this.olViewerGeometryToGeometry(geom));
		this.activateControl('navigation');
	},
	
	/**
	 * Method: olViewerOnDigitizedPointFromRef
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 * 
	 */
	olViewerOnDigitizedPointFromRef: function (geom) {
		// Chiama la funzione di TolomeoIF
		this.fireEvent('onDigitizePointFromRefEnd', this.olViewerGeometryToGeometry(geom));
		this.olViewerOnDigitizedPoint(geom);
	},
	
	/**
	 * Method: olViewerOnDigitizedPointByCAD
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 */
	olViewerOnDigitizedPointByCAD: function (geom) {
		// Chiama la funzione di TolomeoIF
		this.fireEvent('onDigitizePointByCADEnd', this.olViewerGeometryToGeometry(geom));
		this.olViewerOnDigitizedPoint(geom);
		
	},
	
	/**
	 * Method: olViewerOnDigitizedLine
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 */
	olViewerOnDigitizedLine: function (geom) {
		this.fireEvent('onDigitizeEndLine', this.olViewerGeometryToGeometry(geom));
		this.activateControl('navigation');
	},
	
	/**
	 * Method: olViewerOnDigitizedLineByCAD
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 */
	olViewerOnDigitizedLineByCAD: function (geom) {
		// Chiama la funzione di TolomeoIF
		this.fireEvent('onDigitizeLineByCADEnd', this.olViewerGeometryToGeometry(geom));
		this.olViewerOnDigitizedLine(geom);
		
	},
	
	/**
	 * Method: olViewerOnDigitizedPolygon
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 * 
	 */
	olViewerOnDigitizedPolygon: function (geom) {
		// Chiama la funzione di TolomeoIF
		this.fireEvent('onDigitizeEndPolygon', this.olViewerGeometryToGeometry(geom));
		//onDigitizeEndPolygon(this.olViewerGeometryToGeometry(geom));
		this.activateControl('navigation');
	},
	
	/**
	 * Method: olViewerOnDigitizedPolygonByCAD
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 * 
	 */
	olViewerOnDigitizedPolygonByCAD: function (geom) {
		// Chiama la funzione di TolomeoIF
		this.fireEvent('onDigitizePolygonByCADEnd', this.olViewerGeometryToGeometry(geom));		
		this.olViewerOnDigitizedPolygon(geom);
	},

	/**
	 * Method: olViewerOnVertexEditingEnd
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 */
	olViewerOnVertexEditingEnd: function (geom) {
		this.fireEvent('onDigitizeEndVertexEditing', this.olViewerGeometryToGeometry(geom));
		this.activateControl('navigation');
	},
	
	
	/**
	 * Method: olViewerOnDigitizedFeatureVertexEditingEnd
	 * Funzione chiamata alla fine di una azione di vertex editing su una feature già digitalizzata in precedenza
	 * Parameters:
	 * geom - {} la geometria.
	 */
	olViewerOnDigitizedFeatureVertexEditingEnd: function (geom) {
		this.fireEvent('onDigitizedFeatureVertexEditingEnd', this.olViewerGeometryToGeometry(geom));
	},
		
	/**
	 * Method: olViewerOnDragDropEnd
	 *
	 * Parameters:
	 * geom - {} la geometria.
	 */
	olViewerOnDragDropEnd: function (geom) {
		this.fireEvent('onDigitizeEndDragDrop', this.olViewerGeometryToGeometry(geom));
		this.activateControl('navigation');
	},
	
	/**
	 * Method: olViewerOnDigitizedFeatureDragDropEnd
	 * Funzione chiamata alla fine di una azione di drag and drop su una feature già digitalizzata in precedenza
	 * Parameters:
	 * geom - {} la geometria.
	 */
	olViewerOnDigitizedFeatureDragDropEnd: function (geom) {
		this.fireEvent('onDigitizedFeatureDragDropEnd', this.olViewerGeometryToGeometry(geom));
	},
	
	/**
	 * Method: pluginSetDistanceFromRef
	 * Set the distance from the reference point
	 * 
	 * Prameters:
	 * distance - {} the distance
	 */
	pluginSetDistanceFromRef: function(distance){
		this.mapControlsDrawLayer['pointFromRef'].handler.setDistance(distance);
	},
	
	/**
	 * Method: getDefaultZoomLevels
	 *
	 * Returns:
	 *     Il valore di zoom di default.
	 */
	getDefaultZoomLevels : function(){			
		var defaultZoomLevels = this.paramsJS.mappe.zoomLevels.split(",");
		var zoomLevels = [];
		if(defaultZoomLevels){
			for(var i = 0; i<defaultZoomLevels.length; i++){
				var defaultZoomLevel = parseInt(defaultZoomLevels[i]); 
				if(defaultZoomLevel <= this.paramsJS.mappe.minScale && defaultZoomLevel >= this.paramsJS.mappe.maxScale)
					zoomLevels.push(defaultZoomLevel);
			}
		}			
		
		return zoomLevels;
	},
	
		
	/**
	 * Method: pluginGetCoordinateActivate
	 * Esegue il necessario per attivare la rilevazione delle coordinate
	 */
	/*
	pluginGetCoordinateActivate : function(){
		this.map.events.register("click", this, this.notifyCoordinate);
	},
	*/
	/**
	 * Method: notifyCoordinate
	 * Notifica il cambio/rilevazione di coordinate
	 */
	/*
	notifyCoordinate: function(evt){
		var mousexy = evt.xy;
		var lonlat = this.map.getLonLatFromPixel(mousexy);
		var coords = {x:lonlat.lon,y:lonlat.lat};
		this.fireEvent('onCoordinateChange', mousexy, coords, this.pluginGetProjectionCode());
	},
	*/
	/**
	 * Method: pluginGetCoordinateDeactivate
	 * Esegue il necessario per disattivare la rilevazione delle coordinate
	 */
	/*
	pluginGetCoordinateDeactivate : function(){		
		this.map.events.unregister("click", this, this.notifyCoordinate);
	},
	*/
	
	/**
	 * Method: pluginGetProjectionCode
	 * @return {String} restituisce il codice EPSG del sistema di riferimento
	 */
	pluginGetProjectionCode: function(){
		return this.map.getProjection() || this.map.projection;
	},

	// Gestione Google Streetview
	bindToStreetviewViewer: function(streetviewViewer) {
		if (streetviewViewer) {	
			streetviewViewer.on('onPositionChanged', this.pluginUpdateStreetviewPosition, this);
			streetviewViewer.on('onPovChanged'     , this.pluginUpdateStreetviewPosition, this);
			streetviewViewer.on('onLinksChanged'   , this.pluginStreetviewDrawNavLinks  , this);
		}
	},
	
	
	pluginAddStreetviewLayers: function() {
		
		var me = this;		
		
		// Layer del marker: contenete la posizione relativa alla scena streetview visualizzata e il simbolo utilizzato indica anche la direzione di visualizzazione
		this.streetViewMarkerLayer = new OpenLayers.Layer.Vector("Google Streetview Marker", {
        	styleMap: new OpenLayers.StyleMap({
            	"default": {
                    externalGraphic: this.TOLOMEOServer  + this.TOLOMEOStaticRoot + 'img/streetview/tool.png',
                    graphicHeight: 32,
                    graphicWidth: 32,
                    graphicOpacity: 0.8,
                    rotation: "${yaw}"
                },
                "select": {
                    cursor: "pointer"
                }
            })
		});		
		
        // Layer contenete le freccette che indicano in quale direzione è possibile navigare con streetview
		this.streetViewNavigationLinkLayer = new OpenLayers.Layer.Vector("Google Streetview Navigation Links", {
            styleMap: new OpenLayers.StyleMap({
                "default": {
                    externalGraphic: this.TOLOMEOServer +  this.TOLOMEOStaticRoot + 'img/streetview/link.png',
                    graphicHeight: 24,
                    graphicWidth: 16.5,
                    graphicYOffset: -44,
                    graphicOpacity: 0.8,
                    rotation: "${angle}"
                },
                "temporary": {
                    cursor: "pointer",
                    externalGraphic: this.TOLOMEOServer +  this.TOLOMEOStaticRoot + '/img/streetview/link_selected.png'
                }
            })
        });
		
		this.map.addLayer(this.streetViewMarkerLayer);
		this.map.addLayer(this.streetViewNavigationLinkLayer);

		// Controllo DragFeature per drag drop punto di panorama
        this.streetViewDragControl = new OpenLayers.Control.DragFeature(this.streetViewMarkerLayer, {
            onComplete: function(feature, pixel) {
		    	var position = me.map.getLonLatFromPixel(pixel);
		        var currPoint = new Point(position.lon,position.lat);	
		        // riproietto le coordinate
		        currPoint.transform(me.pluginGetProjectionCode(),"EPSG:4326");        
		        me.fireEvent('onStreetviewDropComplete', currPoint.x ,currPoint.y);
        	}
        });
                      
        
        var streetviewDelegatorWidget = Ext.create('TolomeoExt.ToloViewerOLPanel.SelectFeatureControlManager.DelegatorWidget',{
	    	id : 'streetview',
	    	layers : [this.streetViewNavigationLinkLayer,this.streetViewMarkerLayer],
	    	callbacks: {
		    	onSelect : function(layerName,feature,viewer){
		    		viewer.fireEvent('onStreetviewNavLinkClick', feature.attributes.panoId);
		    	},	    	
		    	onDeactivate : function(viewer) {
		    		viewer.streetViewNavigationLinkLayer.destroyFeatures();
		    		viewer.streetViewMarkerLayer.destroyFeatures();
		    	}
	    	},
	    	moreControls : [this.streetViewDragControl]
	    });
        	
        this.routingControlManager.addDelegator(streetviewDelegatorWidget);

	},
	
	
	/*
	_addSelectFeatureControl: function(layers, options) {
		var allLayers = layers.concat(this.selectFeatureCurrLayers);
		var ctrl =  new OpenLayers.Control.SelectFeature(allLayers, options);
		this.selectFeatureCurrLayers = allLayers;
		
		return ctrl;
	},*/
	/*
	pluginRemoveStreetviewLayers: function() {
		
		this.streetViewDragControl.deactivate();
		this.streetviewSelectControl.deactivate();
		this.streetviewHighlightCtrl.deactivate();
		
		this.map.removeLayer(this.streetViewMarkerLayer);
		this.map.removeControl(this.streetViewDragControl);
		this.map.removeLayer(this.streetViewNavigationLinkLayer);
		this.map.removeControl(this.streetviewSelectControl);
		this.map.removeControl(this.streetviewHighlightCtrl);
	},
	*/
	pluginUpdateStreetviewPosition: function(lon, lat, heading) {
		
			// Destroy the existing features
	        this.streetViewMarkerLayer.destroyFeatures();
	        
	        var currPoint = new Point(lon,lat);			
			// riproietto le coordinate
	        currPoint.transform("EPSG:4326",this.pluginGetProjectionCode());
	        // Compute the new position
	        var pos = new OpenLayers.Geometry.Point(currPoint.x, currPoint.y);
	        
	        // Add a vector feature in navigation layer
	        var panomarker = new OpenLayers.Feature.Vector(pos, {yaw: heading});
	        this.streetViewMarkerLayer.addFeatures([panomarker]);
	        
	        //TODO l'evento linkschanged scatta automaticamente quando cambia posizione? In questo caso questa chiamata è inutile 
	        //this.pluginStreetviewDrawNavLinks();
	   
			// DA FARE
			// controllre anche se pos cambiata per evitare inutili
			
	}, 
	
	pluginStreetviewDrawNavLinks: function(links, position) {
		
        //var links = this.panorama.getLinks();
		//var position = this.panorama.getPosition();
		
		// Destroy the existing features
        this.streetViewNavigationLinkLayer.destroyFeatures();
        // Add new link symbols
        this.navigationLinks = [];
        if (links) {
            for (var i = 0; i < links.length; i++) {
                var link = links[i];
                
                //////////////////////////////////////////////////////////////////
    	        var centerPoint = new Point(position.lng(),position.lat());    			
    			// riproietto le coordinate
    	        centerPoint.transform("EPSG:4326",this.pluginGetProjectionCode());
    	        // Compute the new position
    	        var centerPosition = new OpenLayers.Geometry.Point(centerPoint.x, centerPoint.y);
    	        
    			//////////////////////////////////////////////////////////////////
                
                //TODO chiamare proj4js con funzioni lazy di federico o meglio funzione su api
    	        /*
                var centerPosition = new OpenLayers.Geometry.Point(position.lng(), position.lat());
                centerPosition.transform(new OpenLayers.Projection("EPSG:4326"), this.map.getProjectionObject());
                */
                // Add a vector feature as navigation link
		        this.navigationLinks.push(new OpenLayers.Feature.Vector(centerPosition, {angle: link.heading, panoId: link.pano}));
            }
            if (this.navigationLinks.length > 0) {
      	        this.streetViewNavigationLinkLayer.addFeatures(this.navigationLinks);
            }
        }
    },
    
    pluginGetCoordinateFromPixel: function(pixelxy){
    	var lonlat = this.map.getLonLatFromPixel(pixelxy);
    	return {x:lonlat.lon, y:lonlat.lat};
    },
    
    pluginGetPixelFromCoordinate: function(lonlat){
    	return this.map.getPixelFromLonLat(lonlat);    	
    },
    
    /**
	* Method: pluginRoutingActivate
	* Activate routing controls.
	*/
    pluginRoutingActivate: function() {
    	this.routingControlManager.activateDelegator('routing');    	
    },
    
    /**
	* Method: pluginRoutingDeactivate
	* Deactivate routing controls.
	*/
    pluginRoutingDeactivate: function() {
    	this.routingControlManager.deactivateDelegator('routing');
    },
    
    /**
	* Method: pluginRoutingActivate
	* Deactivate streetview controls.
	*/
    pluginStreetViewActivate: function() {
		this.routingControlManager.activateDelegator('streetview');
	},
	
	/**
	* Method: pluginRoutingDeactivate
	* Deactivate streetview controls.
	*/
    pluginStreetViewDeactivate: function() {
    	this.routingControlManager.deactivateDelegator('streetview');	
	},
	
	
    
	/**
     * Restituisce la larghezza di questo viewer
     */
	pluginGetMapViewerWidth: function() {
    	return this.getWidth();
    	
    },
    
    /**
     * Restituisce l'altezza di questo viewer
     */
    pluginGetMapViewerHeight: function() {
    	return this.getHeight();
    	
    },
    
    /**
     * Restituisce la larghezza dell'oggetto mappa incluso in questo viewer
     */
    pluginGetMapWidth: function() {
    	return this.map.size.w;
    	
    },
    
    /**
     * Restituisce l'altezza dell'oggetto mappa incluso in questo viewer
     */
    pluginGetMapHeight: function() {
    	return this.map.size.h;
    	
    },
    
    pluginGetMapRatio: function() {
    	var ratio = this.map.baseLayer.ratio;
    	return (ratio) ? ratio : 1;
    	
    },

    //getItemUrlFromViewer
    /**
     * Restituisce l'url necessaria per richiedere l'immagine corrispondente alla posizione/zoom attuali ad un server/layer alternativo indicato in itemParams
     *
     * @param itemParams {Object} contiene url, layer, tipo di server ed altri parametri relativi al server per il quale deve essere predisposta l'url
     * @param mapWidth larghezza della mappa da richiedere
     * @param mapHeight altezza della mappa da richiedere
     * 
     *    
     */
    pluginServerUrl: function (itemParams, mapWidth, mapHeight ) {
		var retVal = '';
		
		// Recupero parametri necessari da viewer
	    var fullBounds = this.pluginGetMapFullExtent();
	    var viewerFullUrl = this.pluginGetMapUrl(fullBounds);
	    var mapDPI = this.pluginGetDotsPerInch();
	    
	    if ( itemParams.tipo==undefined || itemParams.tipo==null || itemParams.tipo=='') {
	    	itemParams.tipo = 'mapserver';
	    }
	    
	    switch (itemParams.tipo) {	    
		    case 'mapserver':
		    	// url     ----- http://dvptolomeo.comune.prato.it/cgi-bin/mapserv?   
		    	// mappa   ----- map=/usr1/test/vh/tolomeo/mapfiles/stradario.map
		    	// layer   ----- layers=undefined%20circoscrizioni%20fiumi_laghi%20parchi%20edifici%20poligoni_strade%20piste_ciclabili%20cartelli_stradali%20numeri_civici
		    	// formato -----   map_imagetype=agga
		    	// map_resolution=72
		    	// mode=map 
		    	// fullbounds ----- mapext=1668361.0250593+4860744.3379923+1668543.5334328+4860867.2330781
		    	// fullbounds ----- imgext=1668361.0250593+4860744.3379923+1668543.5334328+4860867.2330781
		    	// ???? map_size=1194+804  // calcolare da this.mapViewerRatio etc?
		    	// ?? punto centrale ?? imgx=597&imgy=402
		    	// ???? imgxy=1194+804
		    	
		    	// url
		    	retVal = (itemParams.url && itemParams.url != "") ? itemParams.url :   
		    															(viewerFullUrl.indexOf('?')!=-1) ? viewerFullUrl.substring(0, viewerFullUrl.indexOf('?')) : viewerFullUrl;
		    	retVal += (retVal.indexOf('?')==-1) ?  "?" : "&";
		    	// mappa
		    	retVal += "map=" + escape(itemParams.mappa) + "&";
		    	// layer
		    	retVal += "layers=" + escape(itemParams.layer) + "&";
		    	//formato
		    	retVal += "map_imagetype=" + ((itemParams.formato && itemParams.formato!="") ? escape(itemParams.formato) : "agga") + "&";
		    	// resolution
		    	retVal += "map_resolution=" + mapDPI + "&";
		    	// mode
		    	retVal += "mode=map&";
		    	// mapext
		    	retVal += "mapext=" + fullBounds.left + "+" + fullBounds.bottom + "+" + fullBounds.right + "+" + fullBounds.top + "&";
		    	// imgext
		    	retVal += "imgext=" + + fullBounds.left + "+" + fullBounds.bottom + "+" + fullBounds.right + "+" + fullBounds.top + "&";
		    	// map_size
		    	retVal += "map_size=" + mapWidth + "+" + mapHeight + "&";
		    	// imgx e imgy
		    	retVal += "imgx=" +  Math.round(mapWidth/2) + "&";
		    	retVal += "imgy=" +  Math.round(mapHeight/2)+ "&";
		    	// imgxy
		    	retVal += "imgxy=" + mapWidth + "+" + mapHeight ;
		    	
		    	break;
		    	
		    case 'WMS':
		    	// http://geoserver.comune.prato.it/geoserver/wms?
		    	// LAYERS=comunepo_generica
		    	// MINZOOMLEVEL=0
		    	// SERVICE=WMS
		    	// VERSION=1.1.1
		    	// REQUEST=GetMap
		    	// STYLES=
		    	// EXCEPTIONS=application%2Fvnd.ogc.se_inimage
		    	// FORMAT=image%2Fjpeg
		    	// SRS=EPSG%3A3003
		    	// BBOX=1630695.3952703,4847788.25,1703123.6047297,4873514.75
		    	// WIDTH=1875
		    	// HEIGHT=666
		    	
		    	// url
		    	retVal = (itemParams.url && itemParams.url != "") ? itemParams.url :   
		    															(viewerFullUrl.indexOf('?')!=-1) ? viewerFullUrl.substring(0, viewerFullUrl.indexOf('?')) : viewerFullUrl;
		    	retVal += (retVal.indexOf('?')==-1) ?  "?" : "&";
		    	// layer
		    	retVal += "LAYERS=" + escape(itemParams.layer) + "&";
		    	//MINZOOMLEVEL
		    	retVal += "MINZOOMLEVEL=0&";
		    	//SERVICE
		    	retVal += "SERVICE=WMS&";
		    	//VERSION
		    	retVal += "VERSION=1.1.1&";
		    	// REQUEST
		    	retVal += "REQUEST=GetMap&";
		    	// STYLES
		    	retVal += "STYLES=" + itemParams.styles + "&";
		    	// EXCEPTIONS
		    	retVal += "EXCEPTIONS=application%2Fvnd.ogc.se_inimage&";
		    	//formato
		    	retVal += "FORMAT=" + ((itemParams.formato && itemParams.formato!="") ? escape(itemParams.formato) : "image%2Fjpeg") + "&";
		    	// SRS
		    	retVal += "SRS=" + ((itemParams.srid && itemParams.srid!="") ? escape(itemParams.srid) : escape(this.pluginGetSRID()))  + "&";
		    	// bbox
		    	retVal += "BBOX=" + fullBounds.left + "," + Math.round(fullBounds.bottom) + "," + fullBounds.right + "," + fullBounds.top + "&";
		    	// WIDTH ed HEIGHT
		    	retVal += "WIDTH=" + mapWidth + "&HEIGHT=" + mapHeight ;
		    	break;	
	    
	    }
		return retVal;
	},
    
	pluginGetObjPos: function () {
    	
    	return {
    		srid:   this.pluginGetSRID(),
    		bbox:   this.pluginGetMapExtent(),
    		width:  this.pluginGetMapWidth(),
    		height: this.pluginGetMapHeight() 
    	}
    	
	}, 
	
	   
	/**
	 * Method: pluginAddRouting
	 * Funzione che viene chiamata per aggiungere un oggetto geometrico a quelli del routing.
	 * Se bMulti non definito o false un solo oggetto e' evidenzxiabile, quindi cancella eventuali oggetto selezionati in precedenza
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicatoad un certo viewer.
	 *
	 * Parameters:
	 * routing - {RouteResponse} reouting da aggiungere
	 * bMulti - {Boolean} bMulti Se non definito o false non è consentita la presenza di più di un oggetto, se True è consentita.
	 */
	pluginAddRouting: function (routing, bMulti, style) {
		
		var jsGeometry = routing.geometry;
		
		if (!bMulti) this.routingLayer.destroyFeatures();
		/* Modificato per non tracciare la feature completa ma i singoli pezzi per rednerli selezionabili
		 
		var geoms = new JSGeometryArray();
		
		if (jsGeometry instanceof JSGeometryArray) {
			geoms = jsGeometry;
		} else if (jsGeometry instanceof JSGeometry) {
			geoms.add(jsGeometry);
		} else {
			alert("tipo sconosciuto " + jsGeometry);
			return;
		}
		
		var feats = new Array();
		
		for (var i=0; i<geoms.geometries.length; i++) {
			var wkt = new OpenLayers.Format.WKT();
			var feat = wkt.read(geoms.geometries[i].geometry);
			if (style) feat.style = style;
		    feats.push( feat);
		}
		//ALE	this.routingLayer.addFeatures(feats);
		*/
		
		for (var i = 0; i < routing.instructions.length; i++) {
			this._routingInstructionAdd(routing.instructions[i].geometry, { tooltip: routing.instructions[i].textInstruction }, i!=0, routing.instructions[i].instructionId);
		}
		
	},
	
	pluginRoutingInstructionHighlight: function (instruction, bHighlight) {
		
		var feats = this.routingLayer.getFeaturesByAttribute("instructionId", instruction.instructionId);
		for (var i=0; i<feats.length; i++) {
			this.routingLayer.drawFeature(feats[i], (bHighlight) ? "highlight" : "default");	
		}

	},
		
	_routingInstructionAdd: function(jsGeometry, opts, withNode, instructionId) {
		var wkt = new OpenLayers.Format.WKT();
		var feat = wkt.read(jsGeometry.geometry);
		feat.attributes.instructionId = instructionId;
		
		// Aggiunta feature lineare
		this.routingLayer.addFeatures([feat]);
		
		if (withNode) {
			var startPoint = feat.geometry.getVertices(true)[0];
			
			var o = opts || {};
			
			TolomeoExt.applyIfEmpty(o, {
				icon	: this.TOLOMEOServer + this.TOLOMEOStaticRoot + 'img/ols/routeInfo.png',
				widht	: 11,
				height	: 11,
				xOffset	: -5,
				yOffset	: -5,
				tooltip	: ToloI18n.getMsg("ToloViewerOLPanel._routingInstructionAdd.tooltip")
			});
			
			var infoFeature = new OpenLayers.Feature.Vector(startPoint, 
					{	instructionId   : instructionId }, 
					{
						externalGraphic	: o.icon,
						graphicWidth	: o.widht,
						graphicHeight	: o.height,
						graphicXOffset	: o.xOffset,
						graphicYOffset	: o.yOffset,
						title			: o.tooltip
					});
			
			// Aggiunta nodo
			this.routingLayer.addFeatures([infoFeature]);	
		}
		
	},
	
	pluginZoomToInstruction: function(jsGeometry, zoomFactor) {
		var wkt = new OpenLayers.Format.WKT();
		var feat = wkt.read(jsGeometry.geometry);
		var startPoint = feat.geometry.getVertices(true)[0];
		
		this.pluginGotoPosition(startPoint.x, startPoint.y, zoomFactor, false);  
	},
	
	/**
	 * Method: pluginClearRouting
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * bRedraw - {} bRedraw
	 * 
	 * Returns:
	 *     {Boolean}
	 */
	pluginClearRouting: function (bRedraw) {
		// non viene gestito bRedraw perche' in questo viewer la cancellazione non richiede il ridisegno
	    this.routingLayer.destroyFeatures();
	    // ritorna sempre false perch? il redraw non e' mai necessario
	    return false;
	},
	
	/**
	 * Method: pluginZoomToRouting
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * zoom - {} zoom
	 * buffer - {Number} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	*/
	pluginZoomToRouting: function (zoom, buffer) {
		//var bounds = this.routingLayer.features[0].geometry.getBounds();
		if(!(this.routingLayer.features && this.routingLayer.features.length>0)) return;
		var bounds = this.olGetExtents(this.routingLayer.features);
		if (!zoom) {
			if (buffer && this.isMetresUnits()) {
				boundsBuffered = new OpenLayers.Bounds();
			    boundsBuffered.extend(new OpenLayers.LonLat(bounds.left-buffer/2,bounds.bottom-buffer/2));
			    boundsBuffered.extend(new OpenLayers.LonLat(bounds.right+buffer/2,bounds.top+buffer/2));
			    // Estende il buffer fino a includere anche un buffer intorno ai marker
			    var b = this.routingMarkersLayer.getDataExtent();
			    boundsBuffered.extend(new OpenLayers.LonLat(b.left-buffer/2,b.bottom-buffer/2));
			    boundsBuffered.extend(new OpenLayers.LonLat(b.right+buffer/2,b.top+buffer/2));
			    
			    this.map.zoomToExtent(boundsBuffered);
			} else {
				this.map.zoomToExtent(bounds);
			}
		} else {
			var lonLat = bounds.getCenterLonLat();
			this.pluginGotoPosition(lonLat.lon, lonLat.lat, zoom, false);	
		}
		
		this.isAlreadyDrawn=true;	
	},
	     
	/**
	 * Method: pluginAddStartRoutingMarker
	 * Aggiunge il marker di inizio tracciato per il routing
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	 * Parameters:
	 * lon - {} longitudine
	 * lat - {} latitudine
	 * opts - {} opzioni
	 */
	pluginAddStartRoutingMarker: function(lon, lat, opts) {
		
		if (this.startMarker != null) {
			this.pluginClearStartRoutingMarker();
			//this.startMarker.geometry = new OpenLayers.Geometry.Point(lon, lat);
			//return;
		}
		
		var o = opts || {};
		
		TolomeoExt.applyIfEmpty(o, {
			icon	: this.TOLOMEOServer + this.TOLOMEOStaticRoot + 'img/ols/startPoint.png',
			widht	: 24,
			height	: 37,
			xOffset	: -12,
			yOffset	: -37,
			tooltip	: ToloI18n.getMsg("ToloViewerOLPanel.pluginAddStartRoutingMarker.tooltip")
		});
				
		this.startMarker = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Point(lon, lat), {}, {
			externalGraphic	: o.icon,
			graphicWidth	: o.widht,
			graphicHeight	: o.height,
			graphicXOffset	: o.xOffset,
			graphicYOffset	: o.yOffset,
			title			: o.tooltip 
		});
		
		this.routingMarkersLayer.addFeatures( [this.startMarker] );
		this.pluginRoutingActivate();
	},
	
	/**
	 * Method: pluginClearViaRoutingMarkers
	 * Elimina tutti i marker di tappa intermedia tracciato per il routing
	 * Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	 *
	*/
	pluginClearStartRoutingMarker: function() {
		if (this.startMarker != null) {
			this.routingMarkersLayer.destroyFeatures(this.startMarker);
			this.startMarker = null;
		}
	},
		
	/**
	* Method: pluginAddEndRoutingMarker
	* Aggiunge il marker di fine tracciato per il routing
	* Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	*
	* Parameters:
	* lon - {} longitudine
	* lat - {} latitudine
	* opts - {} opzioni
	*/
	pluginAddEndRoutingMarker: function(lon, lat, opts) {
		if (this.endMarker != null) {
			this.pluginClearEndRoutingMarker();
			//this.endMarker.geometry = new OpenLayers.Geometry.Point(lon, lat);
			//return;
		}
		
		var o = opts || {};
		
		TolomeoExt.applyIfEmpty(o, {
			icon	: this.TOLOMEOServer + this.TOLOMEOStaticRoot + 'img/ols/endPoint.png',
			widht	: 24,
			height	: 37,
			xOffset	: -12,
			yOffset	: -37,
			tooltip	: ToloI18n.getMsg("ToloViewerOLPanel.pluginAddEndRoutingMarker.tooltip")
		});
			
		this.endMarker = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Point(lon, lat), {}, {
			externalGraphic	: o.icon,
			graphicWidth	: o.widht,
			graphicHeight	: o.height,
			graphicXOffset	: o.xOffset,
			graphicYOffset	: o.yOffset, 
			title			: o.tooltip
		});
		
		this.routingMarkersLayer.addFeatures( [this.endMarker] );		
	},
	
   /**
	* Method: pluginClearViaRoutingMarkers
	* Elimina tutti i marker di tappa intermedia tracciato per il routing
	* Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	*
	*/
	pluginClearEndRoutingMarker: function() {
		if (this.endMarker != null) {
			this.routingMarkersLayer.destroyFeatures(this.endMarker);
			this.endMarker = null;
		}
	},
	
	/**
	* Method: pluginAddViaRoutingMarker
	* Aggiunge il marker di tappa intermedia tracciato per il routing
	* Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	*
	* Parameters:
	* lon - {} longitudine
	* lat - {} latitudine
	* opts - {} opzioni
	*/
	pluginAddViaRoutingMarker: function(id, lon, lat, opts) {
		var o = opts || {};
		
		TolomeoExt.applyIfEmpty(o, {
			icon	: this.TOLOMEOServer + this.TOLOMEOStaticRoot + 'img/ols/viaPoint.png',
			widht	: 24,
			height	: 37,
			xOffset	: -12,
			yOffset	: -37,
			tooltip	: ToloI18n.getMsg("ToloViewerOLPanel.pluginAddViaRoutingMarker.tooltip")
		});
			
		var viaMarker = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Point(lon, lat), {
			id: id
		}, {
			externalGraphic	: o.icon,
			graphicWidth	: o.widht,
			graphicHeight	: o.height,
			graphicXOffset	: o.xOffset,
			graphicYOffset	: o.yOffset,
			title			: o.tooltip
		});  
			 
		this.routingMarkersLayer.addFeatures( [viaMarker] );		
		this.viaMarkers.push(viaMarker);
	},
		
	/**
	* Method: pluginClearViaRoutingMarkers
	* Elimina tutti i marker di tappa intermedia tracciato per il routing
	* Come tutte le funzioni con prefisso 'plugin' deve essere implementata in un plugin dedicato ad un certo viewer.
	*
	*/
	pluginClearViaRoutingMarkers: function() {
		this.routingMarkersLayer.destroyFeatures( this.viaMarkers );
		this.viaMarkers = [];
	},
		
	/**
	* Method: olViewerOnDragDropEndRouting
	* Metodo privato per la gestione dell'evento di drag delle geometrie di routing
	*
	* Parameters:
	* feature - {OpenLayers.Feature.Vector} feature soggetta a modifica
	*/
		olViewerOnDragDropEndRouting: function(feature)	{
			var	event;
			var startPoint = null;
			var endPoint = null;
			var viaPoint = null;
			var viaId = null;
			
			if (feature == this.startMarker) {
				event = 'startPointMoved';
				startPoint = this.startMarker ? new Point(this.startMarker.geometry.x, this.startMarker.geometry.y) : null;
			} else if (feature == this.endMarker) {
				event = 'endPointMoved';
				endPoint = this.endMarker ? new Point(this.endMarker.geometry.x, this.endMarker.geometry.y) : null;
			} else {
				event = 'viaPointMoved';
				viaPoint = new Point(feature.geometry.x, feature.geometry.y);
				viaId = feature.attributes.id;
			}
			
			this.fireEvent(event, startPoint, endPoint, viaPoint, viaId);
		}
    	
});

/**
 * Class: TolomeoExt.ToloViewerOLPanel.SelectFeatureControlManager
 * 
 * Clas to manage select feature for more layers
 *
 * Inherits from:
 *  - <Ext.Base>
 *
 */
Ext.define('TolomeoExt.ToloViewerOLPanel.SelectFeatureControlManager', {

    /**
     * @cfg {Object} map
     * The map is the Openlayers Map object with which to work
     */
	
    /**
     * @cfg {Object} viewer
     * The viewer is the TolomeoExt.ToloViewerOLPanel
     */

    /**
     * Creates new SelectFeatureManager.
     * @param {Object} config Config object.
     */
    constructor : function(config){
        this.initialConfig = config;
        this.map = config.map;
        this.viewer = config.viewer;        
        this.init();
    },
    
    layersMap : {},
	delegatorMap : {},
	selectControl: null, 	    		
	highlightControl: null,
	
	init: function(){

		var me = this;
		var viewer = this.viewer;
		
		this.selectControl = new OpenLayers.Control.SelectFeature(
                [],
                {
                	renderIntent: 'select',
                	
                	onBeforeSelect: function(feature){		 
                    	var callback = me.layersMap[feature.layer.name].callbacks.onBeforeSelect;
                		return callback ? callback(feature.layer.name,feature,viewer) : true;
                	},
                
                    onSelect: function(feature){
                    	var callback = me.layersMap[feature.layer.name].callbacks.onSelect;
                    	if(callback){
                    		callback(feature.layer.name,feature,viewer);
                    	}
                    },
                    
                    onUnselect: function(feature){
                    	var callback = me.layersMap[feature.layer.name].callbacks.onUnselect;
                    	if(callback){
                    		callback(feature.layer.name,feature,viewer);
                    	}
                    }                                                           
                }
        );
		
		this.highlightControl = new OpenLayers.Control.SelectFeature(
                [],
                {	                         
                    multiple:false, 
                    hover:true, 
                    highlightOnly:true,
                    renderIntent: 'temporary',
                    
                    eventListeners:{
                    	
                    	beforefeaturehighlighted : function(event){
                    		var callback = me.layersMap[event.feature.layer.name].callbacks.onBeforeHighlight;
                    		return callback ? callback(event.feature.layer.name,event.feature,viewer) : true;
                    	},                  
                    	
                        featurehighlighted: function (event) {
                        	var callback = me.layersMap[event.feature.layer.name].callbacks.onHighlight;
                        	if(callback){
                        		callback(event.feature.layer.name,event.feature,viewer);
                        	}
                        },
                        
                        featureunhighlighted: function (event) {
                        	var callback = me.layersMap[event.feature.layer.name].callbacks.onUnhighlight;
                        	if(callback){
                        		callback(event.feature.layer.name,event.feature,viewer);
                        	}
                        }
                    }	                           
                }
        );
		
		this.map.addControl(this.selectControl);
        this.map.addControl(this.highlightControl);
        
	},
	
	addDelegator : function(d){	  
		
		d.active = false;
		this.delegatorMap[d.id] = d;
		
		for(var i=0; i < d.layers.length ; i++){
			this.layersMap[d.layers[i].name] = d;			
		}
		
		if(d.moreControls){
			for(var i=0; i<d.moreControls.length; i++){								
				this.map.addControl(d.moreControls[i]);
			}
		}
	},
	
	activateDelegator : function(idP){		
		
		var delegator = this.delegatorMap[idP];
		if(!delegator.active){	    				
			this.deactivate();
			delegator.active = true;
			var layers = [];
			
			for(var i in this.delegatorMap){
				if(!this.delegatorMap.hasOwnProperty(i)) continue;
				var d = this.delegatorMap[i];
				if(d.active){
					layers = layers.concat(d.layers);
				}
			}
			
			this.selectControl.setLayer(layers);
			this.highlightControl.setLayer(layers);
			this.activate();
			
			if(delegator.callbacks.onActivate){
				delegator.callbacks.onActivate(this.viewer);
			}
		}
	},
	
	deactivateDelegator : function(idP){
		
		var delegator = this.delegatorMap[idP];
		
		if(delegator.active){	    	
			
			this.deactivate();
			delegator.active = false;
			var layers = [];
			
			for(var i in this.delegatorMap){
				if(!this.delegatorMap.hasOwnProperty(i)) continue;
				
				var d = this.delegatorMap[i]; 
				if(d.active){
					layers = layers.concat(d.layers);
				}	    					
			}
			
			if(layers.length > 0) {
				this.selectControl.setLayer(layers);
				this.highlightControl.setLayer(layers);				
				this.activate();
			} 
			
			if(delegator.callbacks.onDeactivate){
				delegator.callbacks.onDeactivate(this.viewer);
			}
		}
	},
	
	activate : function(){	    	
		
		this.highlightControl.activate();
		this.selectControl.activate();
		
		// Activate further controls of all active delegator
		for(var i in this.delegatorMap){
			if(!this.delegatorMap.hasOwnProperty(i)) continue;
			
			var d = this.delegatorMap[i];
			if(d.active && d.moreControls){
				for(var c=0; c<d.moreControls.length; c++){
					d.moreControls[c].activate();
				}
			}	    					
		}
		
	},
	
	deactivate : function(){
	
		// Deactivate further controls of all active delegator
		for(var i in this.delegatorMap){			
			if(!this.delegatorMap.hasOwnProperty(i)) continue;
			
			var d = this.delegatorMap[i];
			
			if(d.active && d.moreControls){
				for(var c=0; c<d.moreControls.length; c++){					
					d.moreControls[c].deactivate();				    				    				   
				}
			}	    					
		}
		
		this.selectControl.deactivate();
		this.highlightControl.deactivate();
	}
});


Ext.define('TolomeoExt.ToloViewerOLPanel.SelectFeatureControlManager.DelegatorWidget', {
	
	/**
     * @cfg {String} id
     * The unique id of this widget
     */
	
	/**
     * @cfg {Array} layers
     * The list of layers of the widget. 
     * To see the correct things whene feature of a layer are selected or highlighted, you need to define a styleMap for the layer
     * with the following keys
     * 	- default	(for normal style)
     * 	- select	(for selected style)
     *  - highlight	(forn highlighted style)
     */
	
    /**
     * @cfg {Object} callbacks
     * The viewer is the TolomeoExt.ToloViewerOLPanel
     */

	/**
     * @cfg {Object} moreControls
     * Further controls of the widget that Select Feature Control Manager have to activate or deactivate 
     * when SelectFeature ia activated or deactivated.
     * If there are further controls, but you don't pass them to the Select Feature Control Manager
     * you have to handle them by yourself
     */
	
	/**
     * @cfg {Object} callbacks
     * Object with all callbacks for the SelectFeatureControlManager
     * - onActivate 		: called when widget is activated
     * - onBeforeSelect 	: called before select a feature (return false to avoid selection)
     * - onSelect 			: called after feature selection
     * - onUnselect 		: called after deselection
     * - onBeforeHighlight	: called before highlight a feature (return false to avoid highlighting)
     * - onHighlight 		: called after highlighted
     * - onUnhighlight 		: called after unhighlight
     * - onDeactivate 		: called after widget is deactivated to finalize it
     */
	
    /**
     * Creates new SelectFeatureManager.
     * @param {Object} config Config object.
     */
    constructor : function(config){
        this.initialConfig = config;
        this.id = config.id;
        this.layers = config.layers;
        this.moreControls = config.moreControls;
        this.callbacks = config.callbacks;        
    }
});/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.namespace("TolomeoExt");

/**
 * Method: TolomeoExt.ToloAPIOpCodes
 *  
 */	
TolomeoExt.ToloAPIOpCodes = new function() {
	var contaBottoni = 0;
	
 	this.btnNuovo          = contaBottoni++;
 	this.btnNuovoDaLayer   = contaBottoni++;
 	this.btnNuovoDaImport  = contaBottoni++;
 	this.btnDelete         = contaBottoni++;
 	this.btnAdd            = contaBottoni++;
 	this.btnSubtract       = contaBottoni++;
 	this.btnAddSub         = contaBottoni++;
 	this.btnUpdateAlfa     = contaBottoni++;
 	this.btnIdentify       = contaBottoni++;
 	this.btnVertexEdit     = contaBottoni++;
 	this.btnDragDrop       = contaBottoni++;
 	this.btnTemporalFilter = contaBottoni++;
 	this.btnAutoIdentify   = contaBottoni++;

 	this.btnActionPanel2Max = contaBottoni-1;

 	this.btnLegenda	  = contaBottoni++;
 	this.btnRicerca   = contaBottoni++;
 	this.btnPan 	  = contaBottoni++;
 	this.btnPanNord   = contaBottoni++;
 	this.btnPanEst 	  = contaBottoni++;
 	this.btnPanSud 	  = contaBottoni++;
 	this.btnPanOvest  = contaBottoni++;
 	this.btnMeasure   = contaBottoni++;
 	this.btnSeleziona = contaBottoni++;
 	this.btnAnnullaSelezioni = contaBottoni++;

 	this.btnZoomIn  = contaBottoni++;
 	this.btnZoomOut = contaBottoni++;
 	this.btnZoomBox = contaBottoni++;
 	this.btnZoomAll = contaBottoni++;
 	
 	this.btnPrint            = contaBottoni++;
 	this.btnTimeMachine      = contaBottoni++;
 	this.btnSnap             = contaBottoni++;
 	this.btnCsw              = contaBottoni++;
 	this.btnWMSExplorer      = contaBottoni++;
 	this.btn3D      		 = contaBottoni++;
 	
 	this.btnHistoryPrev	 	= contaBottoni++;
 	this.btnHistoryNext	 	= contaBottoni++;
 	
 	//this.btnMostraCoordinate = contaBottoni++;

	// lasciare per ultimo (il codice assume che siano customButton quelli >= btnCustomBase
 	this.btnCustomBase = contaBottoni++;
}();/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloStreetviewViewerPanel
 * @extends Ext.Panel
 * Pannello ExtJs contenente un Google Panorama Street View
 * 
 */        
Ext.define('TolomeoExt.ToloStreetviewViewerPanel', {
		
	extend: 'Ext.Panel',
	
	/**
	 * @property {Number} posLon
	 * Longitudine posizione iniziale
	 * 
	 */
	posLon: null,
	
	/**
	 * @property {Number} posLat
	 * Latitudine posizione iniziale
	 * 
	 */
	posLat: null,
	
	/**
	 * @property {Object} heading
	 * Heading iniziale
	 * 
	 */
	heading: null,
	
	/**
	 * @property {Object} pitch
	 * Pitch iniziale
	 * 
	 */
	pitch: null,
	
	/**
	 * @property {Number} zoom
	 * Zoom iniziale
	 * 
	 */
	zoom: null,
	
	/**
	 * @property {Object} streetviewclient
	 * 
	 * 
	 */
	streetviewclient: null,
	
	/**
	 * @property {Object} panorama
	 * 
	 * 
	 */
	panorama: null,
	
	/**
	 * @method initComponent
	 * 
	 * 
	 */
	initComponent : function() {
		
		this.addEvents('onPositionChanged');
		this.addEvents('onPovChanged');
		this.addEvents('onLinksChanged');
		
		if (this.heading==null) this.heading=0;
		if (this.pitch==null) 	this.pitch=0;
		if (this.zoom==null) 	this.zoom=1;
        
        //	Create StreetViewClient for querying information about panorama
		this.streetviewclient = new google.maps.StreetViewService();
		
		this.bindToViewer(this.viewer);
		this.viewer.pluginAddStreetviewLayers();
		this.viewer.bindToStreetviewViewer(this);
		
		this.callParent(arguments);
		
	},
	
	/**
	 * @method bindToViewer
	 * 
	 * 
	 * @param {Object} viewer
	 * 
	 * 
	 */
	bindToViewer: function(viewer) {
		if (viewer) {
			viewer.on('onStreetviewDropComplete', this.setViewPosition, this);
			viewer.on('onStreetviewNavLinkClick', this.getPanoramabyId, this);
		}
	},
	
	/**
	 * @method getPanoramabyId
	 * 
	 * 
	 * @param {Object} panoId
	 * 
	 * 
	 */
	getPanoramabyId: function(panoId) {
		
		var delegateFn = Ext.Function.bind(this.getPanoramaByIdCallback, this);
		this.streetviewclient.getPanoramaById(panoId, delegateFn);
	   //this.streetviewclient.getPanoramaById(panoId, this.getPanoramaByIdCallback.createDelegate(this));
	},
	
	/**
	 * @method getPanoramaByIdCallback
	 * 
	 * 
	 * @param {Object} data
	 * 
	 * 
	 * @param {Object} streetviewstatus
	 * 
	 * 
	 */
	getPanoramaByIdCallback: function (data, streetviewstatus) {
		if (data) {
			if (streetviewstatus == "ZERO_RESULTS") {        //600 nessun panorama
                //this.deleteFeatures();
	    		//this.panorama.setVisible(false);
            } else if (streetviewstatus == "UNKNOWN_ERROR") { //500 errore
                //this.deleteFeatures();
	    		//this.panorama.setVisible(false);
            } else if (streetviewstatus == "OK") {            // 200 OK
            	var pov = {
            		heading: this.panorama.pov.heading,
            		pitch  : this.panorama.pov.pitch,
            		zoom   : this.panorama.pov.zoom
            	};
				this.setViewPosition(data.location.latLng.lng(), data.location.latLng.lat());
				this.panorama.setPov(pov);
            }
		} else {
            //this.deleteFeatures();
    		//this.panorama.setVisible(false);
        }
    },
	
	/**
	 * @method afterRender
	 * 
	 * 
	 */
	afterRender : function() {
		var pos = new google.maps.LatLng(this.posLat, this.posLon);
		var panoramaOptions = {
	    	position: pos,
	    	pov: {
	        	heading: this.heading,
	        	pitch  : this.pitch,
	        	zoom   : this.zoom
			}
	    }
		this.panorama = new google.maps.StreetViewPanorama(this.body.dom,panoramaOptions);
		this.on('resize', function() {google.maps.event.trigger(this.panorama, 'resize');});
		var thiswin = this;
		google.maps.event.addListener(this.panorama, 'position_changed', 
			function() {
				//alert("position_changed");
				var pos = thiswin.panorama.getPosition();
				var heading = thiswin.panorama.getPov().heading;
				thiswin.fireEvent('onPositionChanged', pos.lng(), pos.lat(), heading);
			});

		//Add panorama event listeners
    	google.maps.event.addListener(this.panorama, "pov_changed", 
    		function() {
				var pos = thiswin.panorama.getPosition();
				var heading = thiswin.panorama.getPov().heading;
				thiswin.fireEvent('onPovChanged', pos.lng(), pos.lat(), heading);
			}
    	);
				        
		google.maps.event.addListener(this.panorama, "links_changed", 
			function() {
				var links = thiswin.panorama.getLinks();
				var pos = thiswin.panorama.getPosition()
				thiswin.fireEvent('onLinksChanged', links, pos);
			}
		);

		this.callParent(arguments);
		
	},
	
	/**
	 * @method beforeDestroy
	 * 
	 * 
	 */
    beforeDestroy: function() {
        delete this.panorama;
        this.callParent(arguments);
    },
	
	/**
	 * @method setViewPosition
	 * 
	 * 
	 * @param {Number} lon
	 * 
	 * 
	 * @param {Number} lat
	 * 
	 * 
	 */
	setViewPosition: function (lon, lat) {
		var pos = new google.maps.LatLng(lat, lon);	
		this.panorama.setPosition(pos);
	} 
});
	/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloTOCAbstractDataProvider
 * @extends Ext.util.Observable
 * 
 * 
 */
Ext.define('TolomeoExt.ToloTOCAbstractDataProvider', {

	extend: 'Ext.util.Observable',
 
	/** 
	 * @property {String} TOLOMEOServer
	 * 
	 * 
	 */
	TOLOMEOServer: null,

	/** 
	 * @property {String} TOLOMEOContext
	 * 
	 * 
	 */
	TOLOMEOContext: null,

	/** 
	 * @property {Boolean} [isStyleCapable=false]
	 * Indica se il data provider prevede la gestione degli stili (es. mapserver no, WMS si )  
	 * 
	 */
	isStyleCapable: false,
	
	/**
	 * @constructor
	 * Crea un nuovo TolomeoExt.ToloTOCAbstractDataProvider.
	 *
	 * @param {Object} config
	 * The configuration
	 *
	 * @returns {TolomeoExt.ToloTOCAbstractDataProvider}
	 * Un nuovo TolomeoExt.ToloTOCAbstractDataProvider.
	 * 
	 */
	constructor: function(config) {		
		Ext.apply(this, config);
		//Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);	
		this.callParent();
	},
	
	/**
	 * @method initComponent
	 * 
	 * 
	 */
	initComponent: function(){ 
        this.addEvents('onFullDataRequestEnd');
        this.addEvents('onVisibleDataRequestEnd');
        
        /**
         * @event onRequestLayerInfoDataEnd
         * Viene emesso quando viene ricevuto la risposta ad una richiesta di layerInfo per un nuovo layer WMS
         * 
    	   * @param {Object} layInfo
    	   * struttura layerinfo relativa al WMS
    	   * 
	       * @param {Object} extra
	       * parametri extra
	       * 
         */
        this.addEvents('onRequestLayerInfoDataEnd');
        
		this.callParent(); 
    },

    
  /**
   * @method requestFullData
   * 
   * 
   * @param {Object} options
   * oggetto contenenti i parametri della richiesta.
	 * options.presetName - {string} nome del preset.
	 * options.currentMap - {string} mappa.
	 * options.scale - {number} valore di scala 	
	 * options.presetXML - {String} xml del file di preset.
	 * options.sendPreset - {boolean} abilitazione all'invio del preset
	 * 
	 */
    requestFullData: function(options) {    	
    	 
    },
    
    /**
     * @method fullDataRequestEnd
     * 
     * 
     * @param {Object} records
     * recordset.
     * 
     * @param {Object} opts
     * opts.
     * 
     */
    fullDataRequestEnd: function(records, scale) {
    	this.fireEvent('onFullDataRequestEnd', records, scale);
    },
    
    /**
     * @method requestVisibleData
     * 
     * 
     * @param {Object} nomePreset
     * nomePreset.
     * 
     * @param {Object} mappa
     * mappa.
     * 
     * @param {Object} scale
     * valore di scala.
     * 
     * @param {Object} tocInfo
     * 
     * 
     */
    requestVisibleData: function(nomePreset, mappa, scale, tocInfo) {
	   	
    },
    
    /**
     * @method visibleDataRequestEnd
     * 
     * 
     * @param {Object} opts
     * opts.
     * 
     * @param {Object} scale
     * 
     * 
     */
    visibleDataRequestEnd: function(obj, scale) {
    	this.fireEvent('onVisibleDataRequestEnd', obj, scale);
    },
    
    /**
     * @method requestLayerInfoDataEnd
     * Funzione di callback che riceve la struttura layerinfo realtiva ad un WMS
     * 
     * @param {Object} layInfo
     * struttura layerinfo relativa al WMS
     * 
     * @param {Object} extra
     * parametri extra
     * 
     */
    requestLayerInfoDataEnd: function(layInfo, extra) {
    	this.fireEvent('onRequestLayerInfoDataEnd', layInfo, extra);
    }
    
});





/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloTOCMultiServerProvider
 * 
 * 
 */
Ext.define('TolomeoExt.ToloTOCMultiServerProvider', {

	extend: 'TolomeoExt.ToloTOCAbstractDataProvider',
	
	alias: ['widget.tx_ToloTOCMultiServerProvider'],
	
	//requires: [],
	
	/** 
	 * @property {String} TOLOMEOServer
	 * 
	 * 
	 */
	TOLOMEOServer: null,

	/** 
	 * @property {String} TOLOMEOContext
	 * 
	 * 
	 */
	TOLOMEOContext: null,
	
	/** 
	 * @property {Object} paramsJS
	 * 
	 * 
	 */
	paramsJS : null,

	/**
	 * @method initComponent
	 * 
	 * 
	 */
	initComponent: function(){
		this.callParent();
    },

        
  /**
   * @method requestFullData
   * 
   * 
   * @param {Object} options
   * oggetto contenenti i parametri della richiesta.
	 * options.presetName - {string} nome del preset.
	 * options.currentMap - {string} mappa.
	 * options.scale - {number} valore di scala 	
	 * options.presetXML - {String} xml del file di preset.
	 * options.sendPreset - {boolean} abilitazione all'invio del preset
	 *   
   */
    requestFullData: function(options) {    	

    	this.requestFullData1(options); 
    	this.callParent(arguments);

    },
    
  /**
   * @method requestFullData1
   * 
   * 
   * @param {Object} options
   * oggetto contenenti i parametri della richiesta.
	 * options.presetName - {string} nome del preset.
	 * options.currentMap - {string} mappa.
	 * options.scale - {number} valore di scala 	
	 * options.presetXML - {String} xml del file di preset.
	 * options.sendPreset - {boolean} abilitazione all'invio del preset
	 *   
   */
    requestFullData1: function(options) {
    	//TOC mai attivata. Occorre costruirla
    	if (options.currentMap.legenda) {   
    		
    		var params = {
    			//scale:   scale ,
    			update: 'false', 
				tipoServer: 'wms',
				numMappaCorrente: '0'
			};
			
    		if(options.sendPreset &&  options.presetXML){
    			params.paramPresetString = options.presetXML;
    			params.presetName = options.presetName;
    		} else {
    			params.paramPreset = options.presetName;
    		}
    		
    		Ext.apply(params, this.paramsJS.urlAdditionalParams);
    		
    		var ajaxOptions = { 
					url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxTOCServletExt',
					method: 'post',
					params: params, //TODO per adesso gestita solo la prima mappa}
					success: function (records, opts) { 
									this.fullDataRequestEnd(records, opts, options.scale); 
								},
					failure: function (store, opts, records) {
							//TODO vedere se fare qualcosa in aggiunta al messaggio a video di defautl
					},
					scope: this
					//text: "Loading...",
					//TODO
					//failure: this.showAjaxError, 
				  }    		
    		
    		new TolomeoExt.ToloCrossAjax().request(ajaxOptions);  		
    	}
    },
    
    /**
     * @method fullDataRequestEnd
     * 
     * 
     * @param {Array} records
     * recordseset.
     * 
     * @param {Object} opts
     * opts.
     * 
     */
    fullDataRequestEnd: function(records, opts, scale) {
    	var obj = records[0].data;
    	this.callParent([obj, scale]);

    },
        
    /**
     * @method requestVisibleData
     * 
     * 
     * @param {Object} options
     * oggetto contenenti i parametri della richiesta.
  	 * options.presetName - {string} nome del preset.
  	 * options.currentMap - {string} mappa.
  	 * options.scale - {number} valore di scala 
	   * options.tocInfo - {object} struttura della TOC
  	 * options.presetXML - {String} xml del file di preset.
	   * options.sendPreset - {boolean} abilitazione all'invio del preset
     * 
     */
    requestVisibleData: function(options) {
		var sep=",";
		
		if (options.currentMap.legenda) {  
		
			var retObj = { layers: "", stili: "", layIdx: "", catIdx: "" };
			
			options.tocInfo.onEachLayer(
				function(cat, lay, catIdx, layIdx) {
					
					if (options.tocInfo.getLayerNeedRequestVisibleData(catIdx, layIdx)) {
						this.stili  += (this.layers!="" ? sep : "") + (lay.style=="" ? "null" : lay.style);
						this.layIdx += (this.layers!="" ? sep : "") + layIdx;
						this.catIdx += (this.layers!="" ? sep : "") + catIdx;
						this.layers += (this.layers!="" ? sep : "") + lay.name;	
					}
				},
				retObj
			);
	    	
			var thisDataProvider = this;
			
			var params = {
    			update: true,
				tipoServer: 'wms',
				layers: retObj.layers,
				stili: 	retObj.stili,
				catIdx:	retObj.catIdx,
				layIdx:	retObj.layIdx,
				random: Math.random(),
				numMappaCorrente: '0'
			};
			
    		if(options.sendPreset && options.presetXML){
    			params.paramPresetString = options.presetXML;
    			params.presetName = options.presetName;
    		} else {
    			params.paramPreset = options.presetName;
    		}
    		
    		Ext.apply(params, this.paramsJS.urlAdditionalParams);
	
			var ajaxOptions = { 
				url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxTOCServletExt',
				method: 'post',
				params: params, //TODO per adesso gestita solo la prima mappa} 
				success: function(records, opts) { 
					thisDataProvider.visibleDataRequestEnd(options.currentMap.legenda, options.scale, records, opts) 
				},
				//TODO
				//failure: this.showAjaxError,
				scope: this 
			}

    		new TolomeoExt.ToloCrossAjax().request(ajaxOptions);
		}
		this.callParent([options.scale]);
    },
    
    /**
     * @method visibleDataRequestEnd
     *
     * 
     * @param {Object} legenda
     * 
     * 
     * @param {Object} scale
     * 
     * 
     * @param {Object} records
     * 
     * 
     * @param {Object} opts
     * 
     * 
     * TODO dipendenza dalla scala per adesso staticamente definita in preset
     */
    visibleDataRequestEnd: function(legenda, scale, records, opts) {
    	
    	var obj = records[0].data;
    	this.callParent([obj, scale]);
    	
    },	
	
    /**
     * @method requestLayerInfoData
     * 
     * 
     * @param {Object} options
     * oggetto contenenti i parametri della richiesta.
  	 * options.presetName - {string} nome del preset.
  	 * options.currentMap - {string} mappa.
  	 * options.scale - {number} valore di scala 
	   * options.tocInfo - {object} struttura della TOC
  	 * options.presetXML - {String} xml del file di preset.
	   * options.sendPreset - {boolean} abilitazione all'invio del preset
     * 
     */
	requestLayerInfoData: function(options) {
    		
		var params = {
			//scale:   scale ,
			serverurl: options.serverurl, 
			layername: options.layername
		};
		
		Ext.apply(params, this.paramsJS.urlAdditionalParams);
		
		var ajaxOptions = { 
				url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxTOCLayerInfoServlet',
				method: 'post',
				params: params, 
				success: function (records, opts, extra) { 
								this.requestLayerInfoDataEnd(records, opts, options.extra);				
							},
				failure: function (store, opts, records) {
						//TODO vedere se fare qualcosa in aggiunta al messaggio a video di defautl
				},
				scope: this

			  }    		
		
		new TolomeoExt.ToloCrossAjax().request(ajaxOptions);  		   		
    }, 
    
    /**
     * @method requestLayerInfoDataEnd
     * Funzione di callback che riceve la struttura layerinfo realtiva ad un WMS
     * 
     * @param {Object} records
     * Record dello store. Contiene un solo oggetto
     * 
     * @param {Object} opts
     * Opzioni
     * 
     * @param {Object} extra
     * parametri extra
     * 
     */
    requestLayerInfoDataEnd: function(records, opts, extra) {
    	var obj = records[0].data;
    	this.callParent([obj, extra]);
    }
    
});





/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * @class TolomeoExt.ToloTOCPanelExt
 * @extends Ext.Panel
 * Pannello per contenere una mappa. 
 * 
 */
Ext.define('TolomeoExt.ToloTOCPanelExt', {

	extend: 'Ext.Panel',
	
	
	/** 
	 * @property {Object} lastUsedLocalCodTPN
	 * 
	 * 
	 */
	lastUsedLocalCodTPN: null,
	
	/** 
	 * @property {Object} dataProvidcerOpt
	 * 
	 * 
	 */
	dataProvidcerOpt: null,
	
	/** 
	 * @property {Boolean} showHideContainer
	 * Contenitore che deve essere
	 * mostrato/nascosto per visualizzare o nascondere la legenda Se null
	 * viene visualizzato e nascosto questo stesso pannelleo
	 * 
	 */
	showHideContainer : null,

	/** 
	 * @property {Function} showHandler
	 * Funzione da chiamare per visualizzare la legenda. Se null non viene chiamata
	 * 
	 */
	showHandler : null,

	/** 
	 * @property {Function} hideHandler
	 * Funzione da chiamare per nascondere la legenda. Se null non viene chiamata
	 * 
	 */
	hideHandler : null,

	/** 
	 * @property {Boolean} isTOCVisible
	 * 
	 * 
	 */
	isTOCVisible : null,

	/** 
	 * @property {Object} paramsJS
	 * 
	 * 
	 */
	paramsJS : null,

	/** 
	 * @property {Object} dataProvider
	 * 
	 * 
	 */
	dataProvider : null,

	/** 
	 * @property {Object} tocInfo
	 * 
	 * 
	 */
	tocInfo : null,

	/** 
	 * @property {String} TOLOMEOServer
	 * 
	 * 
	 */
	TOLOMEOServer : null,

	/** 
	 * @property {String} TOLOMEOContext
	 * 
	 * 
	 */
	TOLOMEOContext : null,

	/** 
	 * @property {Object} scale
	 * scala attuale aggiornata ad ogni chiamata di creazione o aggiornamento TOC da parte di metodi invocati da API
	 * si è reso necessario per gestione cambio stile
	 * 
	 */
	scale: null,
	
	/** 
	 * @property {String} iconBasePath
	 * 
	 * 
	 */
	iconBasePath:null, 
	
	
	/** 
	 * @property {Object} customQuery
	 * 
	 * 
	 */
	customQuery: null,
	

	/**
	 * @method initComponent
	 * Metodo relativo alla gestione Ext.
	 * 
	 */
	initComponent : function() {

		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
		this.isTOCCreated = false;
		this.isFullDataRequested = false;
		this.isTOCGUICreated = false;
		
		if (this.dataProviderOpt==null) this.dataProviderOpt={}; 
	
		// decide il tipo di dataprovider da utilizzare se non definito
		// modificato per utilizzare il multiserver
		var dpxtype = "widget.tx_ToloTOCMultiServerProvider";
		// ...anche nel caso che venissero forzati i dataprovider attualmente esistenti   
		if ((this.dataProviderOpt.xtype=='tx_toloTOCMapServerDataProvider') || (this.dataProviderOpt.xtype=='tx_toloTOCGeoserverDataProvider')) {
			this.dataProviderOpt.xtype = dpxtype;
		}
		
		/*
		switch (this.getParametriMappaCurr().typeCode) {
			case 0: 	// Mapserver
				dpxtype = 'tx_toloTOCMapServerDataProvider';
				break;
			case 11:	// WMS
				dpxtype = 'tx_toloTOCGeoserverDataProvider';	
				break;
			default:
				// data provider non definito
				
		}*/
		
		TolomeoExt.applyIfEmpty(this.dataProviderOpt, {
				TOLOMEOServer: this.TOLOMEOServer,
				TOLOMEOContext: this.TOLOMEOContext,				
				paramsJS : this.paramsJS,
				xclass: dpxtype 		//'tx_toloTOCMapServerDataProvider'
			});
		
		// se definito tipo di dataprovider lo creo
		if (dpxtype!='') this.dataProvider = Ext.create(this.dataProviderOpt);
		
		
		// TODO Events
		// TODO ? this.addEvents('layerSelectionChange');
		// TODO ? this.addEvents('categorySelectionChange');
		// TODO ? this.addEvents('categoryEnableChange');
		this.addEvents('layerCheckedChange');
		this.addEvents('categoryCheckedChange');
		this.addEvents('layerOpacityChange');
		this.addEvents('layerOrderChange');
		this.addEvents('layerStyleChanged');
		
		this.addEvents('categoryExpand');
		this.addEvents('categoryCollapse');
		this.addEvents('layerExpand');
		this.addEvents('layerCollapse');
		
		/**
		 * @event stylePanelRequest
		 * Lanciato quando viene richiesta la visualizzazione del pannello di gestione degli stili
		 * @param {String} nome nome layer
		 * @param {Number} cat numero categoria del layer 
		 * @param {Number} lay numero del layer
		 * @param {String[][]} 	Elenco degli stili definiti per il layer nella forma [[stile1],[stile2]] 
		 * 
		 */
		this.addEvents('stylePanelRequest');
		this.addEvents('ajaxError');
		this.addEvents('tocCreate');
		this.addEvents('tocGuiCreate');

		this.addEvents('contextMenuZoomToExtent');
		this.addEvents('contextMenuShowInfo');
		this.addEvents('contextMenuZoomMaxScaleMax');
		this.addEvents('contextMenuZoomMinScaleMin');
		
		this.addEvents('contextMenuAddWMSFromCatalogClick');
		
		/**
		 * @event contextMenuMetadataClick
		 * Lanciato quando viene scelta la voce del menu di contesto relativa al metadato del layer
		 * @param {Object}  Oggetto javascript contenente i campi format, type e url
		 */
		this.addEvents('contextMenuMetadataClick');
		
		this.addEvents('exportForQgisClicked');
		
		
		//this.addEvents('contextMenuAddCategory');
		//this.addEvents('contextMenuAddLayer');
		
		/**
		 * @event layerLinkClick
		 * Lanciato quando viene clickato una voce di legenda corrispondente ad un layer
		 * @param  {Number} cat indice della categoria
		 * @param  {Number} lay indice del layer
		 */
	//	this.addEvents('layerLinkClick');
	//	this.addEvents('categoryLinkClick');
		
		
		this.addEvents('categoryAdded');
		this.addEvents('layerAdded');
		
		this.addEvents('itemSelected');
		this.addEvents('itemClicked');
		this.addEvents('addLayerToQgisClicked');
		
		this.dataProvider.on('onFullDataRequestEnd', this.onFullDataRequestEnd, this);
		this.dataProvider.on('onVisibleDataRequestEnd', this.onVisibleDataRequestEnd , this);
		this.dataProvider.on('onRequestLayerInfoDataEnd', this.addLayerCallback , this);
		
		
		this.tocInfoField = new Ext.form.TextField({
	    	name: 'tocInfo'
	    });
	    							
	    this.paramsJSField = new Ext.form.TextField({
	    	name: 'paramsJS'
	    });
		
		this.nMappaField = new Ext.form.TextField({
			name: 'nMappa'
		});
		
		this.idxCategoriaBase = new Ext.form.TextField({
			name: 'idxCategoriaBase'
		});
		
		this.idxLayerBase = new Ext.form.TextField({
			name: 'idxLayerBase'
		});

	    this.submitForm = new Ext.form.FormPanel({
			hidden: true,
			renderTo: Ext.getBody(),
			bodyStyle: 'position:absolute; top: 0px; left 0px;',
			standardSubmit: true,
			method: 'POST',
			items: [this.tocInfoField, this.paramsJSField, this.layerOrderField, this.nMappaField, this.idxCategoriaBase, this.idxLayerBase]
	    });		
	    
		
	    if (this.iconBasePath==null) this.iconBasePath =  TolomeoExt.Vars.TOLOMEOServer + TolomeoExt.Vars.TOLOMEOStaticRoot + 'img/icone/16-default/';

		this.callParent();
		//TolomeoExt.ToloTOCPanelExt.superclass.initComponent.call(this);
	},

	/**
	 * @method onFullDataRequestEnd
	 * 
	 * 
	 * @param {Object} obj
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 */
	onFullDataRequestEnd: function(obj, scale) {
		
		var legenda = this.getParametriMappaCurr().legenda;
		var objBuff = {};
		Ext.apply(objBuff, obj);
		Ext.apply(objBuff, { presetLegenda: legenda});
		var a = [];
		
		this.tocInfo = new TolomeoExt.ToloTOCInfo(objBuff);
		
		this.tocInfo.on("catTreeIdxUpdate", this.onTOCInfoCatTreeIdxUpdate, this);
		this.tocInfo.on("layTreeIdxUpdate", this.onTOCInfoLayIdxUpdate, this);
		
		if (legenda) {
			this.tocInfo.onEachLayer(
				function(cat, lay, catIdx, layIdx) { 
					lay.checked = lay.defaultGroup;
					if (lay.temporaryNotAvailable) a.push({catIdx: catIdx, layIdx: layIdx});
					lay.parentcategorychecked = cat.checked;
					lay.layId = cat.catId + "-" + lay.descr;
					var tocInfo = this.tocInfo;
					lay.isEnabled = function(){
						
						if (/* tocInfo.areAllParentCatChecked(catIdx) */ tocInfo.areAllParentCatChecked(cat.catTreeIdx) && cat.checked){
							//return this.parentcategorychecked && this.checked; 
							//return this.checked;
							return this.checked;
						}else{
							//return this.checked;
							return false;
						}
					}
					
					var layer = this.tocInfo.getCategoryPresetInfo(catIdx).layerList[layIdx];
					
					var smin = (layer) ? layer.scalaMinima : -1;
    				var smax = (layer) ? layer.scalaMassima : -1;
					
					lay.visible=this.layerIsVisibleAtScale(scale, smin, smax, lay.minScaleMin, lay.maxScaleMax );

				},
				this
			);
			
		}
		
		this.createTOC(scale);
		for (var i=0; i<a.length; i++) {
			this.setLayerStateChange(a[i].catIdx, a[i].layIdx, false, true);	
		}
		
	},
	
	/**
	 * @method onVisibleDataRequestEnd
	 * 
	 * 
	 * @param {Object} obj
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 */
	onVisibleDataRequestEnd: function(obj, scale) {
		this.updateTocInfoFromVisibleRequest(obj, scale);
		this.updateTOC(scale);
	},
	
	/**
	 * @method showTOC
	 * Visualizza la legenda. La legenda deve essere stata precedentemente creata ed aggornata ai cambi scala con createOrUpdate
	 * 
	 */
	showTOC : function() {

		if (this.isTOCCreated) {
			// solo visualizzare ed aggiornare
			this.isTOCVisible = true;
	
			if (this.showHandler != null) {
				this.showHandler.call(this);
			} else if (this.showHideContainer != null) {
				this.showHideContainer.setVisible(this.isTOCVisible);
			} else
				this.setVisible(this.isTOCVisible);
	
			// setta anche eventuali container
			/*
			 * ct=this.ownerCt; while (ct) { ct.setVisible(this.isTOCVisible);
			 * ct=ct.ownerCt; }
			 */
		}
		
	},

	/**
	 * @method hideTOC
	 * 
	 * 
	 */
	hideTOC : function() {
		if (this.isTOCVisible != null) {
			this.isTOCVisible = false;
			if (this.hideHandler != null) {
				this.hideHandler.call(this);
			} else if (this.showHideContainer != null) {
				this.showHideContainer.setVisible(this.isTOCVisible);
			} else
				this.setVisible(this.isTOCVisible);
		}
	},

	/**
	 * @method createTOC
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 */
	createTOC : function(scale) {
		this.scale=scale;
		this.isTOCCreated = true;
		this.fireEvent('tocCreate');
	},

	/**
	 * @method updateTOC
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 */
	updateTOC : function(scale, posObj) {
		if (scale != undefined) this.scale=scale;
		if (posObj != undefined) this.posObj = posObj;
		this.updateTocInfoVisibleAtScale(scale);
	},
	
	/**
	 * @method createOrUpdate
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 * @param {Boolean} forceRequest
	 * se true forza la richiesta al server dei layer visibili alla scala indicata, altrimenti la legenda viene aggiornata con i dati disponibili
	 * 
	 */
	createOrUpdate: function(scale, forceRequest, posObj) {		
		if (scale != undefined) this.scale  = scale;
		if (posObj != undefined) this.posObj = posObj;
		if (this.isFullDataRequested) {
			//this.requestVisibleData(scale);
			// TODO scelta per casi nei quali va richiesto l'aggiornamento al server come in caso di cambi di stile
			var thisPanel = this;
			if (!this.isTOCCreated) {
				setTimeout(function() {	thisPanel._requestOrUpdate(scale, forceRequest); },4500);
			} else {
				this._requestOrUpdate(scale, forceRequest);
			}
		} else {
			this.requestFullData(scale);
		}		
	},

	/**
	 * @method _requestOrUpdate
	 * @private
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 * @param {Boolean} forceRequest
	 * 
	 * 
	 */
	_requestOrUpdate: function(scale, forceRequest) {
		if (forceRequest) {
			this.requestVisibleData(scale); 
		} else {
			this.updateTOC(scale);
		}
	},
	
	/**
	 * @method requestFullData
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 */
	requestFullData : function(scale) {
		this.scale=scale;
		this.isFullDataRequested = true; 
		var parametriMappaCurr = this.getParametriMappaCurr();
		if (parametriMappaCurr.legenda) {
			var options = {
				presetName: this.paramsJS.nomePreset, 
				currentMap: parametriMappaCurr, 
				scale: scale,
				presetXML: this.presetXML,
				sendPreset: this.sendPreset
			};
						
			this.dataProvider.requestFullData(options);
		}
	},

	/**
	 * @method requestVisibleData
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 */
	requestVisibleData : function(scale) {
		this.scale=scale;
		var parametriMappaCurr = this.getParametriMappaCurr();
		if (parametriMappaCurr.legenda) {
			var options = {
				presetName: this.paramsJS.nomePreset, 
				currentMap: parametriMappaCurr, 
				scale: scale,
				tocInfo: this.tocInfo,
				presetXML: this.presetXML,
				sendPreset: this.sendPreset
			};	
					
			this.dataProvider.requestVisibleData(options);
		}
	},

	/**
	 * @method getScaleMinMaxFromIdxs
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 */
	getScaleMinMaxFromIdxs: function(catIdx, layIdx) {
	
		var lay = this.tocInfo.getCategoryInfo(catIdx).layers[layIdx]
		var layerPresetInfo = this.tocInfo.getCategoryPresetInfo(catIdx).layerList[layIdx];
		
		var smin = (layerPresetInfo) ? layerPresetInfo.scalaMinima : -1;
    	var smax = (layerPresetInfo) ? layerPresetInfo.scalaMassima : -1;
		
		return this.getScaleMinMax( smin, smax, lay.minScaleMin, lay.maxScaleMax);
		
	},
	
	/**
	 * @method getScaleMinMax
	 * 
	 * 
	 * @param {Object} legendaScalaMinima
	 * 
	 * 
	 * @param {Object} legendaScalaMassima
	 * 
	 * 
	 * @param {Object} styleMinScaleMin
	 * 
	 * 
	 * @param {Object} styleMaxScaleMax
	 * 
	 * 
	 */
	getScaleMinMax: function(legendaScalaMinima, legendaScalaMassima, styleMinScaleMin, styleMaxScaleMax) {
		
		var scalaMinima  = legendaScalaMinima;
		var scalaMassima = legendaScalaMassima;
		
		if ((scalaMinima ==undefined)||(scalaMinima ==null)||(scalaMinima ==-1)) {
			// Se non definito su preset conta quello da stile
			scalaMinima = styleMinScaleMin;
		} 
		
		if ((scalaMassima ==undefined)||(scalaMassima ==null)||(scalaMassima ==-1)) {
			// Se non definito su preset conta quello da stile
			scalaMassima = styleMaxScaleMax;
		} 
		
		return {scalaMinima: scalaMinima, scalaMassima: scalaMassima};
		
	},
	
	/**
	 * @method layerIsVisibleAtScale
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 * @param {Object} legendaScalaMinima
	 * 
	 * 
	 * @param {Object} legendaScalaMassima
	 * 
	 * 
	 * @param {Object} styleMinScaleMin
	 * 
	 * 
	 * @param {Object} styleMaxScaleMax
	 * 
	 * 
	 */
	layerIsVisibleAtScale: function(scale,legendaScalaMinima, legendaScalaMassima, styleMinScaleMin, styleMaxScaleMax) {
    	
		var scala = this.getScaleMinMax(legendaScalaMinima, legendaScalaMassima, styleMinScaleMin, styleMaxScaleMax);
		
		var scalaMinima  = scala.scalaMinima;
		var scalaMassima = scala.scalaMassima;
		
		if (((scalaMinima ==undefined)||(scalaMinima ==null)||(scalaMinima ==-1)||(scalaMinima <= scale)) &&
		    ((scalaMassima==undefined)||(scalaMassima==null)||(scalaMassima==-1)||(scale <= scalaMassima))) {
			visible = true;
		} else {
			visible = false;
		}
		
		return visible;
	     
    },	

	/**
	 * @method updateTocInfo
	 * Aggiorna la struttura TocInfo con il risultato di una richiesta di update
	 * 
	 * @param {Object} visibleStateInfo
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 */
	updateTocInfoFromVisibleRequest : function(visibleStateInfo, scale) {
		var legenda = this.getParametriMappaCurr().legenda;
		if (this.tocInfo && this.tocInfo.presetLegenda) {	
			for (var i = 0; i < visibleStateInfo.layers.length; i++) {
				var lay = visibleStateInfo.layers[i].lay;
				var cat = visibleStateInfo.layers[i].cat;
				this.tocInfo.setLayerNeedRequestVisibleData(cat, lay, false);
				var categoria = this.tocInfo.getCategoryPresetInfo(cat);
				var layer = categoria.layerList[lay]; // info sul layer da preset
				this.tocInfo.getCategoryInfo(cat).layers[lay].minScaleMin = visibleStateInfo.layers[i].minScaleMin;
				this.tocInfo.getCategoryInfo(cat).layers[lay].maxScaleMax = visibleStateInfo.layers[i].maxScaleMax;
				if (visibleStateInfo.layers[i].urlLegendaClasseUnica != undefined && 
						visibleStateInfo.layers[i].urlLegendaClasseUnica!=null && 
						visibleStateInfo.layers[i].urlLegendaClasseUnica!="") {
					this.tocInfo.getCategoryInfo(cat).layers[lay].classi[0].nome = visibleStateInfo.layers[i].urlLegendaClasseUnica;
				}
			}
		}
	},
	
	/**
	 * @method updateTocInfoVisibleAtScale
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 */
	updateTocInfoVisibleAtScale: function(scale) {
	
		if (this.tocInfo && this.tocInfo.presetLegenda)
		this.tocInfo.onEachLayer(
				function(cat, lay, catIdx, layIdx) { 
					
					var categ = this.tocInfo.getCategoryPresetInfo(catIdx);
					if (categ!=undefined && categ!=null) { 
						var layer = categ.layerList[layIdx];
						lay.visible = this.layerIsVisibleAtScale(scale, layer.scalaMinima, layer.scalaMassima, lay.minScaleMin, lay.maxScaleMax );
					}
					else  lay.visible = this.layerIsVisibleAtScale(scale, -1, -1, lay.minScaleMin, lay.maxScaleMax );
				},
				this
		);
		
	},
	
	/**
	 * @method setLayerStateChange
	 * 
	 * 
	 * @param {Object} cat
	 * cat.
	 * 
	 * @param {Object} lay
	 * lay.
	 * 
	 * @param {Object} checked
	 * checked.
	 * 
	 * @param {Object} force
	 * se true forza l'operazione anche se lo stato da impostare è uguale a quello attuale
	 * 
	 */
	setLayerStateChange : function(cat, lay, checked, force) {
		
		 
		if (this.tocInfo != null) {
			if (lay != null) {
				// Aggiorna la struttura in memoria
				this.tocInfo.getCategoryInfo(cat)
				var layer = this.tocInfo.getCategoryInfo(cat).layers[lay];
				if(!force && layer.checked == checked) return;
				layer.checked = checked;
				
				// Nel caso di catagoria mutuamente esclusiva, se ho acceso questo layer spengo tutti gli altri
				if (checked==true) {
					var legenda = this.getParametriMappaCurr().legenda;
					
					if (legenda && this.tocInfo.getCategoryPresetInfo(cat).mutualExclusive && this.tocInfo.getCategoryPresetInfo(cat).mutualExclusive==true) {
						for (var i=0; i < this.tocInfo.getCategoryInfo(cat).layers.length; i++) {
							if (i!=lay) this.setLayerStateChange(cat, i, false);
						}
					}
					
					// nel caso di categoria mutualExclusiveCategoriesOnLayers 
					// disattivo gli altri layer delle altre categorie sorelle con 
					// mutualExclusiveCategoriesOnLayers a true
					var parentCategory = this.tocInfo
							.getCategoryInfo(this.tocInfo
							.getParentCategoryIdx(cat));
				
					
					if (parentCategory) {
						if (legenda
								&& this.tocInfo
								.getCategoryPresetInfo(cat).mutualExclusiveCategoriesOnLayers
								&& this.tocInfo
								.getCategoryPresetInfo(cat).mutualExclusiveCategoriesOnLayers == true) 
							{
								for (var i = 0; i < parentCategory.categories.length; i++) 
								{
									if (parentCategory.categories[i].catTreeIdx != cat
											&& this.tocInfo.getCategoryPresetInfo(parentCategory.categories[i].catTreeIdx)
											.mutualExclusiveCategoriesOnLayers
											&& this.tocInfo.getCategoryPresetInfo(parentCategory.categories[i].catTreeIdx)
											.mutualExclusiveCategoriesOnLayers == true) 
									{
										for (var s=0; s < parentCategory.categories[i].layers.length; s++) 
										{	
											var layInfo = this.tocInfo.getCategoryPresetInfo(parentCategory.categories[i].catTreeIdx).layerList[s];
											if (layInfo.hidden!=true && layInfo.userSwitchable!=false) 
											{
												this.setLayerStateChange(parentCategory.categories[i].catTreeIdx, s, false);	
											}
										}
									}
								}
							}

					} else {
						if (legenda
								&& this.tocInfo
								.getCategoryPresetInfo(cat).mutualExclusiveCategoriesOnLayers
								&& this.tocInfo
								.getCategoryPresetInfo(cat).mutualExclusiveCategoriesOnLayers == true) 
						{
							for (var i = 0; i < this.tocInfo.categories.length; i++) 
							{
								if (this.tocInfo.categories[i].catTreeIdx != cat
									&& this.tocInfo.getCategoryPresetInfo(this.tocInfo.categories[i].catTreeIdx)
										.mutualExclusiveCategoriesOnLayers
									&& this.tocInfo.getCategoryPresetInfo(this.tocInfo.categories[i].catTreeIdx)
										.mutualExclusiveCategoriesOnLayers == true)
								{
									for (var s=0; s < this.tocInfo.getCategoryInfo(this.tocInfo.categories[i].catTreeIdx).layers.length; s++) 
										{
											var layInfo = this.tocInfo.getCategoryPresetInfo(this.tocInfo.categories[i].catTreeIdx).layerList[s];
											if (layInfo.hidden!=true && layInfo.userSwitchable!=false) 
											{
												this.setLayerStateChange(this.tocInfo.categories[i].catTreeIdx, s, false);
											}		
									    }
							    }
						    }
					    }
					
					}
				}				

				this.fireEvent('layerCheckedChange',layer, this.tocInfo);
				
			} else {
				this.setCategoryStateChange(cat,checked);				
			}
		}		
	},
	
	/**
	 * @method setCategoryStateChange
	 * 
	 * 
	 * @param {Object} cat
	 * cat.
	 * 
	 * @param {Object} checked
	 * checked.
	 * 
	 */
	setCategoryStateChange : function(cat, checked) {
		if (this.tocInfo != null) {
			var category = this.tocInfo.getCategoryInfo(cat);
			
			category.checked = checked;

			if (checked == true) {
				var legenda = this.getParametriMappaCurr().legenda;
				var parentCategory = this.tocInfo
						.getCategoryInfo(this.tocInfo
								.getParentCategoryIdx(cat));

				if (parentCategory) {
					if (legenda
							&& this.tocInfo
									.getCategoryPresetInfo(this.tocInfo
											.getParentCategoryIdx(cat)).mutualExclusiveCategories
							&& this.tocInfo
									.getCategoryPresetInfo(this.tocInfo
											.getParentCategoryIdx(cat)).mutualExclusiveCategories == true) {
						for (var i = 0; i < parentCategory.categories.length; i++) {
							if (parentCategory.categories[i].catTreeIdx != cat) {
								this
										.setCategoryStateChange(
												parentCategory.categories[i].catTreeIdx,
												false);
							}
						}
					}
				} else {
					if (legenda
							&& this.tocInfo.presetLegenda.mutualExclusiveCategories
							&& this.tocInfo.presetLegenda.mutualExclusiveCategories == true) {
						for (var i = 0; i < this.tocInfo.categories.length; i++) {
							if (this.tocInfo.categories[i].catTreeIdx != cat) {
								this
										.setCategoryStateChange(
												this.tocInfo.categories[i].catTreeIdx,
												false);
							}
						}
					}
				}
			}

			this.fireEvent('categoryCheckedChange', category,
					this.tocInfo);
		}		
	},

	/**
	 * @method setAllLayersState
	 * Imposta allo stato passato tutti i layer in maniera non ricorsiva e senza modificare lo stato dei layer hidden.
	 * 
	 * @param {Object} cat
	 * cat.
	 * 
	 * @param {Object} checked
	 * checked.
	 * 
	 */
	setAllLayersState: function(cat, checked) {
		if (this.tocInfo != null) {
			this.tocInfo.onEachLayer(
					function(cat, lay, catIdx, layIdx) {
						//userSwitchable
						var layInfo = this.tocInfo.getCategoryPresetInfo(catIdx).layerList[layIdx];
						if (layInfo.hidden!=true && layInfo.userSwitchable!=false) {
							this.setLayerStateChange(catIdx, layIdx, checked);	
						}
					},					
					this, cat, false); 
		}
	},
	
	/**
	 * @method layerOrderChange
	 * 
	 * 
	 */
	layerOrderChange: function() {
		this.fireEvent('layerOrderChanged', this.tocInfo);
	},

	/**
	 * @method showAjaxError
	 * TODO gestire errori
	 * 
	 * @param {Object} transport
	 * 
	 * 
	 */
	showAjaxError : function(transport) {
		Ext.MessageBox.show({
			title: ToloI18n.getMsg("ToloTOCPanelExt.showAjaxError.title"),
			msg: ToloI18n.getMsg("ToloTOCPanelExt.showAjaxError.msg"),
			buttons: {
				yes: ToloI18n.getMsg("ToloTOCPanelExt.showAjaxError.btnMostra"),
				cancel: ToloI18n.getMsg("ToloTOCPanelExt.showAjaxError.btnContinua")
			},
			icon: Ext.MessageBox.ERROR,
			fn: function(btn) {
				switch (btn) {
					case 'yes':
						Ext.MessageBox.alert(ToloI18n.getMsg("ToloTOCPanelExt.showAjaxError.title"),
								transport.responseText);
						break;
				}
			}
		});
		
		this.fireEvent('ajaxError', transport);
	},

	/**
	 * @method getCurrTOCSettings
	 * 
	 * 
	 * @returns {Object}
	 * legenda
	 * 
	 */
	getCurrTOCSettings : function() {
		return this.getParametriMappaCurr().legenda;
	},

	/**
	 * @method getParametriMappaCurr
	 * 
	 * 
	 * @returns {Object}
	 * Restituisce i parametri correnti della mappa.
	 * 
	 */
	getParametriMappaCurr : function() {
		//TODO gestisce solo prima mappa
		return this.paramsJS.mappe.mappaList[0];
	},

	/**
	 * @method layerIsVisible
	 * Se il layer non è in legenda si suppone che sia visibile sempre.
	 * 
	 * @returns {Boolean}
	 * true if layer is visible
	 * 
	 */
	layerIsVisible : function(codTPN) {
		var bPresente = false;

		if (this.tocInfo) {
			
			var returnObj = { bPresente:false, bVisible: false };
			
			this.tocInfo.onEachLayer(
				function(cat, lay, catIdx, layIdx) {
					if (lay.codTPN == codTPN) {
						if ((lay.isEnabled()) && lay.visible ) { //this.layerIsScaleVisible(codTPN) 
							this.bPresente= true;
							this.bVisible = true;
						} else {
							this.bPresente = true;
							this.bVisible = false;
						}
					}
				},
				returnObj
			
			);
		
			return (!returnObj.bPresente || returnObj.bVisible )
		}
	},
	
	/**
	 * @method getVisibleLayers
	 * Restituisce la lista ordinata dei codici dei layers visibili.
	 * 
	 * @returns {Array}
	 * lista dei layers visibili
	 * 
	 */
	getVisibleLayers : function() {
		
		var visibleLayers = [];		

		var returnObj = { visibleLayers: visibleLayers, tocInfo: this.tocInfo };
		
		if (this.tocInfo.layerOrder && this.tocInfo.layerOrder.length>0) {
			
			for (var i = 0; i < this.tocInfo.layerOrder.length; i++) {
				var laypos = this.tocInfo.layerOrder[i];
				var cat = this.tocInfo.getCategoryInfo(laypos.cat);
				var lay = cat.layers[laypos.lay];
				
				if ( this.tocInfo.areAllParentCatChecked(laypos.cat) && cat.checked && lay.checked && lay.visible ){ 
					visibleLayers.push(lay);
				}
			}
			
		} else {
		
			this.tocInfo.onEachLayer(
				function(cat, lay, catIdx, layIdx) {
					if ( this.tocInfo.areAllParentCatChecked(catIdx) && cat.checked && lay.checked && lay.visible ){ 
						this.visibleLayers.push(lay);
					}
				},
				returnObj
			);

		}
		return returnObj.visibleLayers;
	},
	
	/**
	 * @method setLayerVisibility
	 * 
	 * 
	 * @param {Object} codTPN
	 * 
	 * 
	 */
	setLayerVisibility : function(codTPN) {
		
		if (!this.layerIsVisible(codTPN)) {
			if (this.tocInfo) {
				this.tocInfo.onEachLayer(
						function(cat, lay, catIdx, layIdx) {
							if (lay.codTPN == codTPN) {
								
								this.checkAllParentCat(catIdx, true);
								if (!lay.checked){
									this.setLayerStateChange(catIdx, layIdx, true);
								}
							}
						},
						this
					);
			}
		}
		
	}, 
	
	/**
	 * @method checkAllParentCat
	 * Accende tutte le categorie antenato della categoria indicata, più eventualmente la categoria stessa
	 * 
	 * @param {String} catIdx
	 * Indice della categoria della quale devono essere accesi tutti i parent
	 * 
	 * @param {Boolean} withCurrent
	 * indica se deve essere accesa anche la categoria corrente (catIdx). Default=false
	 * 
	 */
	checkAllParentCat: function(catIdx, withCurrent) {
		
		var bAllChecked = this.tocInfo.areAllParentCatChecked(catIdx);				
		if (!bAllChecked) {
			this.tocInfo.onEachParentCategory(
					function(cat1,catIdx1) {
						this.setCategoryStateChange(catIdx1, true);
					}, 
					this,
					catIdx); 
		}
		if (withCurrent) {
			var cat = this.tocInfo.getCategoryInfo(catIdx);
			if (!cat.checked ){
				this.setCategoryStateChange(catIdx, true);
			}
		}
	
		
	},
	
	/**
	 * @method setLayerStyle
	 * 
	 * 
	 * @param {Object} cat
	 * 
	 * 
	 * @param {Object} lay
	 * 
	 * 
	 * @param {Object} style
	 * 
	 * 
	 */
	setLayerStyle: function (cat, lay, style) {
		
		var layer = this.tocInfo.getCategoryInfo(cat).layers[lay];
		layer.style = style;
		this.tocInfo.setLayerNeedRequestVisibleData(cat, lay, true);
		this.createOrUpdate(this.scale, true, this.posObj);
		this.fireEvent('layerStyleChanged', layer, style);
	},
	
	/**
	 * @method setPresetXML
	 * 
	 * 
	 * @param {Object} presetXML
	 * 
	 * 
	 */
	setPresetXML: function(presetXML){
		this.presetXML = presetXML;
	},
	
	/**
	 * @method getContextMenu
	 * 
	 * 
	 * @param {Object} cat
	 * 
	 * 
	 * @param {Object} lay
	 * 
	 * 
	 */
	getContextMenu: function(cat, lay) {
		
		// TODO Gestisce solo mappa 0
		var nMappa = 0;
		
		var me = this;
		
		var layInfo = null;
		var catInfo = this.tocInfo.getCategoryInfo(cat)
		if (lay) layInfo = catInfo.layers[lay];
		var isLayer = layInfo && layInfo.itemType=='layer';
		var isCategory = (lay==undefined || lay==null) && catInfo && catInfo.itemType=='category';
		
		var menu = Ext.create('Ext.menu.Menu',{
		    cls: "clearCSS"
		});
		
		if (isLayer) {
			menu.add({
		    			text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuinfo"),
		    			//disabled: (this.tocInfo.getBoundingBox(cat, lay)==null),
		    			listeners: { click: {
    							fn: function() { this.fireEvent('contextMenuShowInfo', cat,lay, this.tocInfo); },
    							scope: this
    							}
    						}
		    		});
			
			//if (this.tocInfo.getCategoryPresetInfo(cat).layerList[lay].attribution) {
			if (layInfo.attribution) {
				menu.add({
	    			text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuattr"),
	    			//disabled: (this.tocInfo.getBoundingBox(cat, lay)==null),
	    			listeners: { click: {
							//fn: function() { Ext.MessageBox.alert('Attribuzioni', this.tocInfo.getCategoryPresetInfo(cat).layerList[lay].attribution ); },
	    					fn: function() { Ext.MessageBox.alert(ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuattr.title"), layInfo.attribution.title ); },
							scope: this
							}
						}
	    		});
			}
			
			var mdList = layInfo.metadataUrlList;
			if (mdList && mdList.length>0) {
				var mdItems = [];
				for (var i = 0; i < mdList.length; i++) {
					var md = mdList[i];
					mdItems.push({
							toloMetadata: md,
	    	    			text: md.type,
	    	    			listeners: { click: {
	    	    							fn: function() { 
	    	    									me.fireEvent('contextMenuMetadataClick', this.toloMetadata);
	    	    								}
	    	    							}
	    	    						}	    	    		
						});
				}
				var menuMetadata = Ext.create('Ext.menu.Item',
						{
							text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnumeta"),
							menu: {
								items: mdItems,
				    	    	 cls: "clearCSS"
								}
							});
				menu.add(menuMetadata);
			}
			
		}
		
		if (isCategory) {
			var menuAccendiSpegni = Ext.create('Ext.menu.Item',
					{
						text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuacc"),
						menu: {
							items: [{
			    	    			text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuacc.atutto"),
			    	    			disabled: this.tocInfo.getCategoryPresetInfo(cat).mutualExclusive,
			    	    			listeners: { click: {
			    	    							fn: function() { this.setAllLayersState(cat, true); },
			    	    							scope: this
			    	    							}
			    	    						}
			    	    			},{
				    	    			text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuacc.stutto"),
				    	    			listeners: { click: {
				    	    							fn: function() { this.setAllLayersState(cat, false); },
				    	    							scope: this
				    	    							}
				    	    						}
				    	    			}],
			    	    	 cls: "clearCSS"
							}
						});
			
			menu.add(menuAccendiSpegni);
			
			if ((this.paramsJS.layOut.csw || this.paramsJS.layOut.WMSExplorer)) {
				
				var menuAggiungi = Ext.create('Ext.menu.Item',
									{
										text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuagg"),
										menu: {
											items: [/*{
														text: "Aggiungi WMS da catalogo",
														listeners: { click: {
																		fn: function() {
																			this.fireEvent("contextMenuAddWMSFromCatalogClick", cat, lay, false);
																		}, 
																		scope: this
																		}
														}
													},{
														text: "Aggiungi WMS da server",
														listeners: { click: {
																		fn: function() {
																			this.fireEvent("contextMenuAddWMSFromWMSWidgetClick", cat, lay, false);
																		}, 
																		scope: this
																		}
														}
													},*/
													{
										    			text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuagg.cat"),
										    			//disabled: (this.tocInfo.getBoundingBox(cat, lay)==null),
										    			listeners: { click: {
								    							fn: function() {
									    								if (lay==null) {
									    									var msgbox = Ext.Msg.prompt(ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuagg.cat.title"), 
									    											ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuagg.cat.msg"),
								    										function(btn, catName) {
								    											if (btn!='cancel') {
										    										var catInfo = {
												    									paramsJSParams: {
												    										catDescr: catName,
												    										layerList: [],
												    										categoryList: [],
																							nome: catName,
																							userSwitchable: true
												    									},
												    									tocInfoParams: {
												    										catDescr: catName,
																							catId: catName +Math.random(),
																							//catTreeIdx: "0",
																							categories: [],
																							checked: false,	
																							clickTarget: "",
																							clickUrl: "",	
																							expanded: false,
																							forceClickable: false,
																							hidden: false,
																							layers: [],
																							nome: catName,	
																							toolTip: "",
																							userSwitchable: true
												    									}
																					};
												    								this.addCategory(catInfo, cat, false);
								    											}
								    										}, this);
									    								}
									    							},
								    							scope: this
								    							}
								    						}
										    		}],
							    	    	 cls: "clearCSS"
											}
										});
		
				if (this.paramsJS.layOut.csw) {
					menuAggiungi.menu.add({
								text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuagg.wmsdacat"),
								listeners: { click: {
												fn: function() {
															this.fireEvent("contextMenuAddWMSFromCatalogClick", cat, lay, false);
														}, 
												scope: this
											}
								}
							});
				}
			
			}
		
			if (this.paramsJS.layOut.WMSExplorer) {
				menuAggiungi.menu.add({
						text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuagg.wmsdaser"),
						listeners: { click: {
										fn: function() {
											this.fireEvent("contextMenuAddWMSFromWMSWidgetClick", cat, lay, false);
										}, 
										scope: this
										}
						}
					});
			}
			
			menu.add(menuAggiungi);			
		}
		
		if (isCategory || isLayer) {
			
			if (lay!=null) {
				
				var menuZoom = Ext.create('Ext.menu.Item',
						{
							text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuzoom"),
							menu: {
								items: [{
				    	    			text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuzoom.est"),
				    	    			disabled: (this.tocInfo.getBoundingBox(cat, lay)==null),
				    	    			listeners: { click: {
				    	    							fn: function() { this.fireEvent('contextMenuZoomToExtent', cat,lay, this.tocInfo); },
				    	    							scope: this
				    	    							}
				    	    						}
				    	    			}],
				    	    	 cls: "clearCSS"
								}
							});

				menuZoom.menu.add({
				    			text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuzoom.min"),
				    			disabled: !(layInfo.minScaleMin != undefined && layInfo.minScaleMin!=null && layInfo.minScaleMin > 0),
				    			listeners: { click: {
				    							fn: function() { this.fireEvent('contextMenuZoomMinScaleMin', cat,lay, this.tocInfo, this.getScaleMinMaxFromIdxs(cat, lay)); },
				    							scope: this
				    							}
				    						}
				    		});
				menuZoom.menu.add({
								text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuzoom.max"),
								disabled: !(layInfo.maxScaleMax != undefined && layInfo.maxScaleMax!=null && layInfo.maxScaleMax > 0 ),
								//disabled: (this.tocInfo.getBoundingBox(cat, lay)==null),
								listeners: { click: {
												fn: function() { this.fireEvent('contextMenuZoomMaxScaleMax', cat,lay, this.tocInfo, this.getScaleMinMaxFromIdxs(cat, lay)); },
												scope: this
												}
											}
							});
		
				menu.add(menuZoom);
				
				if (this._chkIfStyleManagerAvailable(layInfo)) {
					
					var menuStili = Ext.create('Ext.menu.Item',
							{
								text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnustili"),
								menu: {
									//items: [vociStili],
					    	    	 cls: "clearCSS"
									}
								});
				
					for (var i=0; i < layInfo.definedStyles.length ; i++) {
						
						var infoStile = layInfo.definedStyles[i];
						var s = Ext.create('Ext.menu.CheckItem',
							{
								text: (infoStile.title && infoStile.title!="") ? infoStile.title : infoStile.name,
								checked: infoStile.name==layInfo.style, 
								group: 'a',
								stile: infoStile.name,
								listeners: { click: { fn: function() { me.setLayerStyle(cat, lay, this.stile); }} }
								});
						menuStili.menu.add(s);
						
					}
					
					menuStili.menu.add('-');
					
					var manager = Ext.create('Ext.menu.Item',
							{
								text:  ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnustili.gest"),
								listeners: { click: { fn: function() { this.openStyleManager(cat, lay); }, scope: this} }
								});
					menuStili.menu.add(manager);
							
					menu.add(menuStili);
				}
	
				//if (layInfo.withOpacitySettings ) {					
				var sl = new Ext.slider.Single({
	    	     	 		    vertical: false,
	    	     	 		    value: (layInfo.opacity!=undefined && layInfo.opacity!=null )?  layInfo.opacity*100 : 100,
	    	     	 		    //increment: 10,
	    	     	 		    minValue: 0,
	    	     	 		    maxValue: 100	     	 	
	    	     	 		});	
				sl.on('change', function(slider, newValue, thumb, eOpts) { this.layerOpacityChange(cat, lay, newValue ); } , this);
	    	     	 		
				
				var menuTrasparenza = Ext.create('Ext.menu.Item',
												{
													text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnutrasp"),
													menu: {
														items: [sl],
										    	    	 cls: "clearCSS"
														}
													});		
				menu.add(menuTrasparenza);
				
				// Abilita miglioramento immagine se browser lo supporta
				if (Modernizr.cssfilters) {
					
					var enhanceEffects = ToloParamsJS.enhanceEffects;
					var menuEnhance = Ext.create('Ext.menu.Item',
							{
								text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuenha"),
								menu: {
									
					    	    	 cls: "clearCSS"
									}
								});		
					
					for (var i=0; i<enhanceEffects.length; i++) {
						var effect = enhanceEffects[i];
						var layInfoValue =  layInfo[effect.key];
						//alert('pippo');
						//alert ((layInfoValue!=undefined && layInfoValue!=null )?  layInfoValue * effect.scale : effect.defaultValue * effect.scale); 
						var menuBuff = Ext.create('Ext.menu.Item',
							{
								text: effect.getName(),
								menu: {
									items: [new Ext.slider.Single({
							     	 		    vertical: false,
							      	 		    value: (layInfoValue!=undefined && layInfoValue!=null )?  layInfoValue * effect.scale : effect.defaultValue * effect.scale ,
							      	 		    //increment: 10,
							      	 		    effect: effect,
							      	 		    minValue: effect.minValue,
							      	 		    maxValue: effect.maxValue * effect.scale,
							      	 		    listeners: {
							      	 		    		change: { fn: function(slider, newValue, thumb, eOpts){
							      	 		    								this.layerEnhanceChange(slider.effect, cat, lay, newValue );
							      	 		    							},
							      	 		    				  scope: this}
							      	 		    },
							      	 		    restoreDefaultValue: function() {
							      	 		    	this.setValue(this.effect.defaultValue * this.effect.scale );
							      	 		    }
							      	 		}),
							      	 		{
												xtype: 'menuitem',
												text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.mnuenha.reset"),
												listeners: {
														click: { fn: function(item, e, eOpts) {
																	var slider = item.up().down('slider');
																	slider.restoreDefaultValue();
																	},
																scope: this
														}
												}}
							      	 		],
					    	    	 cls: "clearCSS"
									}
								});		
						menuEnhance.menu.add(menuBuff)
						
					}
					menu.add(menuEnhance);
				}
				
				//}
			}
			
			if ((lay==null || this.paramsJS.isQGISExportable(nMappa, cat, lay))) {				
				if(this.paramsJS.layOut.conExportQGIS){
					menu.add(this.getExportMenuItem(cat, lay));
				}
				
				if(this.j2qConnected){
					menu.add({
		    			text: ToloI18n.getMsg("ToloTOCPanelExt.getContextMenu.addqgis"),
		    			iconCls : 'iconAddLayerToQGis',
						cls : "clearCss",					
		    			listeners: { click: {
		    							fn: function() { 
		    									this.fireEvent('addLayerToQgisClicked',cat,lay);																
		    							},
		    							scope: this
		    						}
		    					}
		    			});
				 }
			}
				    
		}
						
        return (menu.items.getCount()>0) ? menu : null;
		
	}, 
	
	
	
	/*
	 * @method getExportMenuItem
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 */
	getExportMenuItem: function(catIdx, layIdx) {
		
		return Ext.create('Ext.menu.Item', {
			text : ToloI18n.getMsg("ToloTOCPanelExt.getExportMenuItem.espo"),
			menu : {
				cls: 'clearCSS',
				items : [{
						xtype: 'menuitem',
						text: ToloI18n.getMsg("ToloTOCPanelExt.getExportMenuItem.espo.qgis"),
						iconCls : 'iconExportForQGis',
						menu:{
							cls: 'clearCSS',
							items: [
								{
									text: ToloI18n.getMsg("ToloTOCPanelExt.getExportMenuItem.espo.qgis180"),
			    	    			iconCls : 'iconExportForQGis',
									cls : "clearCss",
									listeners: { 								
										click: {
											fn: function() {     	    								
												this.fireEvent('exportForQgisClicked',null,null, "1.8.0");																												
											},
											scope: this
							    		}
							    		
									}	
								},{
									text: ToloI18n.getMsg("ToloTOCPanelExt.getExportMenuItem.espo.qgis2180"),
			    	    			iconCls : 'iconExportForQGis',
									cls : "clearCss",
									listeners: { 								
										click: {
											fn: function() {     	    								
												this.fireEvent('exportForQgisClicked',null,null, "2.18.0");																												
											},
											scope: this
							    		}
							    		
									}	
								}
							
							]
						}
					}]
			}
		});
		/*
		return Ext.create('Ext.menu.Item',
							{
								text: 'Export',
								menu: {
									items: [{
				    	    			text: 'Qgis',
				    	    			iconCls : 'iconExportForQGis',
										cls : "clearCss",
				    	    			listeners: { click: {
				    	    							fn: function() { 
				    	    									this.fireEvent('exportForQgisClicked',catIdx, layIdx);
				    	    							},
				    	    							scope: this
				    	    						}
				    	    					}
				    	    			}],
					    	    	 cls: "clearCSS"
									}
								});*/    	

	},
	
	/*
	 * @method layerOpacityChange
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 * @param {Object} newValue
	 * 
	 * 
	 */
	layerOpacityChange: function(catIdx, layIdx, newValue ) {
		//var nomeLayer = this.tocInfo.getCategoryInfo(cat).layers[lay].name;
		var oldValue = this.tocInfo.getCategoryInfo(catIdx).layers[layIdx].opacity;
		this.tocInfo.getCategoryInfo(catIdx).layers[layIdx].opacity=newValue/100;
    	this.fireEvent('layerOpacityChange', catIdx, layIdx, newValue/100, oldValue, this.tocInfo);

	},

	/**
	 * @method layerEnhanceChange
	 * @param {Object} effect
	 * @param {Object} catIdx
	 * @param {Object} layIdx
	 * @param {Object} newValue
	 * 
	 */
	layerEnhanceChange: function(effect, catIdx, layIdx, newValue ) {
		//var nomeLayer = this.tocInfo.getCategoryInfo(cat).layers[lay].name;
		var oldValue = this.tocInfo.getCategoryInfo(catIdx).layers[layIdx][effect.key];
		var newValueScaled = newValue/effect.scale;
		this.tocInfo.getCategoryInfo(catIdx).layers[layIdx][effect.key] = newValueScaled;
    	this.fireEvent('layerEnhanceChange', effect, catIdx, layIdx, newValueScaled, oldValue, this.tocInfo);

	},
	
	/*
	 * @method calcolaIconCls
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 * @param {Object} visible
	 * 
	 * 
	 */
    calcolaIconCls: function(catIdx, layIdx, visible ) {
    	var bConIcone = false;
    	
    	if (layIdx==null) {
    		// Categorie
    		//var cat = this.tocInfo.getCategoryInfo(catIdx);
	    	var iconCls="iconLegendaCategoria";
    	} else {
    		// Layer
			var lay = this.tocInfo.getCategoryInfo(catIdx).layers[layIdx];
	    	
	    	var iconCls="";
	    	if (lay.temporaryNotAvailable) {
	    		// se necessario warning accende icona su questo layer anche se non sarebbero normalemente visibili
	    		bConIcone = true;
	    		iconCls = "iconLegendaWarning";
	    	} else {
	    		if (visible) {
	    			iconCls = (lay.raster) ? "iconLegendaLayerRaster" : "iconLegendaLayerVector" ;	
	    		} else {
	    			iconCls = "iconLegendaLayerNotVisibleAtScale";
	    		}
	    		
	    	}
    	}
	
    	return { iconCls: iconCls, forzaConIcone: bConIcone };
    }, 
    
	/*
	 * @method calcolaLayerClickUrl
	 * 
	 * 
	 * @param {Object} lay
	 * 
	 * 
    calcolaLayerClickUrl: function (lay) {
    	
    	    	
    	if (lay.clickUrl) {
    		var params = "";
        	params += (lay.codTPN) ? "codTPN=" + lay.codTPN : "";
        	params += ((params!="") ? "&" : "") + "catIdx=" +lay.catTreeIdx;  
        	params += ((params!="") ? "&" : "") + "layIdx=" +lay.layTreeIdx;
        	
        	
        	
        	
        	var retVal = lay.clickUrl;
        	if (params!="") {
        		retVal += ((retVal.indexOf("?")==-1) ? "?" : "&")  + params ;	
        	} 
        	
        	return retVal;
    	} else {
    		return undefined;
    	}
    },
	 */
    
	/*
	 * @method calcolaCatToolTip
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 */
    calcolaCatToolTip: function (catIdx) {
    	
    	var cat = this.tocInfo.getCategoryInfo(catIdx)
    	var retVal = "";
    	
    	if (cat.toolTip && cat.toolTip!="") {
    		retVal = cat.toolTip;
    	} else {
    		retVal += '<ul class="legendaCatTooltip">';
    		for (var i=0; i<cat.categories.length; i++) {
    			if (cat.hidden!=undefined && cat.hidden!=null && cat.hidden==false) retVal += '<li class="legendaCatTooltipCat">' + cat.categories[i].catDescr + "</li>";
			}
    		for (var i=0; i<cat.layers.length; i++) {
    			var classCss = (cat.layers[i].raster && cat.layers[i].raster==true) ? "legendaCatTooltipLayVect" : "legendaCatTooltipLayRast";
    			if (cat.layers[i].hidden!=undefined && cat.layers[i].hidden!=null && cat.layers[i].hidden==false) retVal += '<li class="' + classCss + '">' + cat.layers[i].descr + "</li>";
			}
    	}
    	    		
        return retVal;
    },
    
	/*
	 * @method calcolaLayToolTip
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 */
    calcolaLayToolTip: function (catIdx, layIdx) {
    	
    	var lay = this.tocInfo.getCategoryInfo(catIdx).layers[layIdx]
    	var layerPresetInfo = this.tocInfo.getCategoryPresetInfo(catIdx).layerList[layIdx];
    	
    	var smin = (layerPresetInfo) ? layerPresetInfo.scalaMinima : -1;
    	var smax = (layerPresetInfo) ? layerPresetInfo.scalaMassima : -1;
    	
    	var scala = this.getScaleMinMax( smin, smax, lay.minScaleMin, lay.maxScaleMax);
		
		var scalaMinima  = scala.scalaMinima;
		var scalaMassima = scala.scalaMassima;
		
    	
    	var retVal = undefined;
    	    	
    	if (lay.toolTip && lay.toolTip!="") {
    		retVal = lay.toolTip;
    	} else {
    		retVal = lay.descr;
    	}
    	
    	if (scalaMassima!=undefined && scalaMassima!=null && scalaMassima!=-1) {
    		retVal += ToloI18n.getMsg("ToloTOCPanelExt.calcolaLayToolTip.smax", {scalaMassima: Math.round(scalaMassima)});  
    	}
    	
    	if (scalaMinima!=undefined && scalaMinima!=null && scalaMinima!=-1 && scalaMinima!=0) {
    		retVal += ToloI18n.getMsg("ToloTOCPanelExt.calcolaLayToolTip.smin", {scalaMinima: Math.round(scalaMinima)})  
    	}
    	
        return retVal;
    },
    
	/*
	 * @method onItemSelected
	 * 
	 * 
	 * @param {Object} catTreeIdx
	 * 
	 * 
	 * @param {Object} layTreeIdx
	 * 
	 * 
	 * @param {Object} classi
	 * 
	 * 
	 */
    onItemSelected: function(catTreeIdx, layTreeIdx, classi) {
    	this.fireEvent('itemSelected', catTreeIdx, layTreeIdx, classi);
    },
    
	/*
	 * @method onItemClicked
	 * 
	 * 
	 * @param {Object} catTreeIdx
	 * 
	 * 
	 * @param {Object} layTreeIdx
	 * 
	 * 
	 * @param {Object} classi
	 * 
	 * 
	 * @param {Object} e
	 * 
	 * 
	 */
    onItemClicked: function(catTreeIdx, layTreeIdx, classi, e) {
    	if (classi!=null) {
    		this.openStyleManager(catTreeIdx, layTreeIdx);
    	}
    	this.fireEvent('itemClicked', catTreeIdx, layTreeIdx, classi, e);
    },
    
    /**
     * @method openStyleManager
     * Richiede l'apertura della finestra di scelta di uno stile alternativo
     * 
  	 * @param {Object} catIdx
  	 * 
  	 * 
  	 * @param {Object} layIdx
  	 * 
  	 * 
     */
    openStyleManager: function(catIdx, layIdx) {
    	var cat = this.tocInfo.getCategoryInfo(catIdx);
    	var lay = cat.layers[layIdx];
    	
		if (this._chkIfStyleManagerAvailable(lay)) {
			this.fireEvent('stylePanelRequest', 
							lay,
							catIdx,
							layIdx,
							lay.definedStyles
							)
		}
	},
	
    /**
     * @method _chkIfStyleManagerAvailable
     * @private
     * 
     * 
  	 * @param {Object} layInfo
  	 * 
  	 * 
     */
	_chkIfStyleManagerAvailable: function(layInfo) {
		return layInfo.styleCapable && layInfo.definedStyles.length>0;
	},
	
	/**
	 * @method addCategory
	 * Aggiunge una categoria nella posizione indicata dai parametri.
	 * 
	 * @param {Object} catInfo
	 * Categoria da aggiungere
	 * 
	 * @param {Object} addPointCatIdx
	 * categoria prima o dopo della quale va aggiunta la roba
	 * 
	 * @param {Object} bBefore
	 * indica se aggiungere prima o dopo 
	 * 
	 */
	addCategory: function(catInfo, addPointCatIdx, bBefore) {
		
		var addPointCat = this.tocInfo.getCategoryInfo(addPointCatIdx);
		var addPointCatId = addPointCat.catId;
		
		// TODO Gestisce solo mappa 0
		var nMappa = 0;
		
		// Setto flag che indica che è una categoria utente
		catInfo.paramsJSParams.isUserCategory = true;
		catInfo.tocInfoParams.isUserCategory  = true;

		
		this.paramsJS.addCategory(nMappa, catInfo.paramsJSParams, addPointCatIdx, bBefore);
		this.tocInfo.addCategory(catInfo.tocInfoParams, addPointCatIdx, bBefore);
		
		this.addCategoryExtend(catInfo, addPointCat, bBefore);
		
		// Se c'è un ordine diverso da quello iniziale lo ricrea da zero (la nuova categoria provoca rinumerazione 
		if (this.tocInfo.layerOrder && this.tocInfo.layerOrder.length>0) this.tocInfo.layerOrder = this.createOrRefreshLayerOrder();
		
		this.fireEvent('categoryAdded', this.tocInfo, catInfo);
		
	},
	
	/**
	 * @method addCategoryExtend
	 * Metodo astratto da estendere per gestire a livello di singola tipologia di legenda l'aggiunta della categoria per esempio nella GUI.
	 * 
 	 * @param {Object} catInfo
 	 * Categoria da aggiungere
 	 * 
	 * @param {Object} bBefore
	 * indica se aggiungere prima o dopo
 	 * 
	 * @param {Object} addPointCat
	 * categoria prima o dopo della quale va aggiunta la roba
 	 * 
	 */
	addCategoryExtend: function(catInfo, addPointCat, bBefore ) {
		
	},
	
	/**
	 * @method addLayer
	 * Aggiunge un layer nella posizione indicata dai parametri.
	 * 
	 * @param {Object} options
	 * oggetto contenente le opzioni nei seguenti attributi <br />
	 * <ul>
	 * 	<li>serverurl: url del server WMS</li>
	 * 	<li>layername: nome del layer WMS</li>
	 * 	<li>addPointCatIdx: Indice della categoria nella quale viene aggiunto il layer </li>
	 * 	<li>addPointLayIdx: Indice layer prima o dopo del quale aggiungere il nuovo layer</li>
	 *  <li>where: indica se aggiungere prima (0), dopo (1), dentro (2)</li>
	 * </ul>
	 * 
	 */
	addLayer: function(options) {
		
		this.dataProvider.requestLayerInfoData({ 
				serverurl: options.serverurl, //"http://dvpgeoserver.comune.prato.it/geoserver/ows?service=wms&version=1.1.1&request=GetCapabilities",
				layername: options.layername,  //"comunepo:generica_circoscrizioni"
				extra: {
						serverurl: options.serverurl,
						layername: options.layername,
						addPointCatIdx: options.addPointCatIdx,
	 					addPointLayIdx: options.addPointLayIdx, 
	 					bBefore: options.where
					}
		});
		
	},
	
	/**
	 * @method addLayerCallback
	 * Funzione di callback per la richiesta di informazioni di un layer da inserire
	 * 
	 * @param {Object} layTocInfo
	 * Informazioni sul layer (struttura TOCLayerBean) ricevuta dal server
	 * 
	 * @param {Object} extra
	 * parametri extra
	 * 
	 */
	addLayerCallback: function(layTocInfo, extra) {
	
		var paramsInfo= {
			serverID: null,
			name: extra.layername,
		    hidden: false,
		    userSwitchable: true,
		    codTPN: 0,
		    defaultLayer: true,
		    withOpacitySettings: false,
		    iconaLegenda: null,
		    descrizione: layTocInfo.descr,
		    defaultStyle: "",
		    scalaMinima: -1,  // Influenza solo lo stato della visualizzazione nella legenda (nero o grigio), non l'effettiva visualizzazione.
		    								    // Utilizzato solo nel caso di WMS generico o di GEOSERVER senza REST, cioè nei casi nei quali non è possibile ottenere lo stato di visibilità in funzione della scala. Ignorato negli altri casi 
			scalaMassima: -1,  // Influenza solo lo stato della visualizzazione nella legenda (nero o grigio), non l'effettiva visualizzazione.
		     									// Utilizzato solo nel caso di WMS generico o di GEOSERVER senza REST, cioè nei casi nei quali non è possibile ottenere lo stato di visibilità in funzione della scala. Ignorato negli altri casi
			extraLegendGraphPar: null, 	
			catTreeIdx: null,
			layTreeIdx: null,
			raster: false,
			clickUrl: null,
			clickTarget: null,
			stiliIndipDaScala: true,
			toolTip: null,
			expanded: false,
			opacity: null,
			itemType: 'layer',
			queryable: layTocInfo.queryable,
			getFeatureInfoFormats: layTocInfo.getFeatureInfoFormats
		};
		
		var serverInfo = {		
			id						: null,
			nome 					: null,
			typeDescription       	: "WMS",
			typeCode             	: 11,
			nomeCredenziale   		: null,
			allowServerConnection 	: true,
			url          		  	: extra.serverurl,
			usaProxyPer3D			: true,
			serverOpts				: null,
			tilesMultiple 			: false,
			tileStampaAltezza		: null,
			tileStampaLarghezza		: null,
			noTolomeoParams			: false,
			opacity					: 1.0
		}
	
		this.addLayerByLayerInfo(paramsInfo, layTocInfo, serverInfo, extra.addPointCatIdx, extra.addPointLayIdx, extra.bBefore);
		
	},
	
	/**
	 * @method addLayerByLayerInfo
	 * Aggiunge un layer nella posizione indicata dai parametri. Se addPointLayIdx non è definito il punto di inserimento è una categoria
	 * 
	 * @param {Object} paramsInfo
	 * Info relative al layer da inserire in paramsJS
	 * 
	 * @param {Object} layTocInfo
	 * Tocinfo del layer da aggiungere
	 * 
	 * @param {Object} serverInfo
	 * Info sul server del layer da inserire
	 * 
	 * @param {Object} addPointCatIdx
	 * Indice della categoria nella quale viene aggiunto il layer 
	 * 
	 * @param {Object} addPointLayIdx
	 * Indice layer prima o dopo del quale aggiungere il nuovo layer
	 * 
	 * @param {Object} bBefore
	 * indica se aggiungere prima o dopo
	 */
	addLayerByLayerInfo: function(paramsInfo, layTocInfo, serverInfo, addPointCatIdx, addPointLayIdx, bBefore) {

		// Recupera categoria e layer di inserimento
		var addPointCat = this.tocInfo.getCategoryInfo(addPointCatIdx);
		var addPointLay = addPointCat.layers[addPointLayIdx];
		
		// Completa informazioni 
		layTocInfo.parentcategorychecked = addPointCat.checked;
		layTocInfo.layId = Math.random();
		layTocInfo.isEnabled = function(){
			if (this.parentcategorychecked){
				return this.parentcategorychecked && this.checked; 
			}else{
				return this.checked;
			}
		}
					
		//var layer = this.tocInfo.getCategoryPresetInfo(catIdx).layerList[layIdx];
					
		//var smin = (layer) ? layer.scalaMinima : -1;
    	//var smax = (layer) ? layer.scalaMassima : -1;
				
		//lay.visible=this.layerIsVisibleAtScale(scale, smin, smax, lay.minScaleMin, lay.maxScaleMax );

		// Assegna un codTPN
		var newLocalCodTPN = this.getNewLocalCodTPN();
		paramsInfo.codTPN = newLocalCodTPN;
		layTocInfo.codTPN = newLocalCodTPN;
		
		// Setto flag che indica che è un layer utente
		paramsInfo.isUserLayer = true;
		layTocInfo.isUserLayer = true;

		// TODO Gestisce solo mappa 0
		var nMappa = 0;
		
		// Verifica se il server è già definito, e se non è definito lo definisce
		var serverID = this.paramsJS.addServer(nMappa, serverInfo);
		
		// Associa il layer con il serverID corretto
		layTocInfo.serverID = serverID;
		paramsInfo.serverID = serverID;
		
		this.paramsJS.addLayer(nMappa, paramsInfo, addPointCatIdx, addPointLayIdx, bBefore);
		// Aggiorna la struttura tocInfo associata a questa legenda
		this.tocInfo.addLayer(layTocInfo, addPointCatIdx, addPointLayIdx, bBefore);
		// Invoca il metodo che consente ad una legenda che estende questa di aggiornarsi con il nuovo layer 
		this.addLayerExtend(paramsInfo, layTocInfo, serverInfo, addPointCat, addPointLay, bBefore);
		
		// Se c'è un ordine diverso da quello iniziale lo ricrea da zero
		if (this.tocInfo.layerOrder && this.tocInfo.layerOrder.length>0) this.tocInfo.layerOrder = this.createOrRefreshLayerOrder();

		this.fireEvent('layerAdded', this.tocInfo, paramsInfo, layTocInfo, serverInfo);

		// Se il server non è disponibile. Deve essere fatto dopo aver lanciato l'evento per fare in modo che tutto sia aggiornato 
		if (layTocInfo.temporaryNotAvailable) {
			this.setLayerStateChange(layTocInfo.catIdx, layTocInfo.layIdx, false, true);	
		}
		
	},
	
	/**
	 * @method addLayerExtend
	 * Metodo astratto da estendere per gestire a livello di singola tipologia di legenda l'aggiunta della categoria per esempio nella GUI.
	 * 	 
	 * @param {Object} paramsInfo
	 * Info relative al layer da inserire in paramsJS
	 * 
	 * @param {Object} layTocInfo
	 * Tocinfo del layer da aggiungere
	 * 
	 * @param {Object} serverInfo
	 * Info sul server del layer da inserire	 * @param {Object} addPointCat Punto di inserimento (Categoria) 
	 * 
	 * @param {Object} addPointLay
	 * Punto di inserimento (Layer)
	 * 
	 * @param {Object} bBefore
	 * indica se aggiungere prima o dopo
	 * 
	 */
	addLayerExtend: function(paramsInfo, layTocInfo, serverInfo, addPointCat, addPointLay, bBefore) {
	
	},
	
	/**
	 * @method onTOCInfoCatTreeIdxUpdate
	 * Metodo invocato al cambio dell'indice di categoria su tocInfo. I diversi tipi di legenda devono implementarlo per gestire l'aggiornamento sulla GUI della legenda
	 * 
	 * @param {Object} catId
	 * Id della categoria
	 * 
	 * @param {Object} oldCatTreeIdx
	 * indice che è stato cambiato
	 * 
	 * @param {Object} newCatTreeIdx
	 * nuovo valore dell'indice
	 * 
	 */
	onTOCInfoCatTreeIdxUpdate: function(catId, oldCatTreeIdx, newCatTreeIdx) {
		
	},
	
	/**
	 * @method
	 * Metodo invocato al cambio dell'indice di layer su tocInfo. I diversi tipi di legenda devono implementarlo per gestire l'aggiornamento sulla GUI della legenda
	 * 
	 * @param {Object} oldCatTreeIdx
	 * vecchio indice categoria
	 * 
	 * @param {Object} oldLayTreeIdx
	 * indice layer che è stato cambiato
	 * 
	 * @param {Object} newCatTreeIdx
	 * nuovo indice categoria
	 * 
	 * @param {Object} newLayTreeIdx
	 * nuovo valore dell'indice di layer
	 * 
	 */
	onTOCInfoLayIdxUpdate: function(layId, catTreeIdx, oldLayTreeIdx, newCatTreeIdx, newLayTreeIdx) {
		
	},
		
	/** 
	 * @method getNewLocalCodTPN
	 * Genera un nuovo codTPN local
	 * 
	 * @return {Object}
	 * il nuovo codTPN locale
	 * 
	 */
	getNewLocalCodTPN: function() {
		
		this.lastUsedLocalCodTPN = (this.lastUsedLocalCodTPN) ? this.lastUsedLocalCodTPN + 1 : this.paramsJS.codTPNClientLayerBase + 1; //this.LOCALCODTPNBASE;
		
		return this.lastUsedLocalCodTPN; 
	
	},
	
	/**
	 * @method createOrRefreshLayerOrder
	 * Crea o ricrea la struttura layerOrder
	 * 
	 * @return {Object}
	 * restituisce la struttura layerOrder
	 * 
	 */
	createOrRefreshLayerOrder: function() {
		return this.createOrRefreshLayerOrderExtend();	
	},
	
	/**
	 * @method createOrRefreshLayerOrderExtend
	 * Metodo invocato per ricreare la struttura layerOrder,  I diversi tipi di legenda devono implementarlo per gestire la possibilità di scambiare l'ordine dei layer.
	 * 
	 * @returns {Object}
	 * deve ritornare 
	 * 
	 */
	createOrRefreshLayerOrderExtend: function() {
		
	},
	
	/**
	 * @method getUserWMSList
	 * restituisce un array di oggetti  che identificano i layer inseriti dall'utente (non presenti nel preset originale)
	 * 
	 * @return {Array}
	 * array di oggetti di questo tipo { catTreeIdx: '0/1', layTreeIdx: '0'}
	 * 
	 */
	getUserWMSList: function() {
	
		var userWMSList = [];		

		if (this.tocInfo.layerOrder && this.tocInfo.layerOrder.length>0) {
			
			for (var i = 0; i < this.tocInfo.layerOrder.length; i++) {
				var laypos = this.tocInfo.layerOrder[i];
				var cat = this.tocInfo.getCategoryInfo(laypos.cat);
				var lay = cat.layers[laypos.lay];
				if (lay.isUserLayer) {
					userWMSList.push({catTreeIdx: lay.catTreeIdx, layTreeIdx: lay.layTreeIdx});
				}
			}
		} else {
		
			this.tocInfo.onEachLayer(
				function(cat, lay, catIdx, layIdx) {
					if (lay.isUserLayer) {
						userWMSList.push({catTreeIdx: catIdx, layTreeIdx: layIdx});
					}
				}, this
			);

		}
		return userWMSList;
	},
	
	/**
	 * @method getVisibleUserWMSList
	 * Restituisce un array di oggetti  che identificano i layer inseriti dall'utente (non presenti nel preset originale) e che sono visibili (checked e visible)
	 * 
	 * @return {Array}
	 * array di oggetti di questo tipo { catTreeIdx: '0/1', layTreeIdx: '0'}
	 * 
	 */
	getVisibleUserWMSList: function() {
	
		var userWMSList = this.getUserWMSList();
		var retVal = [];
		for (var i = 0; i < userWMSList.length; i++) {
			var idxObj = userWMSList[i]; 
			var currCat = this.tocInfo.getCategoryInfo(idxObj.catTreeIdx);
			var currLayer =  currCat.layers[idxObj.layTreeIdx];	
			
			if ( this.tocInfo.areAllParentCatChecked(currLayer.catTreeIdx) && currCat.checked && currLayer.checked && currLayer.visible ){
				retVal.push({catTreeIdx: idxObj.catTreeIdx, layTreeIdx: idxObj.layTreeIdx});
			}
		}	
		return retVal;
	},
	
	
	/**
	 * @method saveToCommand
	 * Serializza lo stato della legenda in un comando toloCommand.tocCheck
	 * 
	 */
	saveToCommand: function() {
		
		var cmd = Ext.create('TolomeoExt.ToloCommand.tocCheck', {
								mode: 'uncheckOthers',
								ver: 3
				});
		
		this.tocInfo.onEachLayer(
				function(cat, lay, catIdx, layIdx) { 
					
					if (this.tocInfo.areAllParentCatChecked(catIdx)) {
						if (lay.itemType=='layer' && cat.checked && lay.checked && lay.hidden==false ) {
							var s = (lay.serverID=="") ?  "INLINESERVERID" : lay.serverID;
							cmd.addTocEntry(s, lay.name, true, lay.opacity, (lay.styleCapable?lay.style:false));
						} else {
							if (lay.itemType=='text') {
								cmd.addTocTextEntry(lay.descr);
							}
						} 
					}
					/*
					if (this.tocInfo.areAllParentCatChecked(catIdx) && cat.checked && lay.checked && lay.hidden==false && lay.itemType=='layer') {
						var s = (lay.serverID=="") ?  "INLINESERVERID" : lay.serverID;
						cmd.addTocEntry(s, lay.name, true, lay.opacity);	
					} */
					
				},
				this
			);
		
		return cmd;
	},
	
	enableJ2QFeatures: function(){
		this.j2qConnected = true;
	},
	
	updateCustomQuery: function(cq) {
		this.customQuery = cq;
	},
	
	onCategoryExpand: function(catIdx){
		if (this.tocInfo) {
			this.tocInfo.getCategoryInfo(catIdx).expanded=true;
		}
		this.fireEvent('categoryExpand', this.tocInfo, catIdx);
	},
	
	onCategoryCollapse: function(catIdx){
		if (this.tocInfo) {
			this.tocInfo.getCategoryInfo(catIdx).expanded=false;
		}
		this.fireEvent('categoryCollapse', this.tocInfo, catIdx);
	},
	
	onLayerExpand: function(catIdx, layIdx){
		if (this.tocInfo) {
			this.tocInfo.getCategoryInfo(catIdx).layers[layIdx].expanded=true;
		}
		this.fireEvent('layerExpand', this.tocInfo, catIdx, layIdx);
	},
	
	onLayerCollapse: function(catIdx, layIdx){
		if (this.tocInfo) {
			this.tocInfo.getCategoryInfo(catIdx).layers[layIdx].expanded=false;
		}
		this.fireEvent('layerCollapse', this.tocInfo, catIdx, layIdx);
	}
	
	
	/*,
	
	getLegendaInfo: function(posObj, scale) {
		var retVal = {};
		retVal.layers = [];
		
		var vl = this.getVisibleLayers();
		
		for (var i=0; i < vl.length; i++) {
			var li = {};
			var l = vl[i];
			li.nome = l.descr;
			// Aggiunge le classi
			li.classi = [];
			for (j=0; j < l.classi; j++) {
				var voceLegenda = this.createVoceLegenda(
									l.classi[j], 
									l);
				li.classi.push();
			}
			// Aggiunge le info per il layer corrente
			retVal.layers.push(li);
		}
		return retVal;
	}
	
	createVoceLegenda: function(classe, layer, posObj, scale) {
		var retVal = {};
		
		var src = classe.nome;
		var paramScala = "";
		var paramPos = "";
		var stile = (layer.style && layer.style!="") ? layer.style : "default";
		
		if (classe.tipoNome==1) {
			// Se legenda dipendente da scala
			if ( scale && layer.stiliIndipDaScala!=undefined && layer.stiliIndipDaScala==false && (classe.nomeParamScala) && (classe.nomeParamScala!="")) {
				paramScala = classe.nomeParamScala + "=" + scale;
				src += ((src.indexOf("?")!=-1) ? "&" : "?") + paramScala;
			}
			if (posObj && layer.stiliIndipDaPosizione!=undefined && layer.stiliIndipDaPosizione==false) {
				paramPos += "SRS=" + posObj.srid;
				paramPos += "&BBOX=" + posObj.bbox.left  
									+ ',' + posObj.bbox.bottom
									+ ',' + posObj.bbox.right
									+ ',' + posObj.bbox.top;
				paramPos += "&WIDTH="  + posObj.width;
				paramPos += "&HEIGHT=" + posObj.height;
				src += ((src.indexOf("?")!=-1) ? "&" : "?") + paramPos;
			}
			retVal.nomevoce = "";
			retVal.url = src;
		} else {
			retVal.nomevoce = classe.nome;
			retVal.url = classe.icoUrl;
		}
		
		return retVal;
	}
	*/
	
});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloTreeTOCPanelExt
 * 
 * 
 */
Ext.define('TolomeoExt.ToloTreeTOCPanelExt', {

	extend: 'TolomeoExt.ToloTOCPanelExt',
	
	alias: 'widget.tx_toloTreeTOCPanelExt',
	
	//requires: [],
 	
	/** 
	 * @property {Object} tree
	 * 
	 * 
	 */
	tree: null,

	/** 
	 * @property {Object} treeRootNode
	 * 
	 * 
	 */
	treeRootNode: null,
		 
	/** 
	 * @property {Object} treeColumn
	 * 
	 * 
	 */
	treeColumn: null,
	
	/** 
	 * @property {Object} filterVal
	 * 
	 * 
	 */
	filterVal: null,
	
	/** 
	 * @property {Object} waitMsgBox
	 * 
	 * 
	 */
	waitMsgBox: null,
	
	/**
	 * @method initComponent
	 * Metodo relativo alla gestione Ext.
	 * 
	 */
	initComponent: function(){
        
		var me = this;
		this.layout = 'fit';
		
		//Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);	
		var thisPanel = this;
		this.tbar =[{ xtype: 'button',
						  iconCls: 'legendaBtnExpand',
						  //text: 'Apri',
						  tooltip: ToloI18n.getMsg("ToloTreeTOCPanelExt.apreTutto"),
						  listeners: { 'click': { fn: this.expandNodes,
							  						
							  				   scope: this}
						  			}
					},
					{ xtype: 'button',
								  iconCls: 'legendaBtnCollapse',
					  //text: 'Chiudi',
					  tooltip: ToloI18n.getMsg("ToloTreeTOCPanelExt.chiudeTutto"),
					  listeners: { 'click': { fn: function() { this.treeRootNode.collapseChildren(true); }, 
						  				   scope: this}
					  			}
					},
					ToloI18n.getMsg("ToloTreeTOCPanelExt.filtro"),
		            {
						xtype:'trigger',
						width: 100,
						triggerCls:'x-form-clear-trigger',
						onTriggerClick:function() {
										this.setValue('');
										thisPanel.filterClear();
										thisPanel.treeRootNode.collapseChildren(true);
								},
								enableKeyEvents:true,
								listeners:{
									keyup:{
										buffer:200,		
										fn:function(field, e) {
											if(Ext.EventObject.ESC == e.getKey()) {
												field.onTriggerClick();
											} else {
												var val = this.getRawValue();
												me.filterVal = val;
												thisPanel.filterClear();
												thisPanel.filterTree(val);
												
											}
										}
									}
								}
		            }];
		
		this.callParent();
		
		var mnuItems = [];
		if (this.paramsJS.layOut.conExportQGIS) {
			mnuItems.push(this.getExportMenuItem(null, null));
		}				
				
		var model =  Ext.define('User', {
		     extend: 'Ext.data.Model',
		     fields: [
		              {name: 'disabled', type: 'boolean', useNull: true, defaultValue: null },
		              {name: 'text', type: 'string', useNull: true, defaultValue: null},
		              {name: 'hidden', type: 'boolean', useNull: true, defaultValue: null},
		              {name: 'lay', type: 'string', useNull: true, defaultValue: null},
		              {name: 'cat', type: 'string', useNull: true, defaultValue: null},
		              {name: 'classi', type: 'string', useNull: true, defaultValue: null},
		              {name: 'catId', type: 'string', useNull: true, defaultValue: null},
		              {name: 'layId', type: 'string', useNull: true, defaultValue: null},
		              {name: 'temporaryNotAvailable', type: 'boolean', useNull: true, defaultValue: null},
		              {name: 'codTPN', type: 'string', useNull: true, defaultValue: null},
		              {name: 'withOpacitySettings', type: 'boolean', useNull: true, defaultValue: null},
		              {name: 'ownerTOC', type: 'auto', useNull: true, defaultValue: null},
		              {name: 'style', type: 'string', useNull: true, defaultValue: null},
		              {name: 'itemType', type: 'string', useNull: true, defaultValue: null},
		              //{name: 'icon', type: 'string', useNull: true, defaultValue: null},
		              // Campi utilizzati via codice js per modifica visualizzazione nodo
		              {name: 'nodeFiltered', type: 'boolean', useNull: true, defaultValue: false},				// True se il nodo è filtrato da funzione di ricerca (non risponde a criterio di ricerca 
		              {name: 'nodeVisibleAtScale', type: 'boolean', useNull: true, defaultValue: true},			// True se il nodo visibile alla scala attuale
		              {name: 'nodeWithUnfilteredChild', type: 'boolean', useNull: true, defaultValue: false}	// True se il nodo ha almeno un figlio non filtrato 
		     ]
		 });


		this.treestore = Ext.create('Ext.data.TreeStore', {
			proxy: {
				type: 'memory'
			},
		    root: {
		        expanded: true, 
		        text: 'root nascosta'//,
		    }, 
		    data: [],
		    autoSync: true,
		    autoLoad: false,
		    model: model
		});        
		
		this.treeRootNode = this.treestore.getRootNode();

		this.treeColumn = Ext.create('TolomeoExt.ToloTOCNodeColumnExt4', { 
									dataIndex:'text', 
									width    : Ext.isIE6 ? '100%' : 10000 // IE6 needs width:100%
							});

        this.tree = Ext.create('Ext.tree.TreePanel', {
        	store: this.treestore,
			useArrows: true,
		 	rootVisible: false,
		 	animate: true,
			border: false,
			autoScroll : true,
			hideHeaders: true,
			cls: Ext.baseCSSPrefix + 'autowidth-table',
			viewConfig: {
			    plugins: { ptype: 'treeviewdragdrop' }
			},
		    columns: [this.treeColumn]
		});
        var view = this.tree.getView();
        var me =  this;
        Ext.apply(view, {
            //if our function returns false, the original function is not called
            onCheckChange: Ext.Function.createInterceptor(view.onCheckChange,me.onCheckChangeInterceptor,me),
            getRowClass: Ext.Function.bind(me.getRowClass,me)
        });        

        this.tree.getView().on('checkchange', this.checkChange, this);
        this.tree.getView().on('beforedrop',this.nodedragover, this);
        this.tree.getView().on('drop', this.nodeDrop, this);
		this.tree.getView().on('itemclick', this.onItemClicked, this);
        this.tree.getView().on('rowfocus', this.onItemSelected, this);
        
        this.tree.getView().on("afteritemcollapse", function( node, index, item, eOpts ){ 
        												if (node.get("lay")==undefined || node.get("lay")==null){
        													// Nodo castegoria
        													thisPanel.onCategoryCollapse(node.get("cat"));
        												} else {
        													// Nodo layer
        													thisPanel.onLayerCollapse(node.get("cat"), node.get("lay"));
        												}
        												 
        												});
                                
        this.tree.getView().on("afteritemexpand", function( node, index, item, eOpts ){
											        	if (node.get("lay")==undefined || node.get("lay")==null){
															// Nodo castegoria
											        		thisPanel.onCategoryExpand(node.get("cat"));
														} else {
															// Nodo layer
															thisPanel.onLayerExpand(node.get("cat"), node.get("lay"));
														}
        												});
        
        this.tree.on('cellcontextmenu', this.menuShow, this);
        
        //this.add({html: "Attendere prego"});
        
        this.add(this.tree);
        
        this.on('afterrender', function() {
        	if (!this.isTOCGUICreated) {
				this.waitMsgBox = new Ext.LoadMask(this, {msg:"Attendere prego..."});
				this.waitMsgBox.show();
        	}
        }, this, {single: true})
        
    },
    
	/**
	 * @method onItemSelected
	 * 
	 * 
	 * @param {Object} record
	 * 
	 * 
	 * @param {Object} item
	 * 
	 * 
	 * @param {Object} index
	 * 
	 * 
	 */
    onItemSelected: function(record, item, index) {
    	this.callParent([record.get('cat'), record.get('lay'), record.get('classi')]);
    	
    },
    
	/**
	 * @method onItemClicked
	 * 
	 * 
	 * @param {Object} view
	 * 
	 * 
	 * @param {Object} record
	 * 
	 * 
	 * @param {Object} item
	 * 
	 * 
	 * @param {Object} index
	 * 
	 * 
	 * @param {Object} e
	 * 
	 * 
	 * @param {Object} eOpts
	 * 
	 * 
	 */
    onItemClicked: function(view, record, item, index, e, eOpts) {
    	this.callParent([record.get('cat'), record.get('lay'), record.get('classi'), e]);
    },
    
	/**
	 * @method getRowClass
	 * 
	 * 
	 * @param {Object} record
	 * 
	 * 
	 * @param {Object} index
	 * 
	 * 
	 * @param {Object} rowParams
	 * 
	 * 
	 * @param {Object} ds
	 * 
	 * 
	 */
	getRowClass: function(record,index,rowParams,ds){
	
    	var cls = '';
    	
		if (record.get('hidden')) cls+=' tocitemhidden ';
		if (!record.get('nodeVisibleAtScale')) cls+=' tocitemnotvisible ';
		if (!record.get('nodeWithUnfilteredChild') && record.get('nodeFiltered')) cls+=' tocitemfiltered ';
		if (record.get('disabled')) cls+=' tree-node-disabled ';
		if (record.get("itemType")=='separator') {
			cls += " legendaSeparatorRow ";
		}
    	
	    return cls;
	},    

	/**
	 * @method onCheckChangeInterceptor
	 * Consente di impedire il cambio di check state nel caso sia disabilitato
	 * 
	 * @param {Object} record
	 * 
	 * 
	 */
    onCheckChangeInterceptor: function(record) {
    	if (record.get('disabled')){
            return false;
        } 
    },

	/**
	 * @method expandNodes
	 * 
	 * 
	 */
    expandNodes: function() {
    	Ext.suspendLayouts();
		this.treeRootNode.cascadeBy(function(nd) {
				if (nd.get('lay')==null) {
					nd.expand(false, false);
				}});
		Ext.resumeLayouts(true);
	},

	/**
	 * @method filterTree
	 * 
	 * 
	 * @param {Object} val
	 * 
	 * 
	 */
    filterTree: function(val) {
    	Ext.suspendLayouts();

    	var re = new RegExp('.*' + val + '.*', 'i');
		this.treeRootNode.cascadeBy(
    		function(n) {
    			if (n.get('classi')!=null) return false; // se stiamo esaminando il nodo finale (legenda) smettere per non farlo partecipare alla ricerca
    			if (!n.get('hidden')) {
	    			if (re.test(n.get('text')) ) {
	    				this.setNodeFiltered(n, false);
	    				return true;
	    			} else {
	    				if (n!== this.treeRootNode)	this.setNodeFiltered(n, true);
	    				return true;  // continuo ad attraversare l'albero perchè se qualche figlio è attivo va attivato anche questo nodo
	    			}
    			}
    		}, this
    	);
		
		Ext.resumeLayouts(true);
		
		this.expandNodes();
		    	
    },
    
	/**
	 * @method setNodeFiltered
	 * 
	 * 
	 * @param {Object} node
	 * 
	 * 
	 * @param {Object} dafiltrare
	 * 
	 * 
	 */
    setNodeFiltered: function(node, dafiltrare) {
        
        node.set('nodeFiltered', dafiltrare);
        	
        // Aggiorna flag nodeWithUnfilteredChild in tutta la gerarchia a salire
       	var p = node.parentNode;
       	var bContinua = true;
        while(p && bContinua) {
        	
        	// controllo tutti i figli p.get('nodeWithUnfilteredChild')
        	var withUnfiltered = false;
        	for (var i = 0; i< p.childNodes.length ; i++) {
        		if (p.childNodes[i].get('classi')==null && p.childNodes[i].get('hidden')==false && (!p.childNodes[i].get('nodeFiltered') || p.childNodes[i].get('nodeWithUnfilteredChild') )) { 
        			withUnfiltered=true;
        			break;
        		}
        	}
        	
        	if (p.get('nodeWithUnfilteredChild')!= withUnfiltered ) {
        		p.set('nodeWithUnfilteredChild', withUnfiltered);
        	} else {
        		bContinua=false;
        	}
        		
        	p = p.parentNode;
		}        	
        
    },    

	/**
	 * @method filterClear
	 * 
	 * 
	 */
    filterClear: function() {
    	Ext.suspendLayouts();
    	this.treeRootNode.cascadeBy(
        		function(n) {
        				if (!n.get('hidden')) {
        					n.set('nodeWithUnfilteredChild', false);
        					n.set('nodeFiltered', false);
   	                     }
        				
        		}, this
        	);
    	Ext.resumeLayouts(true);
    },
    
	/**
	 * @method menuShow
	 * 
	 * 
	 * @param {Object} view
	 * 
	 * 
	 * @param {Object} td
	 * 
	 * 
	 * @param {Object} cellIndex
	 * 
	 * 
	 * @param {Object} record
	 * 
	 * 
	 * @param {Object} tr
	 * 
	 * 
	 * @param {Object} rowIndex
	 * 
	 * 
	 * @param {Object} e
	 * 
	 * 
	 * @param {Object} eOpts
	 * 
	 * 
	 */
	menuShow : function( view, td, cellIndex, record, tr, rowIndex, e, eOpts ){
    	
		var menu = this.getContextMenu(record.get('cat'), record.get('lay'));
		if (menu) {
    		menu.showAt(e.getXY()); //node.ui.getAnchor()
    		
		}
		e.preventDefault();
   },
    
	/**
	 * @method nodedragover
	 * 
	 * 
	 * @param {Object} node
	 * 
	 * 
	 * @param {Object} data
	 * 
	 * 
	 * @param {Object} overModel
	 * 
	 * 
	 * @param {Object} dropPosition
	 * 
	 * 
	 * @param {Object} dropHandlers
	 * 
	 * 
	 * @param {Object} eOpts
	 * 
	 * 
	 */
    nodedragover: function(node, data, overModel, dropPosition, dropHandlers, eOpts ) { //dropEvent
    	if (dropPosition == 'append')  
    		return false;
    	
    	if(overModel.parentNode.id!=data.records[0].parentNode.id)
    		return false;
    	
    },
    
	/**
	 * @method nodeDrop
	 * 
	 * 
	 * @param {Object} node
	 * 
	 * 
	 * @param {Object} data
	 * 
	 * 
	 * @param {Object} overModel
	 * 
	 * 
	 * @param {Object} dropPosition
	 * 
	 * 
	 * @param {Object} eOpts
	 * 
	 * 
	 */
    nodeDrop: function( node, data, overModel, dropPosition, eOpts ) { 
    	// Andrebbe fatto in modo da garantirsi che l'albero venga percorso nell'ordine giusto
    	this.tocInfo.layerOrder = this.createOrRefreshLayerOrder();
    	this.layerOrderChange();
    	
    },
    
	/**
	 * @method createOrRefreshLayerOrderExtend
	 * 
	 * 
	 */
    createOrRefreshLayerOrderExtend: function() {
    	// Andrebbe fatto in modo da garantirsi che l'albero venga percorso nell'ordine giusto
    	var layerOrder = [];
    	this.treeRootNode.cascadeBy(
			function(nd) {
				if ( nd.get('classi')==null && nd.get('lay')!=null) {
					layerOrder.push({cat: nd.get('cat'), lay: nd.get('lay')});
				}
				return true;
			},
			this
		);
    	
		return layerOrder;
    },
    
    /**
     * @method checkChange
     * 
     * 
     * @param {Object} node
     * node.
     * 
     * @param {Object} checked
     * checked.
     * 
     */
    checkChange: function( node, checked ) {
    	
    	var cat = node.get('cat');
    	var lay = node.get('lay');
    	
    	// Se il click e' su una categoria vengono disabilitati o abilitati tutti i layer della categoria stessa
    	if (lay==null) {
    		this.setCategoryStateChange(cat, checked);
    		
    	} else {
    		this.setLayerStateChange(cat,lay, checked);
    	}    	
    	
    },
    
  /**
	 * @method setLayerStateChange
	 * Aggiorna lo stato di un layer a livello di treePanel quando viene notificata
	 * una modifica dalla classe padre.
	 * 
	 * @param {Object} cat
	 * 
	 * 
	 * @param {Number} lay
	 * layer della tocInfo
	 * 
	 * @param {Object} checked
	 * 
	 * 
	 */
	setLayerStateChange : function(cat, lay, checked) {
		 this.callParent(arguments);
		if (this.tocInfo != null && lay != null) {
			var layer = this.tocInfo.getCategoryInfo(cat).layers[lay];
			var layerNode = this.treeRootNode.findChild('layId',layer.layId,true);
			
			if( layer.userSwitchable &&   (layerNode.get('checked') != layer.checked)){
				layerNode.set('checked', layer.checked);
			}	
		}		
	},

  /**
	 * @method _createCategory
	 * @private
	 * Aggiorna lo stato di un layer a livello di treePanel quando viene notificata
	 * una modifica dalla classe padre.
	 * 
	 * @param {Object} cat
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} addPointCatId
	 * 
	 * 
	 * @param {Object} before
	 * 
	 * 
	 */
    _createCategory: function(cat, catIdx, addPointCatId, before) {

    	var bConIcone = this.getParametriMappaCurr().legenda.conIcone;

    	var nodeCfg = {
    	   			text: cat.catDescr,
					hidden: cat.hidden,
					lay: null,
					cat: catIdx,
					classi: null,
					catId: cat.catId,
					allowDrag: this.tocInfo.orderChangeCapable,
					checked: (cat.userSwitchable) ? cat.checked : undefined,
					disabled: !this.tocInfo.areAllParentCatChecked(catIdx),
					href:  (cat.clickUrl) ? "#" : null, //this.calcolaCategoryClickUrl(cat),
					hrefTarget: (cat.clickTarget) ? cat.clickTarget : undefined,
					url: null,
					cls: 'bold' ,
					iconCls: bConIcone ? "iconLegendaCategoria" : 'treenode-no-icon',
					expanded: cat.expanded,
					qtip: this.calcolaCatToolTip(catIdx),
					itemType: cat.itemType					
				}; 
				
		if (cat.itemType && cat.itemType=='text' && cat.itemIcon) {
			var serverIco = "";
			if (cat.itemIcon.indexOf("http://")==-1 && cat.itemIcon.indexOf("http://")==-1) {
    			serverIco = this.TOLOMEOServer + this.TOLOMEOContext + "/";
			}
    		nodeCfg.icon = serverIco+cat.itemIcon;
			nodeCfg.iconCls = 'iconClassToc';
			
    	}			
		
		var parentCatIdx = this.tocInfo.getParentCategoryIdx(catIdx);
		var parentNode = (parentCatIdx=="") ? this.treeRootNode : this.treeRootNode.findChild('cat', parentCatIdx, true)  ;    //this.findChildNodeByAttribute(this.treeRootNode, 'cat', parentCatIdx)
		var nodeI = null;
		
		if (addPointCatId) {
			var nodeAddPoint = this.treeRootNode.findChild('catId', addPointCatId, true);
			
			if (before) {
				nodeI = parentNode.insertBefore(nodeCfg, nodeAddPoint);	
			} else {
				var nodeAfter = null;
				// Cerca la posizione di nodeAddPoint
				for (var i=0; i < parentNode.childNodes.length; i++) {
						var n = parentNode.getChildAt(i);
						if (n==nodeAddPoint) {
							if (i < parentNode.childNodes.length -1) {
								nodeAfter = parentNode.getChildAt(i+1);
							}
							break;
						}
				}
				if (nodeAfter) {
					// non è l'ultimo
						nodeI = parentNode.insertBefore(nodeCfg, nodeAfter);	
				} else {
						nodeI = parentNode.appendChild(nodeCfg);	
			 
				}
			}
			
		} else {
			// Cerco il primo nodo layer all'interno di parentNode
			var idxFirstLay = null;
			for (var i =0; i<parentNode.childNodes.length; i++) {
				if (parentNode.childNodes[i].get('lay')!=null) {
					idxFirstLay = i;
					break;
				}
			}
			if (idxFirstLay!=null) {
				nodeI = parentNode.insertBefore(nodeCfg, parentNode.childNodes[idxFirstLay]);	
			} else {
				nodeI = parentNode.appendChild(nodeCfg);
			}
		}
		

		
		this.setNodeLayerVisibility(nodeI, true);
		
		/*nodeI.on("click", 
			function(nd) {
				this.fireEvent('categoryLinkClick', nd.get('cat')); 
			}, 
			this);
		*/
		
		// Aggiunta Layer
		this.tocInfo.onEachLayer(
				function(cat, lay, catIdx, layIdx) { this._createLayer(cat, lay, catIdx, layIdx, nodeI) },
				this,
				catIdx,
				false
		);
		
		if(cat.expanded){
			this.expandCategories(catIdx);
		/*	this.tocInfo.onEachParentCategory(
					function(parentCat, parentCatIdx) {
						if (!parentCat.expanded) {
							var parentNode = (parentCatIdx=="") ? this.treeRootNode : this.treeRootNode.findChild('cat', parentCatIdx, true); // this.findChildNodeByAttribute(this.treeRootNode, 'cat', parentCatIdx) 
								
							
							if(!parentNode.isExpanded()){
								parentNode.expand();
							}
						}
					}, 
					this,
					catIdx
			)*/
		}
		
    },
    
    /**
     * @method expandCategories
     * Espande tutte le categorie fino a quella rappresentata dacatIdx
     * 
     * @param {Number} catIdx
     * 
     * 
     */
    expandCategories: function(catIdx) {
    	Ext.suspendLayouts();
    	
    	var thisNode = this.treeRootNode.findChild('cat', catIdx, true);;
		if(!thisNode.isExpanded()){
			thisNode.expand();
		}
    	
		this.tocInfo.onEachParentCategory(
				function(parentCat, parentCatIdx) {
					if (!parentCat.expanded) {
						var parentNode = (parentCatIdx=="") ? this.treeRootNode : this.treeRootNode.findChild('cat', parentCatIdx, true); // this.findChildNodeByAttribute(this.treeRootNode, 'cat', parentCatIdx) 
						
						if(!parentNode.isExpanded()){
							parentNode.expand();
						}
					}
				}, 
				this,
				catIdx
		);
		Ext.resumeLayouts(true);
		
    },
    
    /**
     * @method _layToolTipCfg
     * @private
     * Espande tutte le categorie fino a quella rappresentata dacatIdx
     * 
     * @param {Object} lay
     * 
     * 
     */
    _layToolTipCfg: function(lay) {
    	
    	var layToolTipCfgTxt = this.calcolaLayToolTip(lay.catTreeIdx, lay.layTreeIdx);
    	layToolTipCfgTxt = (layToolTipCfgTxt) ? layToolTipCfgTxt : undefined;
    	
    	return lay.temporaryNotAvailable ? ToloI18n.getMsg("ToloTreeTOCPanelExt.layerNonDisp") : layToolTipCfgTxt;
    },
        
    /**
     * @method _createLayer
     * @private
     * 
     * 
     * @param {Object} cat
     * 
     * 
     * @param {Object} lay
     * 
     * 
     * @param {Object} catIdx
     * 
     * 
     * @param {Object} layIdx
     * 
     * 
     * @param {Object} nodeI
     * 
     * 
     * @param {Object} addPointCatId
     * 
     * 
     * @param {Object} addPointLayId
     * 
     * 
     * @param {Object} bBefore
     * 
     * 
     */
    _createLayer: function(cat, lay, catIdx, layIdx, nodeI, addPointCatId, addPointLayId, bBefore) {
    	
    	var bConIcone = this.getParametriMappaCurr().legenda.conIcone;

    	var iconClsObj=this.calcolaIconCls(catIdx, layIdx);
    	var iconCls = iconClsObj.iconCls;
    	bConIcone = bConIcone || iconCls.forzaConIcone;
    	var singleClass = this.getParametriMappaCurr().legenda.singleClass;
    	
    	
    	var me = this;
    	
    	var node1Cfg = {
    	    			text: lay.descr,
    	    			hidden: lay.hidden, 
						lay: layIdx,
						cat: catIdx,
						classi: null,
						layId: lay.layId,
						codTPN: lay.codTPN,
						withOpacitySettings: lay.withOpacitySettings,
						ownerTOC: this,				
						style: lay.style,
						allowDrag: this.tocInfo.orderChangeCapable,
						temporaryNotAvailable: lay.temporaryNotAvailable,
						checked: (lay.userSwitchable) ? lay.checked : undefined, 
						disabled: !(this.tocInfo.areAllParentCatChecked(catIdx) && cat.checked), //!cat.checked,
						
						iconCls: bConIcone ? iconCls : 'treenode-no-icon',
						qtip: this._layToolTipCfg(lay),   // utilizzo qtip e non qtipCfg altrimenti non riesco a cambiarlo in seguito con setTooltip
						href: (lay.clickUrl) ? "#" : null, // this.calcolaLayerClickUrl(lay),
						hrefTarget: (lay.clickTarget) ? lay.clickTarget : undefined,
						expanded: lay.expanded,
						itemType: lay.itemType
				};
		
		if (lay.itemType && lay.itemType=='text' && lay.itemIcon) {
			var serverIco = "";
			if (lay.itemIcon.indexOf("http://")==-1 && lay.itemIcon.indexOf("http://")==-1) {
    			serverIco = this.TOLOMEOServer + this.TOLOMEOContext + "/";
			}
    		node1Cfg.icon = serverIco+lay.itemIcon;
			node1Cfg.iconCls = 'iconClassToc';
			
    	}				
				
		var nodeJ = null;
  
		
		// se è richiesta la singola classe si mette l'icona a livello del layer
		if(singleClass){
			var paramScala = "";
			if ( this.scale && lay.stiliIndipDaScala==false && (lay.classi[0].nomeParamScala) && (lay.classi[0].nomeParamScala!="")) {
				paramScala = ((lay.classi[0].nome && lay.classi[0].nome.indexOf("?")!=-1) ? "&" : "?") +  lay.classi[0].nomeParamScala + "=" + this.scale;
			}
			
			var url = lay.classi[0].nome;
			var pat = /width\=\d+/;
			url = url.replace(pat, "");
			var pat = /height\=\d+/;
			url = url.replace(pat, "");
			var pat = /\&\&/;
			url = url.replace(pat, "&");
			
			node1Cfg.icon = url + paramScala;
		}
		
  
		if (addPointLayId) {
			
			var nodeAddPointCat = nodeI;
			//var nodeAddPointCat = this.treeRootNode.findChild('catId', addPointCatId, true);
			var addNodeLay = null;
			
			// Cerca la posizione di nodeAddPoint
			for (var i=0; i < nodeAddPointCat.childNodes.length; i++) {
				var n = nodeAddPointCat.getChildAt(i);
				if (n.get('layId')==addPointLayId) {
					if (bBefore) {
						addNodeLay = n;
					} else {
						if (i < nodeAddPointCat.childNodes.length -1) {
							addNodeLay = nodeAddPointCat.getChildAt(i+1);
						}
					}
					break;
				}
			}
			
			if (addNodeLay) {
				nodeJ = nodeI.insertBefore(node1Cfg, addNodeLay);	
			} else {
				nodeJ = nodeI.appendChild(node1Cfg, addNodeLay);
			}
		} else {
			nodeJ = nodeI.appendChild(node1Cfg);
		}
				
	/*	nodeJ.on("click", 
					function(nd) {
						this.fireEvent('layerLinkClick', nd.get('cat'), nd.get('lay')); 
					}, 
					this);
		*/		
		this.setNodeLayerVisibility(nodeJ, lay.visible); 
		
		if(lay.expanded){
			
			if(catIdx && nodeI){

				this.tocInfo.onEachParentCategory(
						function(parentCat, parentCatIdx) {
							if (!parentCat.expanded) {
								var parentNode = (parentCatIdx=="") ? this.treeRootNode : this.treeRootNode.findChild('cat', parentCatIdx, true);

								if(!parentNode.isExpanded()){
									parentNode.expand();
								}
							}
						}, 
						this,
						catIdx
				)
				
				if(!nodeI.isExpanded()){					
					nodeI.expand();
				}
				
							
			}						
		}
		
		// Aggiunta Classi
		// sse non è singola classe
		if( ! singleClass ){
		for (var k=0; k<lay.classi.length; k++) {
			var definedStyles = lay.definedStyles;
			var node2Opts = {	text: this._createTagVoceLegenda(lay.classi[k], lay), 
								lay: layIdx,
								cat: catIdx,
								classi: k,
								allowDrag: false,
								leaf:true
							} ;
			

			if (lay.classi[k].icoUrl!=null && lay.classi[k].icoUrl!="") {
				node2Opts.icon = this.TOLOMEOServer + lay.classi[k].icoUrl;
				node2Opts.iconCls = 'iconClassToc';
			} else {
				node2Opts.iconCls = 'treenode-no-icon';
			}; 
    		var nodeK = nodeJ.appendChild(node2Opts);
		}
		}
    },
    
    /**
     * @method createTOC
     * 
     * 
     * @param {Object} scale
     * 
     * 
     */
    createTOC: function (scale) {

    	this.callParent(arguments);
		this.tocInfo.onEachCategory(
				this._createCategory,
				this
		);
		
    	this.doLayout();
    	this.isTOCGUICreated = true;
    	if (this.waitMsgBox) this.waitMsgBox.hide();
    	// Riabilita toolbar
    	var toolbar = this.getDockedItems('toolbar[dock="top"]')[0];
    	toolbar.enable();
    	
    	this.fireEvent('tocGuiCreate');
	}, 
	
	/**
	 * @method updateTOC
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 * 
	 * @returns {Boolean}
	 * 
	 * 
	 */
	updateTOC: function(scale) {		
		this.callParent(arguments);
		
		this.treeRootNode.cascadeBy(
			function(nd) {
				if (nd.get('classi')==null && nd.get('lay')!=null) {
					this.setNodeLayerVisibility(nd, this.tocInfo.getCategoryInfo(nd.get('cat')).layers[nd.get('lay')].visible);
					
				} else if (nd.get('classi')!=null) {
					// ATTENZIONE: il testo in alcuni casi contiene anche la legendGraphic
					var layer = this.tocInfo.getCategoryInfo(nd.get('cat')).layers[nd.get('lay')];
					var classe = layer.classi[nd.get('classi')];
					var t = this._createTagVoceLegenda(classe, layer); 	
					if (t!=nd.get('text')) {
						nd.set('text', t);
						var lay = this.tocInfo.getCategoryInfo(nd.get('cat')).layers[nd.get('lay')];
						nd.parentNode.set('qtip',this._layToolTipCfg(lay));
					}
				}
				return true;
			},
			this
		)		
	},
	
	updateCustomQuery: function(cq) {
		this.callParent(arguments);
		this.updateTOC();
	},
	
	/**
	 * @method _createTagVoceLegenda
	 * @private
	 * 
	 * 
	 * @param {Object} classe
	 * 
	 * 
	 * @param {Object} layer
	 * 
	 * 
	 * @param {Object} scale
	 * 
	 *
	 */
	_createTagVoceLegenda: function(classe, layer) {

		var tag =  "";
		var src = classe.nome;
		var paramScala = "";
		var paramPos = "";
		var stile = (layer.style && layer.style!="") ? layer.style : "default";
		

		if (classe.tipoNome==1) {
			// Nel caso di WMS
			
			// Se legenda dipendente da scala
			if ( this.scale && layer.stiliIndipDaScala!=undefined && layer.stiliIndipDaScala==false && (classe.nomeParamScala) && (classe.nomeParamScala!="")) {
				paramScala = classe.nomeParamScala + "=" + this.scale;
				src += ((src.indexOf("?")!=-1) ? "&" : "?") + paramScala;
			}
			if (this.posObj && layer.stiliIndipDaPosizione!=undefined && layer.stiliIndipDaPosizione==false) {
				paramPos += "SRS=" + this.posObj.srid;
				paramPos += "&BBOX=" + this.posObj.bbox.left  
									+ ',' + this.posObj.bbox.bottom
									+ ',' + this.posObj.bbox.right
									+ ',' + this.posObj.bbox.top;
				paramPos += "&WIDTH="  + this.posObj.width;
				paramPos += "&HEIGHT=" + this.posObj.height;
				src += ((src.indexOf("?")!=-1) ? "&" : "?") + paramPos;
			}
			
			if (this.customQuery!=null) {
				var paramsCustomQueryObj = {};
				// TODO supportata solo mappa 0
				var nMappa = 0;
				var cq = TolomeoExt.ToloViewerOLPanel.customQueryForServer(this.customQuery[nMappa], layer.serverID);
				Ext.apply(paramsCustomQueryObj, TolomeoExt.ToloViewerOLPanel.customQueryToGeoserverObj(cq));
				Ext.apply(paramsCustomQueryObj, TolomeoExt.ToloViewerOLPanel.customQueryToMapserverObj(cq));
				src += ((src.indexOf("?")!=-1) ? "&" : "?") + Ext.urlEncode(paramsCustomQueryObj);
			}
			
			tag =  '<img src="' + src + '" alt="' + ToloI18n.getMsg("ToloTreeTOCPanelExt.stileApplicato") + ' ' + stile + '" title="' + ToloI18n.getMsg("ToloTreeTOCPanelExt.stileApplicato") + ' '  +stile + '" />';	
		} else {
			// Nel caso di MAPSERVER cgi			
			tag = classe.nome;
		}
		
		return tag;
	},
	
	/**
	 * @method setNodeLayerVisibility
	 * 
	 * 
	 * @param {Object} node
	 * 
	 * 
	 * @param {Object} visible
	 * 
	 * 
	 */
	setNodeLayerVisibility: function(node, visible) {
    	if (visible) {
    		node.set('nodeVisibleAtScale', true);
		} else {
			node.set('nodeVisibleAtScale', false);
		}

		var iconClsObj = this.calcolaIconCls(node.get('cat'), node.get('lay'), visible);
		if (iconClsObj != null) {
			node.set('iconCls',iconClsObj.iconCls);
		}
	},	
	
	
	/**
	 * @method setCategoryStateChange
	 * Aggiorna lo stato di una categoria a livello di treePanel quando viene notificata
	 * una modifica dalla classe padre.
	 * 
	 * @param {String} cat
	 * categoria della tocInfo
	 * 
	 * @param {Object} checked
	 * 
	 * 
	 */
	setCategoryStateChange : function(cat, checked) {
		this.callParent(arguments);
		
		if (this.tocInfo != null) {
			
			var category = this.tocInfo.getCategoryInfo(cat); 
			var categoryNode = this.treeRootNode.findChild('catId', category.catId, true);
			if(category.userSwitchable && (category.checked != categoryNode.get('checked'))) {
				categoryNode.set('checked', category.checked);
			}
	
			categoryNode.cascadeBy(
	    			function(nd) {
	    				if (nd!=categoryNode) {
	    					var currCatIdx = nd.get('cat');
	    					var currCat = this.tocInfo.getCategoryInfo(currCatIdx);
	    					if ( this.tocInfo.areAllParentCatChecked(currCatIdx) && 
	    						(nd.get('lay')==null || 
	    						(nd.get('lay')!=null && currCat.checked )) 
	    					   ) {
	    							nd.set('disabled', false);	
	    						}	    					
		    				else {
			    					nd.set('disabled', true); 
		    					}
	    				}
	    			}, this 	
	    		);
		}		
	},
	
	/**
	 * @method onTOCInfoCatTreeIdxUpdate
	 * Metodo invocato al cambio dell'indice di categoria su tocInfo. I diversi tipi di legenda devono implementarlo per gestire l'aggiornamento sulla GUI della legenda
	 * 
	 * @param {Number} catId
	 * 
	 * 
	 * @param {Number} oldCatTreeIdx
	 * indice che è stato cambiato
	 * 
	 * @param {Number} newCatTreeIdx 
	 * nuovo valore dell'indice
	 * 
	 */
	onTOCInfoCatTreeIdxUpdate: function(catId, oldCatTreeIdx, newCatTreeIdx) {
		var categoryNode = this.treeRootNode.findChild('catId', catId, true);
		if (categoryNode) {
			// Se non esiste è quella nuova
			categoryNode.set('cat', newCatTreeIdx);
		}
	},
	
	/**
	 * @method onTOCInfoLayIdxUpdate
	 * Metodo invocato al cambio dell'indice di layer su tocInfo. I diversi tipi di legenda devono implementarlo per gestire l'aggiornamento sulla GUI della legenda
	 * 
	 * @param {Number} layI
	 * id assoluto del nodo all'interno dell'albero
	 * 
	 * @param {Number} catTreeIdx
	 * vecchio indice categoria
	 * 
	 * @param {Number} oldLayTreeIdx
	 * indice layer che è stato cambiato
	 * 
	 * @param {Number} newCatTreeIdx
	 * nuovo indice categoria
	 * 
	 * @param {Number} newLayTreeIdx
	 * nuovo valore dell'indice di layer
	 * 
	 */
	onTOCInfoLayIdxUpdate: function(layId, catTreeIdx, oldLayTreeIdx, newCatTreeIdx, newLayTreeIdx) {
		/*var categoryNode = this.treeRootNode.findChild('catId', catTreeIdx, true);
		var node = this.treeRootNode.findChildBy(
			function(nd){
				return (nd.get('catId')== catTreeIdx && nd.get('layId')==oldLayTreeIdx);

			}, this, true);
		
		if (categoryNode) {
			var layerNode = categoryNode.findChild('layId', layId, true);
			layerNode.set('lay', newLayTreeIdx);
			layerNode.set('cat', newCatTreeIdx);
		}*/
		
		var node = this.treeRootNode.findChild('layId', layId, true);
		if (node) {
			node.set('lay', newLayTreeIdx);
			node.set('cat', newCatTreeIdx);
			// Aggiorna anche i nodi "classi"
			for (var i=0; i<node.childNodes.length; i++) {
				var n = node.childNodes[i];
				n.set('lay', newLayTreeIdx);
				n.set('cat', newCatTreeIdx);
				n.set('classi', i);
			}
		}
		
	},
	
	/**
	 * @method addCategoryExtend
	 * 
	 * 
	 * @param {Object} catInfo
	 * 
	 * 
	 * @param {Object} addPointCat
	 * 
	 * 
	 * @param {Object} bBefore
	 * 
	 * 
	 */
	addCategoryExtend: function(catInfo, addPointCat, bBefore) {
	
		this._createCategory(catInfo.tocInfoParams, catInfo.tocInfoParams.catTreeIdx, addPointCat.catId, bBefore);
		
	},
	
	/**
	 * @method
	 * Aggiunge un layer nella posizione indicata dai parametri.
	 * 
	 * @param {Object} paramsInfo
	 * Info relative al layer da inserire in paramsJS
	 * 
	 * @param {Object} layTocInfo
	 * Tocinfo del layer da aggiungere
	 * 
	 * @param {Object} serverInfo
	 * Info sul server del layer da inserire
	 * 
	 * @param {Object} addPointCat
	 * Indice della categoria nella quale viene aggiunto il layer 
	 * 
	 * @param {Object} addPointLay
	 * Indice layer prima o dopo del quale aggiungere il nuovo layer
	 * 
	 * @param {Boolean} bBefore
	 * indica se aggiungere prima o dopo
	 * 
	 */
	addLayerExtend: function(paramsInfo, layTocInfo, serverInfo, addPointCat, addPointLay, bBefore) {
	
		var nodeAddPointCat = this.treeRootNode.findChild('catId', addPointCat.catId, true);
		this._createLayer(addPointCat, layTocInfo, layTocInfo.catTreeIdx, layTocInfo.layTreeIdx, nodeAddPointCat, addPointCat.catId, (addPointLay) ? addPointLay.layId : null, bBefore);
	
			
	}
	
});

/* 
 Tolomeo is a developing framework for visualization, editing,  

 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.define('TolomeoExt.ToloTOCNodeColumnExt4', {
    extend: 'Ext.grid.column.Column',
    alias: 'widget.tolotoccolumn',

    tdCls: Ext.baseCSSPrefix + 'grid-cell-treecolumn',

    view: null,
    
    autoLock: true,
    lockable: false,
    draggable: false,
    hideable: false,

    iconCls: Ext.baseCSSPrefix + 'tree-icon',
    checkboxCls: Ext.baseCSSPrefix + 'tree-checkbox',
    elbowCls: Ext.baseCSSPrefix + 'tree-elbow',
    expanderCls: Ext.baseCSSPrefix + 'tree-expander',
    textCls: Ext.baseCSSPrefix + 'tree-node-text',
    innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-treecolumn',
    isTreeColumn: true,
    bShowLevel: false,
    
	// NOTA BENE: il link sul nodo viene messo per dargli l'aspetto, ma come href viene messo # per poterlo gestire con l'evento itemClicked emesso dalla TOC
    cellTpl: [
      //  '<tpl if="hidden!=true" />',      
        '<tpl for="lines">',
            '<img src="{parent.blankUrl}" class="<tpl if="parent.isSep">legendaSeparatorRow</tpl> {parent.childCls} {parent.elbowCls}-img ',
            '{parent.elbowCls}-<tpl if=".">line<tpl else>empty</tpl>"/>',
        '</tpl>',
        '<img src="{blankUrl}" class="<tpl if="isSep">legendaSeparatorRow</tpl> {childCls} {elbowCls}-img {elbowCls}',
            '<tpl if="isLast">-end</tpl><tpl if="expandable">-plus {expanderCls}</tpl>"/>',
        '<tpl if="checked !== null">',
            '<input type="button" role="checkbox" <tpl if="checked">aria-checked="true" </tpl>',
                'class="{childCls} {checkboxCls}<tpl if="checked"> {checkboxCls}-checked</tpl>"/>',
        '</tpl>',
        '<tpl if="isSep">',
        		'<span class="legendaSeparator"></span>',
        '<tpl else>',
        	'<img src="{blankUrl}" class="{childCls} {baseIconCls} ',
            	'{baseIconCls}-<tpl if="leaf">leaf<tpl else>parent</tpl> {iconCls}"',
            '<tpl if="icon">style="background-image:url({icon}) !important"</tpl>/>',
        	'<tpl if="href">',
	            '<a href="#"  class="{textCls} {childCls}">{value}</a>',
        	'<tpl else>',        	
            	'<span class="layerLabel {textCls} {childCls}">{debugLevelLabel}{value}</span>',
            '</tpl>',
        '</tpl>'
     //   '</tpl>'
    ],
//{debugLevelLabel}
//{href} target="{hrefTarget}"
    
    initComponent: function() {
        var me = this;

        me.origRenderer = me.renderer;
        me.origScope = me.scope || window;

        me.renderer = me.treeRenderer;
        
        me.scope = me;        

        
        me.callParent();
        
        me.on('afterrender', me.a);
    },        

    treeRenderer: function(value, metaData, record, rowIdx, colIdx, store, view){

    	var me = this,
            cls = record.get('cls'),
            renderer = me.origRenderer,
            data = record.data,
            parent = record.parentNode,
            rootVisible = view.rootVisible,
            lines = [],
            parentData;

        if (cls) {
            metaData.tdCls += ' ' + cls;
        }

        while (parent && (rootVisible || parent.data.depth > 0)) {
            parentData = parent.data;
            lines[rootVisible ? parentData.depth : parentData.depth - 1] =
                    parentData.isLast ? 0 : 1;
            parent = parent.parentNode;
        }

        var debugLevelLabel = (this.bShowLevel) ? data.cat + "-" + data.lay + "+" + data.classi : "";
        
        var el =  me.getTpl('cellTpl').apply({
        	debugLevelLabel: debugLevelLabel, 
        	cat: data.cat,
        	lay: data.lay,
        	classi: data.classi,
            record: record,
            hidden: data.hidden,
            baseIconCls: me.iconCls,
            iconCls: data.iconCls,
            icon: data.icon,
            checkboxCls: me.checkboxCls,
            checked: data.checked,
            elbowCls: me.elbowCls,
            expanderCls: me.expanderCls,
            textCls: me.textCls,
            leaf: data.leaf,
            expandable: record.isExpandable(),
            isLast: data.isLast,
            blankUrl: Ext.BLANK_IMAGE_URL,
            href: data.href,
            hrefTarget: data.hrefTarget,
            lines: lines,
            metaData: metaData,
            isSep: (data.itemType=='separator'),
            childCls: me.getChildCls ? me.getChildCls() + ' ' : '',
            value: renderer ? renderer.apply(me.origScope, arguments) : value
        }); 
        
        
        return el;
    },
   
    a: function(col, eOpts) {
    	var i=0;
    	
    },
    
    prova: function() {
    	var i=0;
    }
});


 /* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloMenuTOCPanelExt
 * @extends TolomeoExt.ToloTOCPanelExt
 * 
 * 
 */
	
Ext.define('TolomeoExt.ToloMenuTOCPanelExt', {

	extend: 'TolomeoExt.ToloTOCPanelExt',
	
	alias: 'tx_toloMenuTOCPanelExt',

	/**
	 * @property {Object} categoryButton
	 * @private
	 * 
	 * 
	 */
	categoryButton: null,	
		
	/**
	 * @method initComponent
	 * Metodo relativo alla gestione Ext.
	 *  
	 */
	initComponent: function(){
        
		//Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);	
	
		//this.layout='hbox';
		this.callParent(arguments);
        
    },
    
    /**
     * @method checkItemChange
     * 
     * 
     * @param {Object} item
     * item.
     * 
     * @param {Boolean} checked
     * checked.
     * 
     */
    checkItemChange: function(item, checked) {
    	
    	var cat = item.attributes.cat;
    	var lay = item.attributes.lay;
    	
    	this.setLayerStateChange(item.attributes.cat, item.attributes.lay, checked);
    },
    
  /**
	 * @method setLayerStateChange
	 * Aggiorna lo stato di un layer a livello di menu items quando viene notificata
	 * una modifica dalla classe padre.
	 * 
	 * @param {String} cat
	 * 
	 * 
	 * @param {Number} lay
	 * layer della tocInfo
	 * 
	 * @param {Boolean} checked
	 * 
	 * 
	 */
	setLayerStateChange : function(cat, lay, checked) {
		this.callParent(arguments);
		if (this.tocInfo != null && lay != null) {
			
			var layer = this.tocInfo.getCategoryInfo(cat).layers[lay];
			
			for (var i=0; i<this.categoryButton.menu.items.length; i++) {
				var ck = this.categoryButton.menu.items.items[i];
				if (ck.attributes.layId==layer.layId) {
					if (ck.checked!=layer.checked) ck.setChecked(layer.checked);
				}
			}
			
			
		}		
	},

    /**
     * @method createTOC
     * 
     * 
     * @param {Object} obj
     * obj.
     * 
     */
    createTOC: function (obj) {

    	this.callParent(arguments);
		
		this.tocInfo.onEachCategory( 
	    	function(cat, catIdx) {	
    		
				    		//aggiungo bottone categoria
			    		this.categoryButton = new Ext.Button({
			    			text       : cat.catDescr,
							anchor     : '0',
							arrowAlign : 'bottom',
							iconAlign: 'top',
							cls        : 'bold',
							menu: {
				            	xtype: 'menu',
				            	cls: 'clearCSS'
							}
						});
						
						this.categoryButton.attributes = {};
			    		this.categoryButton.attributes.cat = catIdx;
			    		this.categoryButton.attributes.catId = cat.catId;
			    		this.categoryButton.attributes.lay = null;
			    		this.categoryButton.attributes.classi = null;
			    		
			    		this.add(this.categoryButton);
			    		
			    		var listenersCheckChange = {checkchange: {fn: this.checkItemChange, scope: this}};
		    			
			    		//aggiungo menu i cui item sono i layer della stessa
			    		for (var j=0; j<cat.layers.length; j++) {
			        			
			    			var layerItemMenu = new Ext.menu.CheckItem({
			        			text: cat.layers[j].descr,
								checked: cat.layers[j].checked,
								listeners: listenersCheckChange
							});
			        			
							layerItemMenu.attributes = {};			
							layerItemMenu.attributes.cat = catIdx;
			        		layerItemMenu.attributes.lay = j;
			        		layerItemMenu.attributes.codTPN = cat.layers[j].codTPN;
			        		layerItemMenu.attributes.layId = cat.layers[j].layId;
			        		layerItemMenu.attributes.classi = null;
			        		layerItemMenu.attributes.withOpacitySettings= cat.layers[j].withOpacitySettings;
							layerItemMenu.attributes.ownerTOC = this;
			    		
			        		this.categoryButton.menu.add(layerItemMenu);
			        		this.doLayout(true,true);
			    		}
		    	},
		    	this);
		
	}

});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


 Ext.define('modelloDatiStile', {
     extend: 'Ext.data.Model',
     requires: ['Ext.data.SequentialIdGenerator'],
     idgen: 'sequential',
     fields: [	'id',
	            'name',
	            'title',	
	            'styleAbstract',
	            'legendGraphic',
	            'sldtext'
		    ]
 });

/**
 * @class TolomeoExt.ToloStylePanel
 * @extends Ext.Window
 *
 */
Ext.define('TolomeoExt.ToloStylePanel', {

	extend: 'Ext.Window',
 
	
	/** 
	 * @property {String} TOLOMEOServer
	 * 
	 * 
	 */
	TOLOMEOServer: null,

	/** 
	 * @property {String} TOLOMEOContext
	 * 
	 * 
	 */
	TOLOMEOContext: null,
	
	/** 
	 * @property {String} TOLOMEOStaticRoot
	 * 
	 * 
	 */
	TOLOMEOStaticRoot: null,
	
	/** 
	 * @property {Object} layer
	 * 
	 * 
	 */
	layer: null,
	
	/**
	 * @property {Object} definedStyles
	 * @private 
	 * 
	 * 
	 */
	definedStyles: null,
	
	/**
	 * @property {Object} cat
	 * @private 
	 * 
	 * 
	 */
	cat:null,
	
	/**
	 * @property {Object} lay
	 * @private 
	 * 
	 * 
	 */
	lay: null,
	
	/**
	 * @property {Object} sldGrid
	 * @private 
	 * 
	 * 
	 */
	sldGrid: null,
	
	/**
	 * @property {Object} store 
	 * @private
	 * 
	 * 
	 */
	store: null,
	
	/**
	 * @property {Object} stylePreview
	 * @private 
	 * 
	 * 
	 */
	stylePreview: null,
	
	/**
	 * @method initComponent
	 * 
	 * 
	 */
	initComponent: function(){
	    
		//bottoni
		// Messo fisso false per non abilitare la funziona fino a che non è pronta
		if (false) { 
			this.btnNuovo = Ext.create('Ext.Button', {
	            text: ToloI18n.getMsg("ToloStylePanel.btnNuovo"),
		        disabled: false,
		        iconCls: 'iconConferma',
		        width: 75,
		        tooltip : {text: ToloI18n.getMsg("ToloStylePanel.btnNuovo.title"),
		        	title: ToloI18n.getMsg("ToloStylePanel.btnNuovo.msg")
		        },
		        listeners: { click: { fn: this.onCreaStile, scope: this}}
		    });
			
			this.btnImporta = Ext.create('Ext.Button', {
	            text: ToloI18n.getMsg("ToloStylePanel.btnImporta"),
		        disabled: false,
		        iconCls: 'iconConferma',
		        width: 75,
		        tooltip : {text: ToloI18n.getMsg("ToloStylePanel.btnImporta.msg")}, 
		        listeners: { click: { fn: this.onImportaStile, scope: this}}
		    });
			
			this.btnEsporta = Ext.create('Ext.Button', {
	            text: ToloI18n.getMsg("ToloStylePanel.btnEsporta"),
		        disabled: true,
		        iconCls: 'iconConferma',
		        width: 75,
		        tooltip : {text: ToloI18n.getMsg("ToloStylePanel.btnEsporta.msg")}, 
		        listeners: { click: { fn: this.onEsportaStile, scope: this}}
		    });
			
			this.btnModifica = Ext.create('Ext.Button', {
	            text: ToloI18n.getMsg("ToloStylePanel.btnModifica"),
		        disabled: true,
		        iconCls: 'iconConferma',
		        width: 75,
		        tooltip : {text: ToloI18n.getMsg("ToloStylePanel.btnModifica.msg")},
		        listeners: { click: { fn: this.onModificaStile, scope: this}}
		    });
		}	
		
		this.btnApplica = Ext.create('Ext.Button', {
					            text: ToloI18n.getMsg("ToloStylePanel.btnApplica"),
						        disabled: true,
						        iconCls: 'iconConferma',
						        width: 75,
						        tooltip : {text: ToloI18n.getMsg("ToloStylePanel.btnApplica.msg"), title: ToloI18n.getMsg("ToloStylePanel.btnApplica.title")},
						        listeners: { click: { fn: this.onApplica, scope: this}}
						    });
		
		this.btnAnnulla = Ext.create('Ext.Button',{
				            text: ToloI18n.getMsg("ToloStylePanel.btnAnnulla"),
				            width: 75,
							listeners: { click: { fn: this.nascondi, scope: this}}
				        });
		
		
		this.buttons = [this.btnNuovo, this.btnImporta, this.btnEsporta, this.btnModifica, this.btnApplica, this.btnAnnulla];
		
		
		this.formImporta = Ext.create('Ext.form.Panel', {
		    hidden: true,
		    renderTo: Ext.getBody(),
		    items: [{
		        xtype: 'filefield',
		        name: 'photo'
		        /*listeners:{
		            change:function( thiss, value, eOpts ){
		                  alert(value);
		                  //here place your ajax request
		            }
		         },*/
		        //fieldLabel: 'Photo',
		        //labelWidth: 50,
		        //msgTarget: 'side',
		        //allowBlank: false,
		        //anchor: '100%',
		        //buttonText: 'Select Photo...'
		    }
			]
		    });
		
		/*
		 * new Ext.create('Ext.form.field.File', {

            buttonOnly: true,
            hideLabel: true,
            buttonText: 'Carregar Playlist.',
            listeners: {
                'change': function(fb, v) {
                    form.getForm().submit({
                        method: 'POST',
                        url: 'carregaLista.php',
                    });
                }
            }
		 * 
		 */
		
		this.callParent(arguments);
		
		this.layout = 'border';
		this.addEvents('styleApply'); // selezionato stile e richiesta la sua applicazione
			
		this.store = Ext.create('Ext.data.JsonStore',{
		    autoDestroy: true,
		    autoSync: false,
		    model: 'modelloDatiStile'
		  //  storeId: 'myStore',
		    /*fields: [
	            'name',
	            'title',	
	            'styleAbstract',
	            'legendGraphic',
	            'sldtext'
		    ]*/
		});
		
		var columns =  [{
			header: ToloI18n.getMsg("ToloStylePanel.colNome"),
		    sortable: true, 
		    dataIndex: 'name'
		},{
	    	header: ToloI18n.getMsg("ToloStylePanel.colTit"),
			sortable: true, 
			dataIndex: 'title'
		},{
			header: ToloI18n.getMsg("ToloStylePanel.colDesc"),
		    sortable: true, 
		    dataIndex: 'styleAbstract',
		    renderer: function (val){
		    	return '<div style="white-space:normal !important;">'+ val +'</div>';
		    }
	    }];
		
		//griglia sld per layer selezionato
	    this.sldGrid = Ext.create('Ext.grid.GridPanel',{
		    	region: 'center',
		        frame: false,
		        height: 'auto',
		        store: this.store,
		        columns: columns,
		        viewConfig: {
		        		markDirty:false,
		    			forceFit: true,
		    			emptyText: ToloI18n.getMsg("ToloStylePanel.sldGrid.emptyText")
					},
				listeners: { selectionchange: { 
										fn: function(selmodel, selected, eOpts) {
												var bNessunaSelezione = (selected.length==0);
												var bCustomSld = !bNessunaSelezione && (selected[0].data.sldtext) && (selected[0].data.sldtext!="");
												this.btnApplica.setDisabled(bNessunaSelezione);
												if (this.btnEsporta) this.btnEsporta.setDisabled(!bCustomSld);
												if (this.btnModifica) this.btnModifica.setDisabled(!bCustomSld);
												
												if (bNessunaSelezione) {
													this.stylePreview.clearSrc();	
												} else {
													var rec = selected[0];
													if (rec.data.legendGraphic && !rec.data.legendGraphic=="") {
														this.stylePreview.setSrc(rec.data.legendGraphic);	
													} else {
														this.stylePreview.clearSrc();
													}
												}
											},
										scope: this
								}
							}
	       		        
	    	});
	    /*
	    this.sldGrid.getSelectionModel().on('selectionchange', function(selmodel, selected, eOpts) {
			var bNessunaSelezione = (selected.length==0);
			this.btnApplica.setDisabled(bNessunaSelezione);
	
			if (bNessunaSelezione) {
				this.stylePreview.clearSrc();	
			} else {
				var rec = selected;
				if (rec.data.legendGraphic && !rec.data.legendGraphic=="") {
					this.stylePreview.setSrc(rec.data.legendGraphic);	
				} else {
					this.stylePreview.clearSrc();
				}
			}
		}, this);
	    */
	    this.stylePreview = Ext.create('Ext.Panel', {
	    	title: ToloI18n.getMsg("ToloStylePanel.stylePreview.title"),
	    	autoScroll: true,
	    	split: true,
	    	collapsible: true,
	    	region: 'east',
    		width: '25%',
    		setSrc: function(src) {
				this.update('<img src="' + src +'" />');
    	    },
    	    clearSrc: function() {
    	    	this.update(ToloI18n.getMsg("ToloStylePanel.stylePreview.clear"));
    	    }
    	});
	    
	    this.add(this.sldGrid);
		this.add(this.stylePreview);
		
	},
	
	
	/**
	 * @method mostra
	 * 
	 * 
	 * @param {String} nome
	 * 
	 * 
	 * @param {Object} cat
	 * 
	 * 
	 * @param {Object} lay
	 * 
	 * 
	 * @param {Object} definedStyles
	 * 
	 * 
	 */
	mostra: function(layer, cat, lay, definedStyles) {
	
		this.layer = layer;
		this.lay = lay;
		this.cat = cat;
		this.definedStyles = definedStyles;
		
		// Aggiorno store con gli stili disponibili
		this.store.loadData(definedStyles);
		
		// Setto il titolo della finestra
		var title = ToloI18n.getMsg("ToloStylePanel.mostra.title", {nome: layer.nome});
		this.setTitle(title);
		
		// mostro la finestra
		this.show();
		
	},
			
	/**
	 * @method nascondi
	 * 
	 * 
	 */
	nascondi: function() {
		this.hide();
	},
	
	/**
	 * @method onImportaStile
	 * 
	 */
	onImportaStile: function(form) {
		var me = this;
		
		var formpanel = Ext.create('Ext.form.Panel', {
			url: this.TOLOMEOServer + this.TOLOMEOContext + '/MirrorServlet',
			bodyPadding: 10,
		    frame: true,
		    items: [{
		        xtype: 'filefield',
		        name: 'uploadfield',
		        allowBlank: false,
		        blankText: ToloI18n.getMsg("ToloStylePanel.onImportaStile.blankText"),
		        fieldLabel: "File",
		        labelWidth: 50,
		        buttonText: ToloI18n.getMsg("ToloStylePanel.onImportaStile.btnImporta"),
		        anchor: '100%',
				buttonConfig: {
					text: ToloI18n.getMsg("ToloStylePanel.onImportaStile.btnImporta"),
			        disabled: false,
			        iconCls: 'iconConferma',
			        //width: 75,
			        tooltip : {text: ToloI18n.getMsg("ToloStylePanel.onImportaStile.btnImporta.tooltip")}
				},
				buttonOnly: false}],
			buttons: [{
			        text: ToloI18n.getMsg("ToloStylePanel.onImportaStile.btnReset"),
			        handler: function() {
			            this.up('form').getForm().reset();
			        }
			    }, {
			        text: ToloI18n.getMsg("ToloStylePanel.onImportaStile.btnSubmit"),
			        formBind: true, //only enabled once the form is valid
			        disabled: true,
			        handler: function() {
			            var form = formpanel.getForm();
			            if (form.isValid()) {
			                form.submit({
			                    success: function(form, action) {
			                    	
			                        var s = TolomeoExt.Styler.StylePanel.sldToStyle(action.result.contenuto);
			                    	
			                    	me.store.add({
			                    		'name': s.name,
				        	            'title': s.title,
				        	            //'styleAbstract': 'abstract',
				        	            'legendGraphic': undefined,
				        	            'sldtext': action.result.contenuto
			                    	});
			        	       
			                       Ext.Msg.alert('Success', action.result.msg);
			                       formpanel.up('window').close();
			                    },
			                    failure: function(form, action) {
			                        Ext.Msg.alert('Failed', action.result.msg);
			                        formpanel.up('window').close();
			                    }
			                });
			            }
			        }
			    }]});
		        
		        
		    /*,
		        listeners:{
		            change:function( thiss, value, eOpts ){
		                  //alert(value);
		                  var form = formpanel.getForm();
		                  if(form.isValid()){
		                      form.submit({
		                          url: '/tolomeobinj/MirrorServlet',
		                          waitMsg: 'Importazione in corso',
		                          success: function(fp, o) {
		                        	  //  o.result.file 
		                              Ext.Msg.alert('Success', 'Importazione avvenuta con successo');
		                          },
		                          failure: function(form, action) {
		                              Ext.Msg.alert('Failed', action.result.msg);
		                          }
		                      });
		                  }
		            }
		         }*/
		        //fieldLabel: 'Photo',
		        //labelWidth: 50,
		        //msgTarget: 'side',
		        //allowBlank: false,
		        //anchor: '100%',
		        //buttonText: 'Select Photo...'
		    //}]});
		
		Ext.create('Ext.window.Window',{
			width: 400,
			resizable: false,
			height: 120,
			layout: 'fit',
			title: ToloI18n.getMsg("ToloStylePanel.onImportaStile.win.title"),
			items: [formpanel]
		}).show();
		 
		
		//var form = this.formImporta.getForm();
        
		
	},
	
	/**
	 * @method onEsporta
	 */
	onEsportaStile: function() {
		
	    var rec = this.sldGrid.getSelectionModel().getSelection()[0];
		
	    var formattedXml = this.formatXml(rec.data.sldtext);
	    
		var formpanel = Ext.create('Ext.form.Panel', {
			
			url:  this.TOLOMEOServer + this.TOLOMEOContext + '/MirrorServlet',
			standardSubmit: true,
			render: Ext.getBody(),
			hidden: true,
			items: [{
					    xtype: 'hiddenfield',
					    name: 'mode',
					    value: 'download'
					},{
					    xtype: 'hiddenfield',
					    name: 'contenuto',
					    value: formattedXml
					},{
					    xtype: 'hiddenfield',
					    name: 'filename',
					    value: rec.data.name + '.xml'
					}],
					
			buttons: [{
			        text: ToloI18n.getMsg("ToloStylePanel.onEsportaStile.btnReset"),
			        handler: function() {
			            this.up('form').getForm().reset();
			        }
			    }, {
			        text: ToloI18n.getMsg("ToloStylePanel.onEsportaStile.btnSubmit"),
			        formBind: true, //only enabled once the form is valid
			        disabled: true,
			        handler: function() {
			            var form = this.up('form').getForm();
			            if (form.isValid()) {
			                form.submit({
			                    success: function(form, action) {
			                       Ext.Msg.alert('Success', action.result.msg);
			                       formpanel.close();
			                    },
			                    failure: function(form, action) {
			                        Ext.Msg.alert('Failed', action.result.msg);
			                        formpanel.close();
			                    }
			                });
			            }
			        }
			    }]
		});
		
		formpanel.getForm().submit();
		
		
		
		/*
		Ext.create('Ext.window.Window',{
			width: 100,
			height: 60,
			layout: 'fit',
			title: 'ssss',
			items: [formpanel]
			
		}).show();
		*/
	},
	
	onModificaStile: function() {
		var rec = this.sldGrid.getSelectionModel().getSelection()[0];
		
		this.showStyle(rec);
	},
		
	/**
	 * @method onApplica
	 * 
	 * 
	 */
	onApplica: function() {
        var rec = this.sldGrid.getSelectionModel().getSelection()[0];
        if (rec!=null) this.fireEvent('styleApply', this.cat, this.lay, rec.data.name);
    
    },
    
    onCreaStile: function() {
    	//this.currentLegend.symbolType
    	
		//ALE Per ora commentato
		//this.currentLegend.rules.push(rule);
		//this.fireEvent("ruleadded", rule);
		this.showStyle(null);
    },
    
    showStyle: function(rec) {
		//console.log("prima delle modifiche ...");
		//logProperties(rule);
		//for (var symbolizerIndex = 0 ; symbolizerIndex < rule.symbolizers.length ; symbolizerIndex ++) {
		//	logProperties(rule.symbolizers[symbolizerIndex]);
		//}
    	var me = this;

		this.styleDialog = Ext.create("Ext.Window", {
			//title: "Stile \"" + (style.title || style.name || "Untitled") + "\"",
			layout: "fit",
			modal: true,
			width: 500,
			height: 500,
			constrainHeader: true,
			frame: false,
			//recordGriglia: rec,
			items: [{
				xtype: "styler_stylepanel",
				styleRecord: rec,
				layer: this.layer,
				mapPanel: this.mapPanel,
				listeners: {
					"stylecanceled": function() {
						this.styleDialog.close();
					},
					"stylesaved": function(rec) {
						
						if (me.store.getById(rec.get('id'))==null){
							//rec.set('name', 'sdsdsds');
							//rec.set('sldtext', sld);
							me.store.add([rec]);
						}/* else {
							me.store.add({
	                    		'name': 'nome',
		        	            'title': 'titolo',
		        	            'styleAbstract': 'abstract',
		        	            'legendGraphic': undefined,
		        	            'sldtext': sld
	                    	});
						}*/
                    	
                    	this.styleDialog.close();
						//console.log(format.write(sld));
					},
					scope: this
				}
			}]
		});
		
		
		this.styleDialog.show();
	},
	
	formatXml: function (xml) {
	    var formatted = '';
	    var reg = /(>)(<)(\/*)/g;
	    xml = xml.replace(reg, '$1\r\n$2$3');
	    var pad = 0;
	    Ext.Array.each(xml.split('\r\n'), function(node, index) {
	        var indent = 0;
	        if (node.match( /.+<\/\w[^>]*>$/ )) {
	            indent = 0;
	        } else if (node.match( /^<\/\w/ )) {
	            if (pad != 0) {
	                pad -= 1;
	            }
	        } else if (node.match( /^<\w[^>]*[^\/]>.*$/ )) {
	            indent = 1;
	        } else {
	            indent = 0;
	        }
	 
	        var padding = '';
	        for (var i = 0; i < pad; i++) {
	            padding += '  ';
	        }
	 
	        formatted += padding + node + '\r\n';
	        pad += indent;
	    });
	 
	    return formatted;
	}

});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110�1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo � un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo � un software libero; � possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo � distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILIT� o
 IDONEIT� PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110�1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo � sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

// @include "include.js"

/**
 * @class TolomeoExt.ToloQueryPanelExt
 * Pannello per contenere una mappa. Funzioni pubbliche di query e posizionamento (es. ricerca via e civico etc.).
 * Quando viene attivata la ricerca viene mostrato un pannello che contiene l'elenco delle ricerche possibili, 
 * che permette di inserire i criteri di ricerca all'interno di quelli previsti, di lanciare la ricerca. Nel caso che il risultato sia univoco
 * viene evidenziato l'oggetto risultato della ricerca e viene fatto uno zoomToHighlighted. Nel caso il risultato non sia univoco viene visualizzato 
 * l'elenco per poter scegliere quale oggetto evidenziare e zoomare.
 * Le ricerche possibili sono controllate da contenuto del file di configurazione xml, che permette di selezionare quali tra le ricerche
 * disponibili nel package sit debbano essere attivate.
 *
 * @author Ing. Alessandro Radaelli
 */
Ext.define('TolomeoExt.ToloQueryPanelExt', {

	extend: 'Ext.Panel',
	
	//requires: [],


	/** 
	 * @property {Object} showHideContainer
	 * contenitore che deve essere mostrato/nascosto per visualizzare o nascondere la legenda
	 * Se null viene visualizzato e nascosto questo stesso pannelleo
	 * 
	 */
	showHideContainer: null,

	/** 
	 * @property {Function} showHandler
	 * Funzione da chiamare per visualizzare la legenda. Se null non viene chiamata
	 * 
	 */
	showHandler: null,

	/** 
	 * @property {Function} hideHandler
	 * Funzione da chiamare per nascondere la legenda. Se null non viene chiamata
	 * 
	 */
	hideHandler: null,

	/** 
	 * @property {Object} paramsJS
	 * 
	 * 
	 */
	paramsJS: null,

	/** 
	 * @property {String} TOLOMEOServer
	 * 
	 * 
	 */
	TOLOMEOServer: null,

	/** 
	 * @property {String} TOLOMEOContext
	 * 
	 * 
	 */
	TOLOMEOContext: null,

	/** 
	 * @property {Object} panelCampi
	 * 
	 * 
	 */
	panelCampi: null,

	/** 
	 * @property {Object} panelResults
	 * 
	 * 
	 */
	panelResults: null, 

	/** 
	 * @property {Object} formPanelSearch
	 * 
	 * 
	 */
	formPanelSearch: null,

	/** 
	 * @property {Object} layerStore
	 * 
	 * 
	 */
	layerStore: null,

	/** 
	 * @property {Object} cmbLayerSel
	 * 
	 * 
	 */
	cmbLayerSel: null,

	/** 
	 * @property {Object} cmbTipo
	 * 
	 * 
	 */
	cmbTipo:null,

	/** 
	 * @property {Object} pnlCampi
	 * 
	 * 
	 */
	pnlCampi: null, 

	/** 
	 * @property {Object} pnlResults
	 * 
	 * 
	 */
	pnlResults: null,

	/** 
	 * @property {Object} suggestWithGeom
	 * 
	 * 
	 */	
	suggestWithGeom: null,
	
	/** 
	 * @property {Object} queryFields
	 * 
	 * 
	 */	
	queryFields: null,

	/** 
	 * @property {Object} waitMask
	 * 
	 * 
	 */	
	waitMask: null,
	
	/** 
	 * @property {Object} geomFilterField
	 * Campo nascosto che ospiterà geomFilter
	 * 
	 */
	geomFilterField: null,
	
	/**
	 * @method initComponent
	 * Crea un nuovo TolomeoExt.ToloQueryPanelExt
	 * 
	 */
	initComponent: function(){
	
    	// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
		TolomeoExt.applyIfEmpty(this, {suggestWithGeom: true});
		
		this.on('activate', this.focusFirstField);
		
		// Eventi
		// Item selected
    	this.addEvents('querySelected');
    	// No results
    	this.addEvents('queryNoResults');
    	// Hover on item of a selection with multiple results
    	this.addEvents('queryMultipleResultHover');
    	this.addEvents('queryMultipleResultOut');
    	
    	// Evento che scatta quando un nuovo campo filtro geometria è creato
    	// Tipicamente è utilizzato per consentire alla API di inizializzarlo con il box 
    	// utilizzando il metodo setGeomFilterField
    	this.addEvents('geomFilterFieldCreated');
    	
		this.layerStore = Ext.create('Ext.data.Store',{
		    fields: [{
		    	name: 'descrizioneLayer',
		    	mapping: 'descrizioneLayer'
		    },{
		    	name: 'codTPN', mapping: 'codTPN'
		    },{
		    	name: 'eventiLayer',
		        convert: function (v, rec) { 
		        	return rec.raw; 
		        } 
		    }],
		    data: this.paramsJS.azioniEventi.eventiLayerList
		});
		
		this.layerStore.filterBy(this.withSearchLayerFilter);

		this.cmbLayerSel = Ext.create('Ext.form.ComboBox',{
			typeAhead: true,
			forceSelection: true, 
			anchor: "-1",
			lastQuery: '',
			queryMode: 'local',
			triggerAction: 'all',
			emptyText: ToloI18n.getMsg("ToloQueryPanelExt.cmbLayerSel.emptyText"),
			selectOnFocus: true,
			editable:false,
			fieldLabel: ToloI18n.getMsg("ToloQueryPanelExt.cmbLayerSel.fldLabel"),
			//hiddenName: 'codTPN',
			name: 'codTPN',
			valueField: 'codTPN',
			displayField: 'descrizioneLayer',
			listeners: {
				select: {
					fn: this.onQueryChangeLayer,
					scope: this
				}
			},
			store: this.layerStore
		});
		
		this.SearchTypeStore = Ext.create('Ext.data.Store',{
			fields: [{
		    	name: 'metadatoRicerca',
		    	mapping: 'metadatoRicerca'
		    },{
		    	name: 'id',
		    	convert: function (v, rec) { 
		    		return rec.raw.metadatoRicerca.id; 
		    	}
		    },{
		    	name: 'desc',
		    	convert: function (v, rec) { 
		    		return rec.raw.metadatoRicerca.descrizione; 
		    	}
		    },{
		    	name: 'azioniEventiRicercaList',
		    	convert: function (v, rec) { 				        
		    		return rec.raw; 
		    	}
		    }]
		});
				
		this.cmbTipo = Ext.create('Ext.form.ComboBox',{
			typeAhead: false,
			anchor: "-1",
			editable: true,
			triggerAction: 'all',
			queryMode: 'local',
			fieldLabel: ToloI18n.getMsg("ToloQueryPanelExt.cmbTipo.fldLabel"),
			editable:false,
			//hiddenName: 'idRicerca',
			name: 'idRicerca',
			valueField: 'id',
			displayField: 'desc',
			listeners: {
				select: {fn: this.onQueryChangeRicerca, scope: this}
			},
			store: this.SearchTypeStore
		});
		
		this.pnlCampi = Ext.create('Ext.Panel',{
			anchor: "-1",
			border: false,
		//	monitorResize: true,
			layout: "form",
			hidden: false,
			reset: function(){
				this.items.each(function(item){
					if (item.name != 'geomFilter') {
						item.reset();
					}
				});
			}
		}); 
			
		// FieldSet
		this.fs= Ext.create('Ext.form.FieldSet',{
		//	monitorResize: true,
			title: ToloI18n.getMsg("ToloQueryPanelExt.fldSet"),
			anchor: "-1",
			autoWidth: true,
			autoHeight: true,
			defaultType: 'textfield',
			items:[this.cmbLayerSel, this.cmbTipo, this.pnlCampi]
		});

		this.pnlResults = Ext.create('Ext.Panel',{
			title: ToloI18n.getMsg("ToloQueryPanelExt.pnlResults"),
			frame: false,
			hidden: true,
	        layout: 'fit',
	        autoWidth: true,
	        autoScroll: true,
	        height: 300
		});
		
		// FormPanel
		this.formPanelSearch = Ext.create('Ext.FormPanel',{
			monitorValid: true,
		//	monitorResize: true,
			labelWidth: 60,
			standardSubmit: false,
			bodyStyle: 'padding: 5px',
			border: false,
			items: [this.fs, {
				xtype: 'hidden', 
				name: 'SRID',
				value : this.paramsJS.mappe.SRID
			}],
			buttons: [{
				text: ToloI18n.getMsg("ToloQueryPanelExt.btnTrova"),
				formBind: true,
				type: 'button',
				listeners: {'click': {fn: this.onQuery, scope: this}}
			},{
				text: ToloI18n.getMsg("ToloQueryPanelExt.btnPulisci"),
				resultPanel: this.pnlResults,
				fieldsPanel: this.pnlCampi,
				handler: function() {
					this.fieldsPanel.reset();
					this.resultPanel.hide();
				}
			}]/*,
			keys: [{
	        	key: [Ext.EventObject.ENTER], 
             	handler: this.onQuery,
                scope: this
             }] */ 
		});
		
		var map = Ext.create('Ext.util.KeyMap',{
		    target: this,
		    binding: {
		        key: Ext.EventObject.ENTER,
		        fn: this.onQuery,
		        scope: this
		    }
		});		

		
		this.callParent();
		
		// Se esiste seleziono il primo layer
    	if (this.layerStore.getCount()>0) {
    		this.cmbLayerSel.setValue(this.layerStore.getRange(0,0)[0].data.codTPN);
    		this.onQueryChangeLayer (this.cmbLayerSel, this.layerStore.getRange(0,0)[0], 0 );
    	}
    	else //altrimenti nascondo la lista
    		this.cmbLayerSel.setVisible(false);
		
    	this.add(this.formPanelSearch);
    	this.add(this.pnlResults);
    		
	   // this.addButton(this.cmbLayerSel);
    },
    
    /**
     * @method formSubmit
     * Metodo relativo alla gestione Ext.
     * 
     */
    formSubmit: function() {
    	
    	var fparams = this.formPanelSearch.getForm().getValues();
    	fparams.format = "ext";
    	
    	var submitOpt = {
    		url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxQueryServlet',
    		method: 'POST',
    		params: fparams,
    		waitMsg: ToloI18n.getMsg("ToloQueryPanelExt.formSubmit.wait"),
    		success: this.doOnQueryAjaxCallback,
    		failure: this.doOnQueryAjaxFailure,
    		scope: this
    	};
    	
    	// Submit ajax della form

    	this.waitMask=new Ext.LoadMask(this.id, {msg:ToloI18n.getMsg("ToloQueryPanelExt.formSubmit.wait")});
    	this.waitMask.show();
		new TolomeoExt.ToloCrossAjax().request(submitOpt);
		//this.formPanelSearch.getForm().submit(submitOpt);
    },
    
    /**
     * @method withSearchLayerFilter
     * Metodo relativo alla gestione Ext.
     *
     * @param {Object} record
     * record.
     * 
     */
    withSearchLayerFilter: function(record) {
    	return (record.data.eventiLayer.azioniEventiRicercaList.ricercaList.length!=0);
    },

    /**
     * @method showQuery
     * Metodo relativo alla gestione Ext.
     * 
     */
    showQuery: function () {
    	if (this.showHandler!=null) {
			this.showHandler.call(this);
		} else if (this.showHideContainer!=null) {
			this.showHideContainer.setVisible(true);
		} else this.setVisible(true); 	
    },

    /**
     * @method hideQuery
     * Metodo relativo alla gestione Ext.
     * 
     */
    hideQuery: function () {
    	//this.onQueryChangeLayer();
    	if (this.hideHandler!=null) {
			this.hideHandler.call(this);
		} else if (this.showHideContainer!=null) {
			this.showHideContainer.setVisible(false);
		} else this.setVisible(false); 
    },
    
    /**
     * @method showAjaxError
     * Metodo relativo alla gestione Ext.
     * 
     * @param {Object} transport
     * transport.
     */
    showAjaxError: function(transport){			
		Ext.Msg.show({
		    title: ToloI18n.getMsg("ToloQueryPanelExt.showAjaxError.title"),
		    msg: ToloI18n.getMsg("ToloQueryPanelExt.showAjaxError.msg"),
		    buttons: {		    	
			    yes: ToloI18n.getMsg("ToloQueryPanelExt.showAjaxError.btnMostra"),
			    cancel: ToloI18n.getMsg("ToloQueryPanelExt.showAjaxError.btnContinua")						    
		    },
		    icon: Ext.MessageBox.ERROR,
		    fn: function(btn) {
		    	switch(btn) {
		       		case 'yes':
		       			Ext.Msg.alert(ToloI18n.getMsg("ToloQueryPanelExt.showAjaxError.title"),transport.responseText);
		       			break;
		    	}
		    }
		});
		this.fireEvent('ajaxError', transport);						
	},

	/**
	 * @method onQueryChangeLayer
	 * Funzione chiamata quando nel pannello di query viene cambiato il layer selezionato.
	 * Provvede ad aggiornare l'elenco delle ricerche disponibili.
	 *
	 * @param {Ext.form.ComboBox} combo
	 * combo.
	 * 
	 * @param {Ext.data.Record} record
	 * record.
	 * 
	 * @param {Number} index
	 * index.
	 * 
	 */
	onQueryChangeLayer: function (combo, record, index ) {

		 var recbuff;
		 if (record instanceof Array) {
			 recbuff=record[0];
		 } else {
			 recbuff = record;
		 }
		 
		var queryList = recbuff.data.eventiLayer.azioniEventiRicercaList.ricercaList;
		var store = this.cmbTipo.store;
		
		//store.removeAll();
		store.loadData( queryList, false);

		// Se esiste seleziono prima scelta
		if (store.getCount()>0) {
			if(store.getCount() == 1){
    			this.cmbTipo.hide();
    		}else{
    			this.cmbTipo.show();
    		}
			this.cmbTipo.select(store.data.items[0]);
    		
			//this.cmbTipo.setValue(store.getRange(0,0)[0].data.id);
        	this.onQueryChangeRicerca (this.cmbTipo, store.getRange(0,0)[0], 0 );
		}
	}, 

	/**
	 * @method onQueryChangeRicerca
	 * Funzione chiamata quando nel pannello di query viene cambiata la ricerca selezionata.
	 * Provvede ad aggiornare l'elenco dei campi di ricerca e ad inizializzare, se disponibile, il meccanismo di suggest.
	 *
	 * @param {Ext.form.ComboBox} combo
	 * combo.
	 * 
	 * @param {Ext.data.Record} record
	 * record.
	 * 
	 * @param {Number} index
	 * index.
	 * 
	 */
	onQueryChangeRicerca: function (combo, record, index) {
		var me = this;
		
		var recbuff;
		if (record instanceof Array) {
			recbuff=record[0];
		} else {
			recbuff = record;
		}
		
		var nomiCampi = recbuff.data.metadatoRicerca.nomiCampi;
		var suggestDisponibile = recbuff.data.metadatoRicerca.suggestDisponibile;
		var suggestMinLength = recbuff.data.metadatoRicerca.suggestMinLength;
		var suggestProvider = recbuff.data.metadatoRicerca.suggestProvider;
		//var tipiCampi = query.metadatoRicerca.tipiCampi;
		
		this.pnlResults.hide();
		this.pnlCampi.removeAll();
		this.pnlCampi.hide();
		this.queryFields = [];

		// Inserimento campi e labels
		for (var i=0; i<nomiCampi.length; i++) {
			
			var proxy = TolomeoExt.ToloCrossAjaxUtil.getProxy(null, this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxSuggestServlet');
			proxy.extraParams= {
	        	format:"ext",
	        	idCampo: i,
	        	SRID: this.paramsJS.mappe.SRID,
	        	withGeom: this.suggestWithGeom
	        };
			
			var ds = Ext.create('Ext.data.JsonStore',{
		        proxy: proxy,
		        idCampo: i,
				listeners: {
					beforeload: {
						fn: function(store, options) {
							return me.setAutoCompleteBaseParams(store, options);  
						}
					},
					load: {
						fn: function(store, records, options) {
							store.sort('descriptionSuggest'+store.idCampo,'ASC');
						}
					}
				}
		    });
			var a = new TolomeoExt.ToloCrossAjax();
			
			ds.on('exception', a.storeException);
			
			
		    var field1 = null;
		    var fldopts = {
				fieldLabel: nomiCampi[i],
				name: 'campoRicerca' + i,
				allowBlank: false
			};
			
			var fldoptskey = {
				name: 'campoRicercaChiave' + i
			};
		    
		    // Se autosuggest
		    if ( (suggestDisponibile!=undefined) && (suggestDisponibile!=null) && (suggestDisponibile.size!=0) && suggestDisponibile[i]) {
		    
		    	if ( (suggestProvider!=undefined) && (suggestProvider!=null) && (suggestProvider.size!=0) && suggestProvider[i]!=null) {
		    	// suggest provider definito
		    		
		    		var filterBind 	= suggestProvider[i].filterBind;
		    		var fields 		= [];
		    		if (suggestProvider[i].valori.length>0) {
		    			for(var propertyName in suggestProvider[i].valori[0]) {
			    			fields.push(propertyName);
			    		}	
		    		} else {
		    			fields 		= ['COD', 'DESC'];
		    		}
		    		
		    		var store = Ext.create('Ext.data.JsonStore',{
		    		    // store configs
		    		    autoDestroy: true,
		    		    data: suggestProvider[i],
		    		    fields: fields, 
		    		    proxy: {
		    		    	type: 'memory',
		    		    	// reader configs
		    				reader: {
		    		    			type: 'json',
		    		    			root: 'valori'
		    		    			//idProperty: suggestProvider[i].valueFieldName // 'COD',
		    		    			//fields: fields //['COD', 'DESC', 'REG']
		    				}
		    			}
		    		});
		    		var field1key = Ext.create('Ext.form.field.Hidden',fldoptskey);
		    		this.pnlCampi.add(field1key);
		    		Ext.apply(fldopts, {
						forceSelection: true,
						lastQuery: '',
						store: store,
						displayField: suggestProvider[i].displayFieldName, //'DESC',
				        valueField: suggestProvider[i].valueFieldName, //'COD',
				        queryMode: 'local',
						selectOnFocus: true,
						editable:true,
						triggerAction: 'all',
						//queryParam: 'q',
				        typeAhead: true,
				        loadingText: ToloI18n.getMsg("ToloQueryPanelExt.onQueryChangeRicerca.loadingText"),
				        field1key: field1key,
				        anchor: "-3",
				        minChars: suggestMinLength[i],
		    			//hiddenName: 'campoRicerca' + i,		// invia con la form il valueField invece del contenuto del campo
		    			filterBind: filterBind,
		    			fieldIdx: i,
		    		    //listeners: { beforeQuery: { fn: function() { 
		    		    //									me.storeFilterApply(this.store, this.filterBind);} } }
		    			listeners: { change: { fn: function() { 
		    				me.suggestUpdate(suggestProvider, this);} 
		    				},
		    						 select: { 
				        					   fn: function( combo, records, eOpts ) {
					        						combo.field1key.setValue(records[0].get('key'));	
					        					},
					        					scope: this
				        					},
				        			 afterrender: { fn: function() { me.suggestUpdate(suggestProvider, this);} }
		    			}
			    	});
			    	
			    	
		    		field1 = Ext.create('Ext.form.ComboBox',fldopts);
		    		 
		    	} else {
		    		// senza suggest provider (meccanismo classico con AjaxSuggestServlet)
			    	var field1key = Ext.create('Ext.form.field.Hidden',fldoptskey);
		    		this.pnlCampi.add(field1key);
		    		Ext.apply(fldopts, {
						forceSelection: true, 
						store: ds,
				        displayField: 'descriptionSuggest' + i,
				        //valueField: 'key',
				        queryParam: 'q',
				        typeAhead: false,
				        loadingText: ToloI18n.getMsg("ToloQueryPanelExt.onQueryChangeRicerca.loadingText"),
				        anchor: "-3",
				        minChars: suggestMinLength[i],
				        hideTrigger:true,
				        field1key: field1key,
				        listeners: { select: {  
				        						fn: function( combo, records, eOpts ) {
					        						combo.field1key.setValue(records[0].get('key'));	
					        					},
					        					scope: this
				        					}
				        			}
			    		});
		    		field1 = Ext.create('Ext.form.ComboBox',fldopts);		
		    		
		    	}
		    }else {
			    field1 = Ext.create('Ext.form.TextField',fldopts);
		    }
		   
		    this.queryFields.push(field1);
			this.pnlCampi.add(field1);			
			if (i==0) field1.focus(false, 20);			
		}
		
		// Inserimento checkbox per filtro geografico
		if (recbuff.data.metadatoRicerca.geomFilterAvailable) {
			this.pnlCampi.add({
		                        boxLabel  : ToloI18n.getMsg("ToloQueryPanelExt.cercamappavis"),
		                        name      : 'geomFilterActive',
		                        xtype	  : 'checkboxfield'
		                    });
			
			this.geomFilterField = Ext.create('Ext.form.field.Hidden', {
                name      : 'geomFilter'
            });
			
			this.pnlCampi.add(this.geomFilterField);
			
			this.fireEvent('geomFilterFieldCreated', this.geomFilterField);
		}
		
		
		this.pnlCampi.setVisible(true);
		this.pnlCampi.doLayout();
	},

	/**
	 * @method arrayContains
	 * 
	 *
	 * @param {Object} arr
	 * 
	 * 
	 * @param {Object} obj
	 * 
	 * 
	 */
	arrayContains: function(arr, obj) {
		var retVal=false;
		for (var i=0; i<arr.length; i++) if (arr[i]==obj) retVal=true;
		
		return retVal;
	},

	/**
	 * @method suggestUpdate
	 * 
	 *
	 * @param {Object} suggestProvider
	 * 
	 * 
	 * @param {Object} changedField
	 * 
	 * 
	 */
	suggestUpdate: function(suggestProvider, changedField) {
		var dipendonoDa = [];
		var changedIdx = changedField.fieldIdx;
		
		for (var i=0; i<suggestProvider.length; i++) {
			
			if (changedIdx!=i) {
				//suggest del campo escluso changedIdx stesso
				var s = suggestProvider[i].filterBind;
				// Numerocampo#nomecampo 
				var s1 = s.split(",");
				for (var j=0; j<s1.length; j++) {		
					var s2 = s1[j].split("#");
					var numeroCampo = s2[0];
					if (numeroCampo==changedIdx) {
						// Il campo numeroCampo dipende da changedIdx
						// Controllo se c'� gi� altrimenti aggiungo nella lista 
						if (!this.arrayContains(dipendonoDa, numeroCampo)) dipendonoDa.push(i);
					}					
				}
			}
			
		}
		
		// Aggiorna lo storeFilter per ogni campo che dipende dal campo che � cambiato		
		for (var i =  0; i<dipendonoDa.length; i++) {
			var numCampo = dipendonoDa[i];
			this.storeFilterApply(this.queryFields[numCampo]);
		}
	},

	/**
	 * @method storeFilterApply
	 * 
	 *
	 * @param {Object} field
	 * 
	 * 
	 */
	storeFilterApply: function(field ) {
		var store		= field.store;
		var filterBind 	= field.filterBind;
					
		if (filterBind==undefined || filterBind==null || filterBind=="") return;
		
		field.setValue("");
		var filters = filterBind.split(",");
		var filtriExt = [];
		for (var i = 0; i<filters.length; i++) {
			var f = filters[i].split("#");
			if (this.queryFields[f[0]].getValue()!="") {
				var filtroExt = {
								root: 'data',
								property     : f[1],
				                value        : this.queryFields[f[0]].getValue(),
				                anyMatch     : true, //optional, defaults to true
				                caseSensitive: true  //optional, defaults to true
							};
				filtriExt.push(Ext.create('Ext.util.Filter',filtroExt));
			}
		}
		if (filtriExt.length != 0) {
			store.clearFilter(true);
			store.filter(filtriExt);
		} else {
			store.clearFilter(filtriExt);
		}
		
		
	},
	
	/**
	 * @method setAutoCompleteBaseParams
	 * 
	 * 
	 * @param {Object} ds
	 * ds.
	 * 
	 * @param {Object} options
	 * options.
	 * 
	 */
	setAutoCompleteBaseParams: function(ds, options) {
		
		ds.getProxy().extraParams = ds.getProxy().extraParams || {};
		Ext.apply(ds.getProxy().extraParams,{
			codTPN: this.cmbLayerSel.getValue(),
			idRicerca: this.cmbTipo.getValue()
		});
		//ds.setBaseParam('codTPN', this.cmbLayerSel.getValue());
		//ds.setBaseParam('idRicerca', this.cmbTipo.getValue());

		// necessario dalla versione 3.1 di extJS
		options.params.codTPN = this.cmbLayerSel.getValue();
		options.params.idRicerca = this.cmbTipo.getValue();
		
		for (var i=0; i<this.pnlCampi.items.items.length; i++) {
			var cmp = this.pnlCampi.items.items[i];
			ds.getProxy().extraParams[cmp.name] = cmp.getValue(); 
			options.params[cmp.name] = cmp.getValue();
		}
		
		return true;
	},
	
	/**
	 * @method focusFirstField
	 * 
	 * 
	 */
	focusFirstField:function() {
		if ( this.pnlCampi && this.pnlCampi.items && this.pnlCampi.items.items.size>0) this.pnlCampi.items.items[0].focus(false, 30); 	
	},	

	/**
	 * @method onQuery
	 * Esegue la ricerca tramite ajax sulla base dei valori contenuti nel pannello di query.
	 * Questa funzione si occupa solo di eseguire la chiamata ajax, che viene poi gestita da doOnQueryAjaxCallback.
	 * 
	 */
    onQuery: function () {
    	// Svuoto lista risultati precedenti
    	if (this.pnlResults) this.pnlResults.removeAll();

    	this.formSubmit();
    },

    /**
     * @method doOnQueryAjaxCallback
     * Funzione di callback della chiamata Ajax che effettua la ricerca onQuery.
     *
     * @param {Object} results
     * risposta della chiamata ajax.
     * 
     * @param {Object} store
     * store.
     * 
     */
    doOnQueryAjaxCallback: function (results, store) {

    	this.waitMask.hide();
    	if(results.length == 0){
    		this.onQueryNoResults();
    	} else if (results.length == 1) {
    		var geoms = new JSGeometryArray();
    		geoms.FromStore(results, store);
    		if (geoms.geometries[0].geometry=="") {
    			Ext.Msg.alert(ToloI18n.getMsg("ToloQueryPanelExt.doOnQueryAjaxCallback.title"),ToloI18n.getMsg("ToloQueryPanelExt.doOnQueryAjaxCallback.msg")).getDialog().focus();
    		}else{
    			this.onQuerySingleResult(geoms);
    		}
    	} else {
    		this.onQueryMultipleResults(results, store);
    	}
    },

    /**
     * @method doOnQueryAjaxFailure
     * Funzione di gestione errore avvenuto nella chiamata Ajax che effettua la ricerca onQuery.
     * 
     * @param {Object} store
     * store.
     * 
     */
    doOnQueryAjaxFailure: function (store) {
    	this.waitMask.hide();
    	//showAjaxError(transport);
    },

    /**
     * @method onQuerySingleResult
     * Gestisce la risposta della query; se si tratta di risposta univoca esegue le azioni previste chiamando la funzione doEventActions, 
     * nel caso di risposta multipla chiama onQueryMultipleResults.
     * 
     * @param {TolomeoExt.JSGeometryArray} geoms
     * Oggetti risultato della ricerca.
     * 
     */
    onQuerySingleResult: function (geoms) {		
    	this.fireEvent('querySelected', geoms);
    },

    /**
     * @method onQueryNoResults
     * Gestisce il caso in cui la ricerca non abbia dato dei risultati.
     * 
     */
    onQueryNoResults: function () {
 		this.fireEvent('queryNoResults');
 		//.getDialog()
 		Ext.MessageBox.alert(ToloI18n.getMsg("ToloQueryPanelExt.onQueryNoResults.title"), ToloI18n.getMsg("ToloQueryPanelExt.onQueryNoResults.msg")).focus();
    },

    /**
     * @method onQueryMultipleResults
     * Gestisce il caso di ricerca con pi� di un risultato visualizzando la lista e fornendo le funzionalit� per fare zoom all'intero 
     * insieme dei risultati, o al singolo.
     *
     * @param {Object} results
     * results
     * 
     * @param {Object} store
     * store
     * 
     */
    onQueryMultipleResults: function(results, store) {

    	this.pnlResults.setVisible(true);
    	
    	var listView = Ext.create('Ext.grid.Panel',{
    		store: store,
    		autoWidth: true,
    		emptyText: ToloI18n.getMsg("ToloQueryPanelExt.onQueryMultipleResults.emptyText"),
    		viewConfig: {
    			//firstCls: null,
    			trackOver: true
    		},
    		
    		//reserveScrollOffset: true,
    		//multiSelect: false,
    		listeners: {
    			'itemmouseenter': { fn: this.onQueryAddHighlighted, scope: this},
    			'itemmouseleave': { fn: this.onQueryClearHighlighted, scope: this},
    			'itemclick':      { fn: this.onQuerySingleResultByIndex, scope: this}
    		},
    		columns: [{
    			header: ToloI18n.getMsg("ToloQueryPanelExt.onQueryMultipleResults.colDesc"),
    			//width: 1,
    			flex: 1,
    			dataIndex: 'description'
    		}]
    	});

    	store.sort('description', 'ASC');
    	this.pnlResults.add(listView);
    	this.pnlResults.doLayout();
    }, 

    /**
     * @method onQuerySingleResultByIndex
     * Funzione che permette di estrerre il singolo oggetto nella lista dei risultati della query.
     * 
     * @param {Object} listView
     * listView
     * 
     * @param {Object} record
     * 
     * 
     * @param {Object} item
     * 
     * 
     * @param {Object} index
     * index
     * 
     * @param {Object} e
     * l'evento
     * 
     * @param {Object} eOpts
     * 
     * 
     */
    onQuerySingleResultByIndex: function (listView, record, item, index, e, eOpts) {
    	var geom = record.data; 
    		//listView.store.data.items[index].data;     	
    	var jsGeoArr = new JSGeometryArray();
    	jsGeoArr.FromStoreSingleRecord(geom);

    	this.onQuerySingleResult(jsGeoArr);
    },

    /**
     * @method onQueryAddHighlighted
     * Evidenzia il singolo oggetto identificato da index all'interno del risultato della query. Viene invocato sull'evento onmouseover sulla lista dei risultati.
     *
     * @param {Object} listView
     * listView
     * 
     * @param {Object} record
     * 
     * 
     * @param {Object} item
     * 
     * 
     * @param {Object} index
     * index
     * 
     * @param {Object} e
     * l'evento
     * 
     */
    onQueryAddHighlighted: function (listView,  record, item, index, e, eOpts) {

    	//store.data.items[index].data;
    	var geom = record.data;
    	var jsGeoArr = new JSGeometryArray();
    	jsGeoArr.FromStoreSingleRecord(geom);

    	this.fireEvent('queryMultipleResultHoverStart', jsGeoArr.geometries[0]); 
    	/*
    	TODO su chi ascolta evento
    	var geoms   = document.getElementById("divQueryListaRisultati").geoms;		

    	addHighlighted(geoms.geometries[index]);
    	 */

    },

    /**
     * @method onQueryClearHighlighted
     * Viene invocato sull'evento onmouseout sulla lista dei risultati per eseguire eventualmente operazione di deselezione. Attualmente non esegue alcuna operazione.
     *
     * @param {Object} listView
     * listView
     * 
     * @param {Object} record
     * 
     * 
     * @param {Object} item
     * 
     * 
     * @param {Object} index
     * index
     * 
     * @param {Object} e
     * l'evento
     * 
     * @param {Object} eOpts
     * 
     * 
     */
    onQueryClearHighlighted: function (listView, record, item, index, e, eOpts) {
    	var geom = record.data; 
    	var jsGeoArr = new JSGeometryArray();
    	jsGeoArr.FromStoreSingleRecord(geom);
    	
    	this.fireEvent('queryMultipleResultOut', jsGeoArr.geometries[0] ); 
    },


    /**
     * @method setGeomFilterField
     * 
     *
     * @param {Object} geom
     * 
     * 
     */
    setGeomFilterField: function(geom) {
    	if (this.geomFilterField) {
    		this.geomFilterField.setValue(geom);
    	}
    	
    }
    
    /*
     * @method onQueryZoomOggetto
     * Esegue lo zoom all'elemento individuato da index all'interno del risultato della query.
     * TODO su ci ascolta il relativo evento
     *
     * @param {Number} index
     * index
     * 
     onQueryZoomOggetto: function (index){
     	var geoms   = document.getElementById("divQueryListaRisultati").geoms;
     	if (index || index == 0) {
     		zoomToExtent(geoms.geometries[index].boundingbox);
     	} else {
     		zoomToExtent(geoms.boundingbox);
     	}	
     }*/
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * Class: TolomeoExt.ToloButtonPanelExt
 * 
 * Inherits from:
 *  - <Ext.Toolbar>
 *
 */

Ext.define('TolomeoExt.ToloButtonPanelExt', { 
//TolomeoExt.ToloButtonPanelExt = Ext.extend(Ext.Toolbar,{

	extend: 'Ext.Toolbar',
	//requires: [],
	
	/**
	 * Settato automaticamente da bindToApi. 
	 * @type {TolomeoExt.ToloMapAPIExt}
	 */
	api: null,
	
	/** 
	 * Property: paramsJS
	 * {JSONObject} 
	 */
	paramsJS:null,

	/** 
	 * Property: TOLOMEOServer
	 * {String} 
	 * 
	 */
	TOLOMEOServer: null,

	/** 
	 * Property: TOLOMEOContext
	 * {String}
	 * 
	 */
	TOLOMEOContext: null,

	/** 
	 * Property: withPan
	 * {Boolean} Indica se il pulsante "Pan" deve essere presente.
	 */
	withPan: true,

	/** 
	 * Property: withPanArrows
	 * {Boolean} Indica se i pulsanti "PanNord", "PanEst", "PanSud" e "PanOvest" deve essere presente
	 */
	withPanArrows: false,

	/** 
	 * Property: withNavHistory
	 * {Boolean} Indica se i pulsanti di navigation history sono attivi.
	 */
	withNavHistory: true,
	
	/** 
	 * Property: withZoomIn
	 * {Boolean} Indica se il pulsante "ZoomIn" deve essere presente.
	 */
	withZoomIn: true,

	/** 
	 * Property: withZoomOut
	 * {Boolean} Indica se il pulsante "ZoomOut" deve essere presente.
	 */
	withZoomOut: true,
	
	/** 
	 * Property: withZoomBoox
	 * {Boolean} Indica se il pulsante "withZoomBoox" deve essere presente.
	 */
	withZoomBox: true,

	/** 
	 * Property: withZoomAll
	 * {Boolean} Indica se il pulsante "ZoomAll" deve essere presente.
	 */
	withZoomAll: true,

	/** 
	 * Property: withMeasure
	 * {Boolean} Indica se il pulsante "Measure" deve essere presente.
	 */
	withMeasure: true,

	/** 
	 * Property: withPrint
	 * {Boolean} Indica se il pulsante "Print" deve essere presente.
	 */
	withPrint: true,

	/** 
	 * Property: withLegenda
	 * {Boolean} Indica se il pulsante "Legenda" deve essere presente.
	 */
	withLegenda: true,

	/** 
	 * Property: withQuery
	 * {Boolean} Indica se il pulsante "Query" deve essere presente.
	 */
	withQuery: true,

	/** 
	 * Property: withSeleziona
	 * {Boolean} Indica se il pulsante "Seleziona" deve essere presente.
	 */
	withSeleziona: true,
	
	/** 
	 * Property: withInfoSeleziona
	 * {Boolean} Indica se il pulsante "Seleziona" deve aver le info sui tasti funzione.
	 */
	withInfoSelezione: true,

	/** 
	 * Property: withAnnullaSeleziona
	 * {Boolean} Indica se il pulsante "AnnullaSeleziona" deve essere presente.
	 */
	withAnnullaSeleziona: true,

	/** 
	 * Property: withLayerList
	 * {Boolean} Indica se il pulsante "LayerList" deve essere presente.
	 */
	withLayerList: true,

	/** 
	 * Property: withIdentify
	 * {Boolean} Indica se il pulsante "Identify" deve essere presente.
	 */
	withIdentify: true,

	/** 
	 * Property: withNuovo
	 * {Boolean} Indica se il pulsante "Nuovo" deve essere presente.
	 */
	withNuovo: true,
	
	/** 
	 * Property: withNuovoDaLayer
	 * {Boolean} Indica se il pulsante "Nuovo da layer" deve essere presente.
	 */
	withNuovoDaLayer: true,
	
	/** 
	 * Property: withNuovoDaImport
	 * {Boolean} Indica se il pulsante "Nuovo da import" deve essere presente.
	 */
	withNuovoDaImport: true,

	/** 
	 * Property: withUpdateAlfa
	 * {Boolean} Indica se il pulsante "UpdateAlfa" deve essere presente.
	 */
	withUpdateAlfa: true,

	/** 
	 * Property: withAdd
	 * {Boolean} Indica se il pulsante "Add" deve essere presente.
	 */
	withAdd: true,

	/** 
	 * Property: withSubtract
	 * {Boolean} Indica se il pulsante "Subtract" deve essere presente.
	 */
	withSubtract: true,

	/** 
	 * Property: withAddSub
	 * {Boolean} Indica se il pulsante "AddSub" deve essere presente.
	 */
	withAddSub: true,

	/** 
	 * Property: withVertexEdit
	 * {Boolean} Indica se il pulsante "VertexEdit" deve essere presente.
	 */
	withVertexEdit: true,

	/** 
	 * Property: withDragDrop
	 * {Boolean} Indica se il pulsante "DragDrop" deve essere presente.
	 */
	withDragDrop: true,

	/** 
	 * Property: withDelete
	 * {Boolean} Indica se il pulsante "Delete" deve essere presente.
	 */
	withDelete: true,

	/** 
	 * Property: withAutoIdentify
	 * {Boolean} Indica se il pulsante "AutoIdentify" deve essere presente.
	 */
	withAutoIdentify: true,
	
	/** 
	 * Property: withFilter
	 * {Boolean} Indica se il pulsante "filter" deve essere presente.
	 */
	withTemporalFilter: true,
	
	/** 
	 * Property: withTimeMachine
	 * {Boolean} Indica se il pulsante della macchina temporale deve essere presente.
	 */
	withTimeMachine: true,
	
	/** 
	 * Property: withSnap
	 * {Boolean} Indica se il pulsante "snap" deve essere presente.
	 */
	withSnap: true,
	
	/** 
	 * Property: withCsw
	 * {Boolean} Indica se il pulsante "csw" deve essere presente.
	 */
	withCsw: true,
	
	/** 
	 * Property: withWMSExporer
	 * {Boolean} Indica se il pulsante "WMSExplorer" deve essere presente.
	 */
	withWMSExporer: true,
	
	/** 
	 * Property: with3D
	 * {Boolean} Indica se il pulsante "3D" deve essere presente.
	 */
	with3D: true,
	
	/** 
	 * Property: withShowCoordinate
	 * {Boolean} Indica se il pulsante "Mostra Cooordinate" deve essere presente.
	 */
	//withShowCoordinate: true,
	
	/** 
	 * Property: temporalFilterWindow
	 */
	temporalFilterWindow: null,

	/** 
	 * Property: buttons
	 * {Array} Array contenente i controllo (button od altro) presenti nella pulsantiera.
	 */
	buttons: null,

	/** 
	 * Property: btnMeasure
	 * 
	 */
	btnMeasure: null,

	/** 
	 * Property: iconBasePath
	 * 
	 */
	iconBasePath:null,  

	/** 
	 * Property: cmbLayerSel
	 * 
	 */
	cmbLayerSel: null, 

	/** 
	 * Property: groupPrefix
	 * 
	 */
	groupPrefix: null, 

	/**
	 * Method: withSearchLayerFilter
	 * 
	 * Parameters:
	 * record - il record.
	 * id - l'identificativo.
	 */
	withSearchLayerFilter: function(record,id){
		return record.data.eventiLayer.raw.interactable;
	},

	/**
	 * Constructor: TolomeoExt.ToloButtonPanelExt
	 * Create a new button toolbar
	 *
	 * Returns:
	 * {<TolomeoExt.ToloButtonPanelExt>} A new button toolbar
	 */
	initComponent: function() {

		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
		this.groupPrefix = this.getId();

		this.buttons = new Array();
		
		this.callParent();

		Ext.QuickTips.init();

		// add custom events
		this.addEvents('onPanPressFn');
		this.addEvents('onPanReleaseFn');
		this.addEvents('onCustomButtonPressFn');
		this.addEvents('onCustomButtonDefaultReleaseFn');

		this.addEvents('onNuovoPressFn');
		this.addEvents('onNuovoReleaseFn');
		
		this.addEvents('onNuovoDaLayerPressFn');
		this.addEvents('onNuovoDaLayerReleaseFn');

		this.addEvents('onNuovoDaImportPressFn');
		this.addEvents('onNuovoDaImportReleaseFn');
		
		this.addEvents('onPanNordPressFn');
		this.addEvents('onPanEstPressFn');
		this.addEvents('onPanSudPressFn');
		this.addEvents('onPanOvestPressFn');

		this.addEvents('onHistoryNextPressFn');
		this.addEvents('onHistoryPrevPressFn');
		
		this.addEvents('onZoomInPressFn');
		this.addEvents('onZoomOutPressFn');
		this.addEvents('onZoomBoxPressFn');
		this.addEvents('onZoomAllPressFn');

		this.addEvents('onMeasureActivate');
		this.addEvents('onMeasureDeactivate');
		this.addEvents('onMeasureTypeChange');

		this.addEvents('onPrintPressFn');
		this.addEvents('onLegendPressFn');
		this.addEvents('onLegendReleaseFn');
		this.addEvents('onQueryPressFn');
		this.addEvents('onQueryReleaseFn');
		this.addEvents('onSelectPressFn');
		this.addEvents('onSelectReleaseFn');
		this.addEvents('onIdentifyPressFn');

		this.addEvents('onDeletePressFn');
		this.addEvents('onUpdateAlfaPressFn');
		this.addEvents('onAddPressFn');

		this.addEvents('onAddReleaseFn');
		this.addEvents('onSubtractPressFn');
		this.addEvents('onSubtractReleaseFn');
		this.addEvents('onAddSubPressFn');
		this.addEvents('onAddSubReleaseFn');
		this.addEvents('onVertexEditPressFn');
		this.addEvents('onVertexEditReleaseFn');
		this.addEvents('onDragDropPressFn');

		this.addEvents('onDragDropReleaseFn');
		this.addEvents('onAnnullaSelezioniPressFn');
		this.addEvents('onAutoIdentifyPressFn');
		this.addEvents('onAutoIdentifyReleaseFn');
		
		this.addEvents('onTemporalFilterPressFn');
		this.addEvents('onTemporalFilterReleaseFn');
		this.addEvents('onTemporalFilterApply');
		
		this.addEvents('onTimeMachinePressFn');
		this.addEvents('onTimeMachineReleaseFn');
		
		this.addEvents('onCswPressFn');
		this.addEvents('onCswReleaseFn');
		
		this.addEvents('onWMSPressFn');
		this.addEvents('onWMSReleaseFn');
		
		this.addEvents('showPermalinkClicked');
		this.addEvents('exportForQgisClicked');
		this.addEvents('showInfoClicked');
		this.addEvents('regeneratePageClicked');
		this.addEvents('mailToAdministratorClicked');
		this.addEvents('showGuideClicked');
		this.addEvents('showFaqClicked');
		
		this.addEvents('showWithOSMClicked');
		this.addEvents('showWithGoogleSatelliteClassicClicked');
		this.addEvents('showWithGoogleMapClassicClicked');
		this.addEvents('showWithGoogleSatelliteClicked');
		this.addEvents('showWithGoogleMapClicked');
		this.addEvents('showWithBingObClicked');
		this.addEvents('showWithBingSatelliteClicked');
		this.addEvents('showWithBingMapClicked');
		this.addEvents('showWithHereSatelliteClicked');
		this.addEvents('showWithHereMapClicked');
		
		//this.addEvents('onCoordinatePressFn');
		//this.addEvents('onCoordinateReleaseFn');	
		
		this.addEvents({
			onSnapPressFn  : true,
			onSnapReleaseFn: true
		});

		var thisToolbar = this;
		var layout = this.paramsJS.layOut;

		/*
	    var btnModel = Ext.extend(Ext.Button, {
	    	enableToggle:true,
	    	listeners: { toggle: { fn: this.btnToggleHandler, scope: thisToolbar}}
	    });
		 */

		var listenersToggle = {toggle: {fn: this.btnToggleHandler, scope: thisToolbar}};
		var listenersClick  = {click:  {fn: this.btnClickHandler,  scope: thisToolbar}};
		//var listenersCheck  = { check: { fn: this.btnClickHandler, scope: thisToolbar}};

		var btnSplitModel = Ext.extend(Ext.SplitButton, {
			enableToggle:false,
			scale: (this.defaults && this.defaults.scale) ? this.defaults.scale : undefined,
			defaults: (this.defaults) ? this.defaults : undefined,
			listeners: {toggle: {fn: this.btnToggleHandler, scope: thisToolbar}}
		});

		if (this.iconBasePath==null) this.iconBasePath =  TolomeoExt.Vars.TOLOMEOServer + TolomeoExt.Vars.TOLOMEOStaticRoot + 'img/icone/16-default/'; // this.TOLOMEOServer + '/img/icone/16-default/';
		
		var keymap = new Ext.KeyMap(document, null);

		// Pan
		if (this.withPan) { 
			this.btnPan = this.addButton({
				//scale: 'medium',
				icon: this.iconBasePath + 'hand.gif',
				enableToggle: true,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnPan.overflowText"),
				pressed:true,
				allowDepress: false,
				toggleGroup: this.encodeToggleGroup(2),
				becomeDefault: true,
				groupDefault: true,				
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnPan.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnPan.tooltip.title")
					},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'Pan',
				opCode: TolomeoExt.ToloAPIOpCodes.btnPan
			});
			keymap.addBinding({
				    key: Ext.EventObject.A,
				    fn: function(key){
				    	if(!this.btnPan.pressed){
							this.btnPan.toggle(true);
				    	}
					},
					alt: true,
					ctrl: true,
				    scope: this,
				    stopEvent: true
				});
		}
		
		// Pan con frecce
		if (this.withPanArrows) { 
			// Pan Nord
			this.addButton({
				icon: this.iconBasePath + 'su.gif',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnPanN.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnPanN.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnPanN.tooltip.title")
					},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'PanNord',
				opCode: TolomeoExt.ToloAPIOpCodes.btnPanNord
			});

			// Pan Sud
			this.addButton({
				icon: this.iconBasePath + 'giu.gif',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnPanS.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnPanS.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnPanS.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'PanSud',
				opCode: TolomeoExt.ToloAPIOpCodes.btnPanSud
			});

			// Pan Ovest
			this.addButton({
				icon: this.iconBasePath + 'sinistra.gif',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnPanS.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnPanS.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnPanS.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'PanOvest',
				opCode: TolomeoExt.ToloAPIOpCodes.btnPanOvest
			});

			// Pan Est
			this.addButton({
				icon: this.iconBasePath + 'destra.gif',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnPanS.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnPanS.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnPanS.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'PanEst',
				opCode: TolomeoExt.ToloAPIOpCodes.btnPanEst
			});
		}
		
		if (this.withNavHistory) {
			// History prev
			this.addButton({
				icon: this.iconBasePath + 'zoom_backward.png',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnNavHistPrev.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnNavHistPrev.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnNavHistPrev.tooltip.title")
				},
				listeners: listenersClick,
				id: this.getId() + 'HistoryPrevBtn',
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'HistoryPrev',
				opCode: TolomeoExt.ToloAPIOpCodes.btnHistoryPrev,
				disabled: true
			});
			
			// History next
			this.addButton({
				icon: this.iconBasePath + 'zoom_forward.png',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnNavHistNext.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnNavHistNext.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnNavHistNext.tooltip.title")
				},
				listeners: listenersClick,
				id: this.getId() + 'HistoryNextBtn',
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'HistoryNext',
				opCode: TolomeoExt.ToloAPIOpCodes.btnHistoryNext,
				disabled: true
			});			
		}
		
		
		this.add('-');
		
		// ZoomIn
		if (this.withZoomIn) {
			this.addButton({
				icon: this.iconBasePath + 'zoomin.gif',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomIn.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomIn.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomIn.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'ZoomIn',
				opCode: TolomeoExt.ToloAPIOpCodes.btnZoomIn
			});
		}

		// ZoomOut
		if (this.withZoomOut) {
			this.addButton({
				icon: this.iconBasePath + 'zoomout.gif',
				enableToggle:false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomOut.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomOut.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomOut.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'ZoomOut',
				opCode: TolomeoExt.ToloAPIOpCodes.btnZoomOut
			});
		}
		
		// ZoomBoox
		if (this.withZoomBox) {
			this.addButton({
				icon: this.iconBasePath + 'zoombox.gif',
				enableToggle: true,
				allowDepress: false,
				toggleGroup: this.encodeToggleGroup(2),
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomBox.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomBox.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomBox.tooltip.title")
				},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'ZoomBox',
				opCode: TolomeoExt.ToloAPIOpCodes.btnZoomBox
			});
		}

		// ZoomAll
		if (this.withZoomAll) {
			this.addButton({
				icon: this.iconBasePath + 'nozoom.gif',
				enableToggle:false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomAll.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomAll.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnZoomAll.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'ZoomAll',
				opCode: TolomeoExt.ToloAPIOpCodes.btnZoomAll
			});
		}
		
		if (this.withZoomIn || this.withZoomOut || this.withZoomAll) this.add('-');
		
		/*
		Ext.define('btnSplitModel', {
			extend: 'Ext.SplitButton',
			scale: (this.defaults && this.defaults.scale) ? this.defaults.scale : undefined,
			defaults: (this.defaults) ? this.defaults : undefined,
			listeners: {toggle: {fn: this.btnToggleHandler, scope: thisToolbar}}
		});
		*/
		
		// Measure
		if (this.withMeasure) {
			this.btnMeasure = this.addButton(
					new btnSplitModel({
						icon: this.iconBasePath + 'misura.gif',
						enableToggle: true,
						allowDepress: false,
						toggleGroup: this.encodeToggleGroup(2),
						overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasure.overflowText"),
						tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasure.tooltip.text"), 
							title: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasure.tooltip.title")
						},
						scale: (this.defaults && this.defaults.scale) ? this.defaults.scale : "small",
						// Proprietà aggiuntive per Tolomeo
						listeners: { toggle: {fn: this.btnMeasureMng , scope: thisToolbar} },
						handlerBaseName: 'Measure',
						menu: {
							cls: 'clearCSS',
							
						
							items: [{
								text: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasurePoly.Text"),
								checked: false,
								iconCls : 'iconPolygonalMeasure',
								cls : "clearCss",
								group: 2,
								overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasurePoly.overflowText"),
								tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasurePoly.tooltip.text"), 
									title: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasurePoly.tooltip.title")
								},
								listeners: { checkchange: {fn: this.btnMeasureItemMng, scope: thisToolbar} },
								// Proprietà aggiuntive per Tolomeo
								handlerBaseName: 'MeasurePolygon',
								measureType: 0,
								opCode: TolomeoExt.ToloAPIOpCodes.btnMeasure
							},{
								text: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasureCircle.Text"),
								checked: false,
								iconCls : 'iconCircularMeasure',
								cls : "clearCss",
								group: 2,
								overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasureCircle.overflowText"),
								tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasureCircle.tooltip.text"), 
									title: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasureCircle.tooltip.title")
								},
								listeners: { checkchange: {fn: this.btnMeasureItemMng , scope: thisToolbar} },
								// Proprietà aggiuntive per Tolomeo
								handlerBaseName: 'MeasureCircle',
								measureType: 1,
								opCode: TolomeoExt.ToloAPIOpCodes.btnMeasure
							},{
								text: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasureLine.Text"),
								checked: true,
								iconCls : 'iconLinearMeasure',
								cls : "clearCss",
								group: 2,
								overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasureLine.overflowText"),
								tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasureLine.tooltip.text"), 
									title: ToloI18n.getMsg("ToloButtonPanelExt.btnMeasureLine.tooltip.title")
								},
								listeners: { checkchange: {fn: this.btnMeasureItemMng , scope: thisToolbar} },
								// Proprietà aggiuntive per Tolomeo
								handlerBaseName: 'MeasureLine',
								measureType: 2,
								opCode: TolomeoExt.ToloAPIOpCodes.btnMeasure
							}]
						}
					})
			);
			this.add('-');
		}
		
		// TimeMachine
		// TODO  generalizzare adesso considera solo mappa[0] gestisce solo prima mappa
		if (this.withTimeMachine && (this.paramsJS.mappe.mappaList[0].timeMachineList!=null && this.paramsJS.mappe.mappaList[0].timeMachineList.length>0)) {
			this.addButton({
				icon: this.iconBasePath + 'macchinaTempo.png',
				enableToggle: true,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnTimeMachine.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnTimeMachine.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnTimeMachine.tooltip.title")
				},
				listeners: listenersToggle,
				toggleGroup: this.encodeToggleGroup(0),
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'TimeMachine',
				opCode: TolomeoExt.ToloAPIOpCodes.btnTimeMachine
			});
			this.add('-');
		}

		// Print
		if (this.withPrint) {
			this.addButton({
				icon: this.iconBasePath + 'print.gif',
				enableToggle:false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnPrint.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnPrint.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnPrint.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'Print',
				opCode: TolomeoExt.ToloAPIOpCodes.btnPrint
			});
		}

		// Legenda
		if (this.withLegenda) {
			if (this.paramsJS.mappe.mappaList[0].legenda!=null) {
				this.addButton({
					icon: this.iconBasePath + 'legenda.gif',
					enableToggle: true,
					toggleGroup: this.encodeToggleGroup(1),
					overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnLegenda.overflowText"),
					tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnLegenda.tooltip.text"), 
						title: ToloI18n.getMsg("ToloButtonPanelExt.btnLegenda.tooltip.title")
					},
					listeners: listenersToggle,
					// Proprietà aggiuntive per Tolomeo
					handlerBaseName: 'Legend',
					opCode: TolomeoExt.ToloAPIOpCodes.btnLegenda
				});
			}
		}

		// verifica la necessità di pannello query
		if (this.withQuery) {
			var bQuery = false;
			for (var i=0; i<this.paramsJS.azioniEventi.eventiLayerList.length; i++) {
				if (this.paramsJS.azioniEventi.eventiLayerList[i].azioniEventiRicercaList.ricercaList.length != 0){
					bQuery = true; 
					break;
				}
			}
			if (bQuery) {
				this.addButton({
					icon: this.iconBasePath + 'ricerca.gif',
					enableToggle: true,
					toggleGroup: this.encodeToggleGroup(1),
					overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnCerca.overflowText"),
					tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnCerca.tooltip.text"), 
						title: ToloI18n.getMsg("ToloButtonPanelExt.btnCerca.tooltip.title")
					},
					listeners: listenersToggle,
					// Proprietà aggiuntive per Tolomeo
					handlerBaseName: 'Query',
					opCode: TolomeoExt.ToloAPIOpCodes.btnRicerca
				});
			}
		}

		if (this.withPrint || this.withLegenda || this.withQuery) this.add('-');
		
		// Seleziona
		if (this.withSeleziona) {
			
			var helpMsg = ToloI18n.getMsg("ToloButtonPanelExt.btnSelect.m1");
			if (this.withInfoSelezione) {
				helpMsg += ToloI18n.getMsg("ToloButtonPanelExt.btnSelect.m2");
				if (this.paramsJS.selectDefaultMode=='FIRSTONTOP') {
					helpMsg += ToloI18n.getMsg("ToloButtonPanelExt.btnSelect.m3"); 
				} else {
					helpMsg += ToloI18n.getMsg("ToloButtonPanelExt.btnSelect.m4");  
				}
				helpMsg += ToloI18n.getMsg("ToloButtonPanelExt.btnSelect.m5");
				
			}
			
			
			
			ToloI18n.getMsg("ToloButtonPanelExt.btnCerca.overflowText"),
			
			this.btnSelect = this.addButton({
				icon: this.iconBasePath + 'sector.gif',
				enableToggle:true,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnSelect.overflowtext"),
				toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				tooltip: {text: helpMsg, title:ToloI18n.getMsg("ToloButtonPanelExt.btnSelect.title")},
				listeners: listenersToggle,
				becomeDefault: true,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'Select',
				opCode: TolomeoExt.ToloAPIOpCodes.btnSeleziona
			});
			
			if(this.paramsJS.getSelectableCodTPN().length>0){
				keymap.addBinding({
					    key: Ext.EventObject.S,
					    fn: function(key){
					    	if(!this.btnSelect.disabled && !this.btnSelect.pressed){
								this.btnSelect.toggle(true);
					    	}
						},
						alt: true,
						ctrl: true,
					    scope: this,
					    stopEvent: true
					});
			}
		}
		
		// Annulla selezioni
		if (this.withAnnullaSeleziona) {
			this.addButton({
				icon: this.iconBasePath + 'unsector.gif',
				enableToggle:false,
				toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnAnnullaSeleziona.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnAnnullaSeleziona.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnAnnullaSeleziona.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'AnnullaSelezioni',
				opCode: TolomeoExt.ToloAPIOpCodes.btnAnnullaSelezioni
			});
			
		}
		
		if ( this.withSeleziona || this.withAnnullaSeleziona ) this.add('-');
		//if (this.withLayerList || this.withNuovo || this.withUpdateAlfa || this.withAdd || this.withSubtract || this.withAddSub || this.withVertexEdit || this.withDragDrop || this.withDelete ) this.add('Editing :  ');

		// Layer list
		if (this.withLayerList) {
			
			
			var layerStore = new Ext.data.JsonStore({
				proxy: {
		    		type: 'memory'//,
		    		// reader configs
		    		//reader: {
		    		//	type: 'json',
		    		//	root: 'valori',
		    		//	idProperty: suggestProvider[i].valueFieldName // 'COD',
		    			//fields: fields //['COD', 'DESC', 'REG']
					//}
				},
				
				mode: "local",
				data: this.paramsJS.azioniEventi.eventiLayerList,
				autoLoad: false,
				remoteSort: false,
				fields: [{
						name: 'descrizioneLayer',
						mapping: 'descrizioneLayer'
					},{
						name: 'codTPN',
						mapping: 'codTPN'
					},{
						name: 'eventiLayer',
					    convert: function (v, rec) { return rec; }
					}]
			});
			
			/*
			var layerStore = new Ext.data.JsonStore({
				fields: [{
					name: 'descrizioneLayer',
					mapping: 'descrizioneLayer'
				},{
					name: 'codTPN',
					mapping: 'codTPN'
				},{
					name: 'eventiLayer',
				    convert: function (v, rec) { return rec; }
				}],
				proxy: {
					
				}
				data : this.paramsJS.azioniEventi.eventiLayerList
			});*/

			layerStore.filterBy(this.withSearchLayerFilter);

			this.cmbLayerSel = new Ext.form.ComboBox({
				typeAhead: true,
				queryMode: 'local',
				triggerAction: 'all',
				forceSelection: true,
				editable: false,
				allowBlank: false,
				emptyText: ToloI18n.getMsg("ToloButtonPanelExt.cmbLayerSel.emptyText"),
				selectOnFocus:true,
				hiddenName: 'codTPN',
				valueField: 'codTPN',
				displayField: 'descrizioneLayer',
				name: 'codTPN1',
				lastQuery: '',
				listeners: {select: {fn: this.cmbSelectLayerHandler, scope: thisToolbar},
				beforerender: function(){this.store.filterBy(function(record,id){
					return record.data.eventiLayer.raw.interactable;
				},this);}},
				store: layerStore,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: null
				//TODO opCode: TolomeoExt.ToloAPIOpCodes.btnPan
			});				

			// Se esiste seleziono prima scelta
			if (layerStore.getCount()>1) {
				this.cmbLayerSel.setValue(layerStore.getRange(0,0)[0].data.codTPN);
			} else if (layerStore.getCount()==1) {
				this.cmbLayerSel.setVisible(false);
				this.cmbLayerSel.setValue(layerStore.getRange(0,0)[0].data.codTPN);
			} else if (layerStore.getCount()<1) {
				this.cmbLayerSel.setVisible(false);
			}

			this.addCombo(this.cmbLayerSel);
		}    		

		// Visualizza informazioni su oggetto		
		if (this.withIdentify) {
			// Identify
			this.addButton({
				icon: this.iconBasePath + 'info.gif',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnIdentify.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnIdentify.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnIdentify.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'Identify',
				opCode: TolomeoExt.ToloAPIOpCodes.btnIdentify
			});
		}						

		// Nuovo
		if (this.withNuovo) {
			/*
			this.addButton({
				icon: this.iconBasePath +  'nuovo.gif',
				enableToggle: true,
				overflowText: "Nuovo oggetto",
				toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				tooltip: {text: 'Crea un nuovo oggetto cartografico nella mappa (punto, linea, poligono, ecc...)', title:'Nuovo oggetto'},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'Nuovo',
				opCode: TolomeoExt.ToloAPIOpCodes.btnNuovo
			});
			*/
			
			this.btnNew = this.addButton(
					new btnSplitModel({
						enableToggle: true,
						icon: this.iconBasePath + 'nuovo.gif',
						allowDepress: false,
						toggleGroup: this.encodeToggleGroup(2),
						overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnNew.overflowText"),
						tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnNew.tooltip.text"), 
							title: ToloI18n.getMsg("ToloButtonPanelExt.btnNew.tooltip.title")
						},
						//listeners: listenersToggle,
						listeners: { toggle: {fn: this.btnNewToggleHandler , scope: thisToolbar} },
						scale: (this.defaults && this.defaults.scale) ? this.defaults.scale : "small",
						// Proprietà aggiuntive per Tolomeo
						//listeners: { toggle: {fn: this.btnMeasureMng , scope: thisToolbar} },
						handlerBaseName: 'Nuovo',
						opCode: TolomeoExt.ToloAPIOpCodes.btnNuovo,
						newType: 0,
						menu: {
							cls: 'clearCSS',
							items: [{
								text: 'Nuovo',
								checked: true,
								icon: this.iconBasePath + 'nuovo.gif',
								group: 2,
								overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnNewAMano.overflowText"),
								tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnNewAMano.tooltip.text"), 
									title: ToloI18n.getMsg("ToloButtonPanelExt.btnNewAMano.tooltip.title")
								},
								listeners: { checkchange: {fn: this.btnNewItemMng, scope: thisToolbar} },
								toggleGroup: this.encodeToggleGroup(2),
								// Proprietà aggiuntive per Tolomeo
								//handlerBaseName: 'Nuovo',
								newType: 0
								//opCode: TolomeoExt.ToloAPIOpCodes.btnNuovo
							},{
								text: 'Nuovo con CAD',
								checked: false,
								icon: this.iconBasePath + 'nuovoByCAD.gif',
								group: 2,
								overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnNewCAD.overflowText"),
								tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnNewCAD.tooltip.text"), 
									title: ToloI18n.getMsg("ToloButtonPanelExt.btnNewCAD.tooltip.title")
								},
								listeners: { checkchange: {fn: this.btnNewItemMng , scope: thisToolbar} },
								toggleGroup: this.encodeToggleGroup(2),
								// Proprietà aggiuntive per Tolomeo
								//handlerBaseName: 'Nuovo',
								newType: 1
								//opCode: TolomeoExt.ToloAPIOpCodes.btnNuovo
							}]
						}
					})
			);
			
		}

		// NuovoDaLayer
		if (this.withNuovoDaLayer) {
			
			this.addButton({
				icon: this.iconBasePath +  'aggiungiOggetto2.png',
				enableToggle: false,
				//toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnNuovoDaLayer.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnNuovoDaLayer.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnNuovoDaLayer.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'NuovoDaLayer',
				opCode: TolomeoExt.ToloAPIOpCodes.btnNuovoDaLayer
			});
			
		}

		// NuovoDaImport
		if (this.withNuovoDaImport) {
			
			this.addButton({
				icon: this.iconBasePath +  'application_get.png',
				enableToggle: false,
				//toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnNuovoDaImport.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnNuovoDaImport.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnNuovoDaImport.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'NuovoDaImport',
				opCode: TolomeoExt.ToloAPIOpCodes.btnNuovoDaImport
			});
			
		}
		
		// UpdateAlfa
		if (this.withUpdateAlfa) {
			this.addButton({
				icon: this.iconBasePath + 'modifica.gif',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnUpdateAlpha.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnUpdateAlpha.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnUpdateAlpha.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'UpdateAlfa',
				opCode: TolomeoExt.ToloAPIOpCodes.btnUpdateAlfa
			});
		}

		// Add
		if (this.withAdd) {
			this.addButton({
				icon: this.iconBasePath + 'poligono.gif',
				enableToggle: true,
				toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnAdd.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnAdd.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnAdd.tooltip.title")
				},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'Add',
				opCode: TolomeoExt.ToloAPIOpCodes.btnAdd
			});
		}

		// Subtract
		if (this.withSubtract) {
			this.addButton({
				icon: this.iconBasePath + 'poligonodelete.gif',
				enableToggle: true,
				toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnSubtract.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnSubtract.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnSubtract.tooltip.title")
				},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'Subtract',
				opCode: TolomeoExt.ToloAPIOpCodes.btnSubtract
			});	
		}

		// AddSub
		if (this.withAddSub) {
			this.addButton({
				icon: this.iconBasePath + 'poligonopiumeno.gif',
				enableToggle: true,
				toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnAddSub.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnAddSub.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnAddSub.tooltip.title")
				},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'AddSub',
				opCode: TolomeoExt.ToloAPIOpCodes.btnAddSub
			});	
		}

		// VertexEdit
		if (this.withVertexEdit) {
			this.addButton({
				icon: this.iconBasePath + 'poligonovertici.gif',
				enableToggle: true,
				toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnVertexEdit.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnVertexEdit.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnVertexEdit.tooltip.title")
				},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'VertexEdit',
				opCode: TolomeoExt.ToloAPIOpCodes.btnVertexEdit
			});	
		}

		// DragDrop
		if (this.withDragDrop) {
			this.addButton({
				icon: this.iconBasePath + 'hand.gif',
				enableToggle: true,
				toggleGroup: this.encodeToggleGroup(2),
				allowDepress: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnDragDrop.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnDragDrop.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnDragDrop.tooltip.title")
				},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'DragDrop',
				opCode: TolomeoExt.ToloAPIOpCodes.btnDragDrop
			});	
		}

		// Delete
		if (this.withDelete) {
			this.addButton({
				icon: this.iconBasePath + 'elimina.gif',
				enableToggle: false,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnDelete.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnDelete.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnDelete.tooltip.title")
				},
				listeners: listenersClick,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'Delete',
				opCode: TolomeoExt.ToloAPIOpCodes.btnDelete
			});	
		}
		
		var isSnappingRequired = false; 
		// Controllo se c'è qualche snapping impostato per l'applicazione
		for(var evtLi = 0; evtLi < this.paramsJS.azioniEventi.eventiLayerList.length; evtLi++){			
			var evtL = this.paramsJS.azioniEventi.eventiLayerList[evtLi];			
			if(evtL.snapping && evtL.snapping.snappingLayerList && evtL.snapping.snappingLayerList.length > 0){
				isSnappingRequired = true;
				break;
			}
		}
		
		// Snap
		if(isSnappingRequired && this.withSnap) {
			this.addButton({
				icon: this.iconBasePath + 'grid-snap-dot.png',
				enableToggle: true,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnSnap.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnSnap.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnSnap.tooltip.title")
				},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'Snap',
				opCode: TolomeoExt.ToloAPIOpCodes.btnSnap
			});		
		}
		
		// Csw
		if(layout.csw && this.withCsw) {
			this.addButton({
				icon: this.iconBasePath + 'csw.png',
				enableToggle: true,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnCsw.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnCsw.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnCsw.tooltip.title")
				},
				listeners: listenersToggle,
				handlerBaseName: 'Csw',
				opCode: TolomeoExt.ToloAPIOpCodes.btnCsw
			});		
		}
		
		// WMS
		if(layout.WMSExplorer && this.withWMSExporer) {
			this.addButton({
				icon: this.iconBasePath + 'wms.png',
				enableToggle: true,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnWMSExplorer.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnWMSExplorer.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnWMSExplorer.tooltip.title")
				},
				listeners: listenersToggle,
				handlerBaseName: 'WMS',
				opCode: TolomeoExt.ToloAPIOpCodes.btnWMSExplorer
			});		
		}

		// 3D
		if(layout.visualizzazione3D && this.with3D && Modernizr.webgl) {
			this.addButton({
				icon: this.iconBasePath + '3d_glasses.png',
				enableToggle: true,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btn3D.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btn3D.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btn3D.tooltip.title")
				},
				listeners: listenersToggle,
				handlerBaseName: '3D',
				opCode: TolomeoExt.ToloAPIOpCodes.btn3D
			});		
		}

		// Filtro temporale
		if (layout.conFiltroTemporale && this.withTemporalFilter) {
			this.addButton({
		    	icon: this.iconBasePath + 'filtro.gif',
		    	//enableToggle: true,
		    	//toggleGroup: this.encodeToggleGroup(2),
				allowDepress: true,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnFiltroTemporale.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnFiltroTemporale.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnFiltroTemporale.tooltip.title")
				},
		    	listeners: listenersClick,
		    	// Proprietà aggiuntive per Tolomeo
		    	handlerBaseName: 'TemporalFilter',
		    	opCode: TolomeoExt.ToloAPIOpCodes.btnTemporalFilter
		    });
		}
		
		// AutoIdentify
		if (this.withAutoIdentify && this.autoIdentifyLayersPresent()) {
			this.addButton({
				icon: this.iconBasePath +  'identifica.gif',
				enableToggle: true,
				allowDepress: true,
				overflowText: ToloI18n.getMsg("ToloButtonPanelExt.btnAutoIdentify.overflowText"),
				tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnAutoIdentify.tooltip.text"), 
					title: ToloI18n.getMsg("ToloButtonPanelExt.btnAutoIdentify.tooltip.title")
				},
				listeners: listenersToggle,
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'AutoIdentify',
				opCode: TolomeoExt.ToloAPIOpCodes.btnAutoIdentify
			});
		}

		// Custom buttons
		for (var i=0; i<layout.customButtonList.length; i++) {
			var cb = layout.customButtonList[i];
			var btNo = TolomeoExt.ToloAPIOpCodes.btnCustomBase+i;

			this.addButton({
				icon: this.iconBasePath + cb.iconFileName,
				enableToggle: (cb.gruppo!=null && cb.gruppo!=-1 ) ? true : false,
				listeners:    (cb.gruppo!=null && cb.gruppo!=-1 ) ? listenersToggle : listenersClick,
				toggleGroup:  (cb.gruppo!=null && cb.gruppo!=-1 ) ? this.encodeToggleGroup(cb.gruppo) : null,
				overflowText: cb.iconTitleMessage,
				//allowDepress: true,
				tooltip: { text: cb.tooltipMessage, title:cb.iconTitleMessage},
				// Proprietà aggiuntive per Tolomeo
				handlerBaseName: 'CustomButton',
				opCode: btNo,
				idCustomButton: cb.idCustomButton,
				pressFunction: cb.pressFunction,
				releaseFunction: cb.releaseFunction
			});

			//bottoni.add(btNo, 'customButton' + btNo, cb.iconFileName, cb.gruppo, pf, rf, cb.disabledMessage, cb.iconTitleMessage, cb.iconAltMessage, cb.idCustomButton);		
		}

		/*// Custom buttons
		for (var i=0; i<paramsJS.layOut.customButtonList.length; i++) {
			var cb = paramsJS.layOut.customButtonList[i];
			var btNo = btnCustomBase+i;
			var pf = ((cb.pressFunction == null)||(cb.pressFunction == ''))  ? btnCustomButtonDefaultPressFn : cb.pressFunction;
			var rf = ((cb.releaseFunction == null)||(cb.releaseFunction == ''))  ? btnCustomButtonDefaultReleaseFn : cb.releaseFunction;

			 //<li><a href="JavaScript:bottoni.press(btnIdentify);" title="Ottieni informazioni su un oggetto selezionato"><img src="${applx.urlCss}/img/icone_on/info.gif" alt="Informazioni" id="identify" /></a></li>		

			bottoni.add(btNo, 'customButton' + btNo, cb.iconFileName, cb.gruppo, pf, rf, cb.disabledMessage, cb.iconTitleMessage, cb.iconAltMessage, cb.idCustomButton);		
		}*/
								
		this.filler = new Ext.toolbar.Fill();		
		this.add(this.filler);
		this.fillerAdded = true; 		
        
		this.add('-');		
		
		this.add({
			icon: this.iconBasePath + 'permalink.png',
			id: this.getId() + 'permalinkBtn',
			disabled : true,
			tooltip: {text: ToloI18n.getMsg("ToloButtonPanelExt.btnPermalink.tooltip.text"), 
				title: ToloI18n.getMsg("ToloButtonPanelExt.btnPermalink.tooltip.title")
			},
			listeners: { 								
				click: {
					fn: function() {     	    								
						this.fireEvent('showPermalinkClicked');																												
					},
					scope: this
	    		}
	    		
			}			
		});		
		
		this.add('-');	
		
		if (layout.conExportQGIS || layout.withVisWith) {
			var strumentiItems = [];
			
			
			
			if (layout.withVisWith &&
				(layout.withVisWith.withOSM ||
				 layout.withVisWith.withGoogle ||
				 layout.withVisWith.withBing ||
				 layout.withVisWith.withHere)) { 
				
				var mnuVisualizzaCon = Ext.create('Ext.menu.Item', {
	    			iconCls : 'iconVisCon',
	    			text: ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.text"),
					menu : {
						cls: 'clearCSS',
						items : []
					}
				});
				strumentiItems.push(mnuVisualizzaCon);
				
				if (layout.withVisWith.withOSM) {
					var mnuOSM = Ext.create('Ext.menu.Item', {
						text : ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.OSM.text"),
						iconCls : 'iconVisConOSM',
						listeners: { 								
							click: {
								fn: function() {     	    								
									this.fireEvent('showWithOSMClicked',null,null);																												
								},
								scope: this
				    		}}
					});
					mnuVisualizzaCon.menu.add(mnuOSM);
				}
				
				if (layout.withVisWith.withGoogle) {
					var mnuGoogle = Ext.create('Ext.menu.Item', {
						text : ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.Google.text"),
		    			iconCls : 'iconVisConGoogle',
						menu : {
							cls: 'clearCSS',
							items: [{
								text: ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.GoogleSat.text"),
								cls : "clearCss",
								listeners: { 								
									click: {
										fn: function() {     	    								
											this.fireEvent('showWithGoogleSatelliteClicked',null,null);																												
										},
										scope: this
						    		}
								}},{
									text: ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.GoogleMappa.text"),
									cls : "clearCss",
									listeners: { 								
										click: {
											fn: function() {     	    								
												this.fireEvent('showWithGoogleMapClicked',null,null);																												
											},
											scope: this
							    		}
									}},{
									text:  ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.GoogleSatClassica.text"),
									cls : "clearCss",
									listeners: { 								
										click: {
											fn: function() {     	    								
												this.fireEvent('showWithGoogleSatelliteClassicClicked',null,null);																												
											},
											scope: this
							    		}
									}},{
									text: ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.GoogleMappaClassica.text"),
									cls : "clearCss",
									listeners: { 								
										click: {
											fn: function() {     	    								
												this.fireEvent('showWithGoogleMapClassicClicked',null,null);																												
											},
											scope: this
							    		}
									}}]
						}
					});
					mnuVisualizzaCon.menu.add(mnuGoogle);
				}
				
				if (layout.withVisWith.withHere) {
					var mnuHere = Ext.create('Ext.menu.Item', {
						text : 'Here',
		    			iconCls : 'iconVisConHere',
						menu : {
							cls: 'clearCSS',
							items: [{
								text: ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.HereSat.text"),
								cls : "clearCss",
								listeners: { 								
									click: {
										fn: function() {     	    								
											this.fireEvent('showWithHereSatelliteClicked',null,null);																												
										},
										scope: this
						    		}
								}},{
								text: ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.HereMappa.text"),
								cls : "clearCss",
								listeners: { 								
									click: {
										fn: function() {     	    								
											this.fireEvent('showWithHereMapClicked',null,null);																												
										},
										scope: this
						    		}
								}}]
						}
					});
					mnuVisualizzaCon.menu.add(mnuHere);
				}
				
				if (layout.withVisWith.withBing) {
					var mnuBing = Ext.create('Ext.menu.Item', {
						text : 'Bing',
		    			iconCls : 'iconVisConBing',
						menu : {
							cls: 'clearCSS',
							items: [{
								text: ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.BingSatOb.text"),
								cls : "clearCss",
								listeners: { 								
									click: {
										fn: function() {     	    								
											this.fireEvent('showWithBingObClicked',null,null);																												
										},
										scope: this
						    		}
								}},{
								text: ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.BingSat.text"),
								cls : "clearCss",
								listeners: { 								
									click: {
										fn: function() {     	    								
											this.fireEvent('showWithBingSatelliteClicked',null,null);																												
										},
										scope: this
						    		}
								}},{
								text: ToloI18n.getMsg("ToloButtonPanelExt.mnuVisWith.BingMappa.text"),
								cls : "clearCss",
								listeners: { 								
									click: {
										fn: function() {     	    								
											this.fireEvent('showWithBingMapClicked',null,null);																												
										},
										scope: this
						    		}
								}}]
						}
					});
					mnuVisualizzaCon.menu.add(mnuBing);
				}
				
			}
			
			if (layout.conExportQGIS) {
				var mnuEsporta = Ext.create('Ext.menu.Item', {
					text : ToloI18n.getMsg("ToloButtonPanelExt.mnuEsporta.text"),
					menu : {
						cls: 'clearCSS',
						items : [{
								xtype: 'menuitem',
								text: ToloI18n.getMsg("ToloButtonPanelExt.mnuEsporta.qgis.text"),
								iconCls : 'iconExportForQGis',
								id: this.getId() + 'exportQgisBtn',
								disabled : true,
								menu:{
									cls: 'clearCSS',
									items: [
										{
											text: ToloI18n.getMsg("ToloButtonPanelExt.mnuEsporta.qgis180.text"),
					    	    			iconCls : 'iconExportForQGis',
											cls : "clearCss",
											listeners: { 								
												click: {
													fn: function() {     	    								
														this.fireEvent('exportForQgisClicked',null,null, "1.8.0");																												
													},
													scope: this
									    		}
									    		
											}	
										},{
											text: ToloI18n.getMsg("ToloButtonPanelExt.mnuEsporta.qgis2180.text"),
					    	    			iconCls : 'iconExportForQGis',
											cls : "clearCss",
											listeners: { 								
												click: {
													fn: function() {     	    								
														this.fireEvent('exportForQgisClicked',null,null, "2.18.0");																												
													},
													scope: this
									    		}
									    		
											}	
										}
									
									]
								}
							}]
					}
				});
				strumentiItems.push(mnuEsporta);
			}			
			
			this.addButton({
				//text: 'Strumenti',
				icon: this.iconBasePath + 'options.png',
				hidden : true,
				id: this.getId() + 'toolsMenu',
				tooltip : ToloI18n.getMsg("ToloButtonPanelExt.btnStrumenti.tooltip"),
				//scale: 'medium',
				menu : {		
					xtype: 'menu',
					cls: 'clearCSS',
					items: strumentiItems
				}
			});
		}
		
		var troubleItems = [];
		troubleItems.push ({
        	text: ToloI18n.getMsg("ToloButtonPanelExt.mnuTrouble.rigenera.text"),
        	iconCls : 'iconRegenerateWithoutCache',
			cls : "clearCss",
        	tooltip: ToloI18n.getMsg("ToloButtonPanelExt.mnuTrouble.rigenera.tooltip"),
        	listeners : {
        		click : {
        			fn : function(){
        				this.fireEvent('regeneratePageClicked');
        			},
        			scope: this
        		}
        	}     
        });
        
        
        
        if(layout.helpMailTo){        	
        	troubleItems.push ({
            	text: ToloI18n.getMsg("ToloButtonPanelExt.mnuTrouble.mailto.text"),
            	iconCls : 'iconMailToAdmin',
				cls : "clearCss",
				//'Invia una e-mail a ' + layout.helpMailTo,
            	tooltip:  ToloI18n.getMsg("ToloButtonPanelExt.mnuTrouble.mailto.tooltip", {MAIL: layout.helpMailTo}),
            	listeners : {
            		click : {
            			fn : function(){
            				this.fireEvent('mailToAdministratorClicked',layout.helpMailTo,layout.helpMailSubject);
            			},
            			scope: this
            		}
            	}    
            });
        }
        
        var helpItems = [];
        if(layout.helpUrl){
        	
        	helpItems.push({
                	text : ToloI18n.getMsg("ToloButtonPanelExt.mnuGuida.text"),              
                	listeners : {
                		click : {
                			fn : function(){
                				this.fireEvent('showGuideClicked',layout.helpUrl);
                			},
                			scope: this
                		}
                	}    
                });
                
            helpItems.push('-');
                            
        }
        
        if(layout.faqUrl){
        	helpItems.push({
                	text : ToloI18n.getMsg("ToloButtonPanelExt.mnuFAQ.text"),
                	listeners : {
                		click : {
                			fn : function(){
                				this.fireEvent('showFaqClicked',layout.faqUrl);
                			},
                			scope: this
                		}
                	}    
                });
                
            helpItems.push('-');
        }
        
        helpItems.push({
                	text: ToloI18n.getMsg("ToloButtonPanelExt.mnuProblemi.text"),
                	menu: {		   
                		cls: 'clearCSS',
		                items: troubleItems
               		}
                });
                
        helpItems.push('-');
        helpItems.push({
                	text: ToloI18n.getMsg("ToloButtonPanelExt.mnuInfoTolomeo.text"),
                	listeners : {
                		click : {
                			fn : function(){
                				this.fireEvent('showTolomeoInfoClicked');	
                			},
                			scope: this
                		}
                	}                	
                });
                
        if(layout.helpInfo){
        	helpItems.push({
                	text: layout.helpInfo.mainTitle,
                	listeners : {
                		click : {
                			fn : function(){
                				this.fireEvent('showCustomInfoClicked',layout.helpInfo);	
                			},
                			scope: this
                		}
                	}                	
                });
        }
                
		
		this.add({
			tooltip : ToloI18n.getMsg("ToloButtonPanelExt.mnuAiutoEInfo.text"),
			icon: this.iconBasePath + 'help.png',           
            menu: {
            	cls: 'clearCSS',
                xtype: 'menu',
                plain: true,
                items: helpItems
            }
        });

		this.add({
			//tooltip : 'aaaa',
			//icon: this.iconBasePath + 'help.png', 
			//xtype: 'splitbutton',
			icon: this.getLanguageIcon(ToloI18n.getLanguage()),
            menu: {
            	cls: 'clearCSS',
                xtype: 'menu',
                plain: true,
                items: [
                	{
                		xtype: 'menucheckitem',
                		checked: ToloI18n.getLanguage()=='it',
                		icon: this.getLanguageIcon('it'),
                		text: 'Italiano',
                		group: 'lang',
                		enableToggle: true,
                		listeners: { checkchange: {fn: function(button, checked) { 
                									if (checked) button.up('button').setIcon(button.icon);} , scope: thisToolbar},
                					 click: {fn: function() { this.languageChange('it');}, scope: this}
                					}
                	},
                	{
                		checked: ToloI18n.getLanguage()=='en',
                		icon: this.getLanguageIcon('en'),
						group: 'lang',
						enableToggle: true,
                		iconCls : 'iconLangEn',
                		text: 'English',
                		listeners: { checkchange: {fn: function(button, checked) { 
                			if (checked) button.up('button').setIcon(button.icon);} , scope: thisToolbar},
                			click: {fn: function() { this.languageChange('en');}, scope: this}}
                	}
                ]
            }
        });

		
        
		this.doLayout();													

		
		/**
		 * Finestra filtro temporale
		 */
		this.temporalFilterWindow = Ext.create('Ext.Window', {
				title: ToloI18n.getMsg("ToloButtonPanelExt.winFiltroTemp.title"),
				closeAction: 'hide',
				constrain: true,
				width: 300,
				autoHeight: true,
				items: new Ext.FormPanel({
					frame: true,
					border: false,
					autoHeight: true,
					bodyStyle: 'padding-top:5px;',
					defaults: {
						labelWidth: 50,
						anchor: '98%'
					},
					items: [{
						id: this.getId() + 'dtInizio',
						xtype: 'datefield',
						fieldLabel: ToloI18n.getMsg("ToloButtonPanelExt.winFiltroTemp.fldDtInizio"),
						format: 'd/m/Y'
					},{
						id: this.getId() + 'dtFine',
						xtype: 'datefield',
						fieldLabel: ToloI18n.getMsg("ToloButtonPanelExt.winFiltroTemp.fldDtFine"),
						format: 'd/m/Y'
					}],
					buttons: [{
						text: ToloI18n.getMsg("ToloButtonPanelExt.winFiltroTemp.btnReset"),
						handler: function(b,e){
							var dtInizio = Ext.getCmp(this.getId() + 'dtInizio').setValue();
							var dtFine = Ext.getCmp(this.getId() + 'dtFine').setValue();
							this.fireEvent("onTemporalFilterApply", null, null);
						}, scope: this
					},{
						text: ToloI18n.getMsg("ToloButtonPanelExt.winFiltroTemp.btnApplica"),
						handler: function(b,e){
							var dtInizio = Ext.getCmp(this.getId() + 'dtInizio').getValue();
							var dtFine = Ext.getCmp(this.getId() + 'dtFine').getValue();
							this.fireEvent("onTemporalFilterApply", dtInizio, dtFine);
						}, scope: this
					},{
						text: ToloI18n.getMsg("ToloButtonPanelExt.winFiltroTemp.btnChiudi"),
						handler: function(){
							this.temporalFilterWindow.hide();
						}, scope: this
					}]
				})
			});

	},
	
	getLanguageIcon: function(lng){
		
		switch (lng){
			case 'it':
		  		return this.iconBasePath + 'Flags/ITA.png';
				break;
			case 'en':
				return this.iconBasePath + 'Flags/ENG.png';
				break;
		}
		
	},
	
	languageChange: function(lng){
		this.fireEvent('languageChange', lng);
	}, 
	
	checkFiller : function(item){
		// Non more filler are allowed
		 if(item == null)  return false;
		 
		 if ( 	(typeof item == 'string' && item == '->') || 
		 		(item.xtype && item.xtype == 'tbfill') ||
		 		(item.getXType && item.getXType() == 'tbfill')
		 	) 
		 {
		 	return true;
		 }
	},
		
	add : function(item){		
		 if(this.fillerAdded && this.checkFiller(item)) return;			 		 
		 this.callParent(arguments);
	},
	
	/**
	 * Method: addLeft
	 * Aggiunge l'item nell'ultima posizione del gruppo allineato a sinistra
	 * 
	 * Parameters:
	 * item - item da aggiungere alla toolar. 
	 */
	addLeft : function(item){
		if(this.checkFiller(item)) return;
		var fillerIndex = this.items.indexOf(this.filler);					
		this.insert(fillerIndex,item);
	},
		
	/**
	 * Method: addRight
	 * Aggiunge l'item nella prima posizione del gruppo allineato a destra
	 * 
	 * Parameters:
	 * item - item da aggiungere alla toolar. 
	 */
	addRight : function(item){			
		if(this.checkFiller(item)) return;
		var fillerIndex = this.items.indexOf(this.filler);					
		this.insert(fillerIndex+1,item);
	},
	
	/**
	 * Method: addFirst
	 * Aggiunge l'item in prima posizione
	 * 
	 * Parameters:
	 * item - item da aggiungere alla toolar. 
	 */
	addFirst : function(item){
		if(this.checkFiller(item)) return;
		this.insert(0,item);
	},
	
	/**
	 * Method: addLast
	 * Aggiunge l'item in ultima posizione
	 * 
	 * Parameters:
	 * item - item da aggiungere alla toolar. 
	 */
	addLast : function(item){
		this.add(item);
	},

	/**
	 * Method: encodeToggleGroup
	 * 
	 * Parameters:
	 * group - il gruppo da codificare. 
	 * 
	 * Returns:
	 * Il gruppo codificato.
	 */
	encodeToggleGroup: function(group) {
		return this.groupPrefix + group;
	},

	/**
	 * Method: decodeToggleGroup
	 * 
	 * Parameters:
	 * encodedGroup - il gruppo da decodificare.
	 * 
	 * Returns:
	 * Il gruppo codificato. 
	 */
	decodeToggleGroup: function(encodedGroup) {
		return encodedGroup.substr(this.groupPrefix.length);
	},		

	/**
	 * Method: addButton
	 * Add a single button. 
	 * 
	 * Parameters:
	 * btn - il pulsante da aggiungere.
	 * 
	 * Returns:
	 * Il componente aggiunto.
	 */
	addButton: function(btn) {
		//var cmp = Ext.ComponentMgr.create(btn, 'button');
		if (this.defaults && this.defaults.scale) Ext.applyIf(btn, {scale: this.defaults.scale} );
		var cmp = Ext.create('Ext.button.Button',btn);
		
		this.buttons.push(cmp);
		this.add(cmp);

		return cmp;
	},

	/**
	 * Method: addCombo
	 * 
	 * Parameters:
	 * btn - combo da aggiungere.
	 * 
	 * Returns:
	 * Il componente aggiunto.
	 */
	addCombo: function(cmb) {
		//var cmp = Ext.ComponentMgr.create(btn, 'button');
		//if (this.defaults && this.defaults.scale) Ext.applyIf(btn, {scale: this.defaults.scale} );		
		this.buttons.push(cmb);
		this.add(cmb);

		return cmb;
	},	
	
	/**
	 * Method: cmbSelectLayerHandler
	 * 
	 * Parameters:
	 * combo - la combo box.
	 * record - il record selezionato.
	 * index - l'indice del record.
	 * 
	 * Returns:
	 * {Boolean}
	 */
	cmbSelectLayerHandler: function (combo, record, index) {
		var eventName = 'onSelectLayer';
		this.fireEvent(eventName, record[0].data['codTPN']);
		return true
	},

	/**
	 * Method: btnToggleHandler
	 * 
	 * Parameters:
	 * button - il pulsante.
	 * state - lo stato del pulsante.
	 * 
	 * Returns:
	 * {Boolean}
	 */
	btnToggleHandler: function (button, state) {
		var retVal=true;
		if (button.handlerBaseName !=null) {
			var eventName = 'on' + button.handlerBaseName + ((state == true) ? 'PressFn' : 'ReleaseFn');
			
			if(state && button.becomeDefault){
				for (var i=0; i<this.buttons.length; i++) {
					if (this.buttons[i].toggleGroup==button.toggleGroup) {
						this.buttons[i].groupDefault = false;
					}
				}
				button.groupDefault = true;
			}
			
			this.fireEvent(eventName, button);			

			/*
			// Necessario per fare in modo che annullaselezioni non rimanga premuto, pur essendo parte di un gruppo di toggle 
			if (button.opCode ==  TolomeoExt.ToloAPIOpCodes.btnAnnullaSelezioni)  {
					this.pressDefault(this.decodeToggleGroup(button.toggleGroup));
					state=false;
					retVal=false;
			}
			 */
		}
		return retVal;
	},

	/**
	 * Method: btnClickHandler
	 * 
	 * Parameters:
	 * button - il pulsante.
	 * e - l'evento.
	 * 
	 * Returns:
	 * {Boolean}
	 */
	btnClickHandler: function (button, e) {
		if (button.handlerBaseName !=null) {
			var eventName = 'on' + button.handlerBaseName + 'PressFn';
			this.fireEvent(eventName, button);
		}

		// Necessario per premere bottone default quando premuto annulla selezioni 
		if (button.opCode ==  TolomeoExt.ToloAPIOpCodes.btnAnnullaSelezioni)  {
			this.pressDefault(this.decodeToggleGroup(button.toggleGroup));
		}

		// Necessario per premere bottone default quando premuto annulla selezioni 
		if (button.opCode ==  TolomeoExt.ToloAPIOpCodes.btnTemporalFilter)  {
			this.temporalFilterWindow.show();
		}
		
		return true;
	},

	/**
	 * Method: btnMeasureMng
	 * 
	 * Parameters:
	 * button - il pulsante.
	 * state - l'evento.
	 */
	btnMeasureMng: function(button, state) {

		var itm = null;

		// Determina quale voce di menù è selezionata
		for (var i= 0; i<button.menu.items.items.length; i++) {
			if (button.menu.items.items[i].checked) itm=button.menu.items.items[i]; 
		}

		if (itm!=null) {
			var eventName = 'onMeasure' + ((state == true) ? 'Activate' : 'Deactivate');
			this.fireEvent(eventName, itm.measureType);
		}

		// tolto perchè va in conflitto con altre funzionalità
		//if (!state) this.pressDefault(this.decodeToggleGroup(button.toggleGroup));

	},

	/**
	 * Method: btnMeasureItemMng
	 * 
	 * Parameters:
	 * button - il pulsante.
	 * checked - indica se il pulsante è premuto o meno.
	 */
	btnMeasureItemMng: function(button, checked) {

		if (checked) {
			switch (button.measureType) {
			case 0:
				this.btnMeasure.setIcon(this.iconBasePath + 'misurapoligono.gif');
				break;
			case 1:
				this.btnMeasure.setIcon(this.iconBasePath + 'misuracerchio.gif');
				break;
			case 2:
				this.btnMeasure.setIcon(this.iconBasePath + 'misuralinea.gif');
				break;
			}	
		}

		if ((checked) && (this.btnMeasure.pressed)) {	
			var eventName = 'onMeasureTypeChange';
			this.fireEvent(eventName, button.measureType);
		}

		if (checked) this.btnMeasure.toggle(true);

	},
	
	
	btnNewToggleHandler: function(button, state) {
		this.btnToggleHandler(button,state);
		if (!state) this.pressDefault(this.decodeToggleGroup(button.toggleGroup));
	},
	
	/**
	 * Method: btnNewItemMng
	 * 
	 * Parameters:
	 * button - il pulsante.
	 * checked - indica se il pulsante è premuto o meno.
	 */
	btnNewItemMng: function(button, checked) {

		if (checked) {											
			if(this.btnNew.pressed) {
				/*
				var eventName = 'onNewTypeChange';
				this.fireEvent(eventName, button);				
				this.btnNew.toggle();
				this.pressDefault(this.decodeToggleGroup(button.toggleGroup));
				*/
				this.fireEvent('onNuovoReleaseFn', this.btnNew);
				this.fireEvent('onNuovoPressFn', button);
				
			}
			this.btnNew.setIcon(button.icon);							
			this.btnNew.newType = button.newType;				
			//this.btnNew.toggle(true);
		}
	},

	/**
	 * Method: pressDefault
	 * 
	 * Parameters:
	 * group - il gruppo.
	 */
	pressDefault: function(group) {
		for (var i=0; i<this.buttons.length; i++) {
			if ((this.buttons[i].toggleGroup) && (this.buttons[i].toggleGroup==this.encodeToggleGroup(group)) && (this.buttons[i].groupDefault) && (this.buttons[i].groupDefault==true) ) {
				this.buttons[i].toggle(true);
			}
		}

		// bottoni.press(bottoni.getDefaultName(group));
		//alert("PressDefault del gruppo "+ group);
	},

	/**
	 * Method: operationEnable
	 * 
	 * Parameters:
	 * opCode - codice operazione.
	 * enabled - indica se l'operazione è abilitata.
	 */
	operationEnable: function(opCode, enabled) {
		var thisPanel = this;
		this.items.each(
				function (item, index, length) {
					if (item.opCode == opCode){
						(thisPanel.paramsJS.layOut.nascondiBottoniInattivi) ? item.setVisible(enabled) : item.setDisabled(!enabled);
						if(opCode == TolomeoExt.ToloAPIOpCodes.btnSnap){							
							if(!enabled && item.pressed){
								item.toggle();
							} else if (enabled && item.pressed){
								item.toggle();
								item.toggle();
							}
						}
					}
				}	
		);
	},

	/**
	 * Method: bindToAPI
	 * 
	 * Parameters:
	 * api - API.
	 */
	bindToAPI: function(api) {
		this.api=api;
		// Eventi API ai quali viene registrato buttonsPanel
		api.on('onOperationEnable', function (opCode) {this.operationEnable(opCode, true);}, this );
		api.on('onOperationDisable', function (opCode) {this.operationEnable(opCode, false);}, this );
		api.on('onOperationPressDefault', function (group) {this.pressDefault(group);}, this );
		api.on('selectedObjectsRemoved', 
			function (removedGeoms,geoms) {
				var geom = geoms.geometries?geoms.geometries[0]:geoms;
				if (this.withLayerList && this.cmbLayerSel.getValue() != geom.codTPN && this.paramsJS.isSelectable(geom.codTPN)){					
					this.cmbLayerSel.setValue(geom.codTPN);
					//this.fireEvent('onSelectLayer', geom.codTPN);
				}
			}, 
			this );
		api.on('onObjectSelect', function(geoms){
			var geom = geoms.geometries?geoms.geometries[0]:geoms;
			var codTpn = geom.codTPN;
			if (this.withLayerList && this.cmbLayerSel.getValue() != codTpn){					
				this.cmbLayerSel.setValue(codTpn);
			}
		},this);
		
		if (this.withLayerList) api.setCurrentSelectLayer(this.cmbLayerSel.getValue());
		
		if (this.withNavHistory) {
			this.api.on('previousNavigationStateAvailable',		function(){Ext.getCmp(this.getId() + 'HistoryPrevBtn').setDisabled(false)}, this);
			this.api.on('previousNavigationStateNotAvailable',	function(){Ext.getCmp(this.getId() + 'HistoryPrevBtn').setDisabled(true)}, this);
			this.api.on('nextNavigationStateAvailable',			function(){Ext.getCmp(this.getId() + 'HistoryNextBtn').setDisabled(false)}, this);
			this.api.on('nextNavigationStateNotAvailable',		function(){Ext.getCmp(this.getId() + 'HistoryNextBtn').setDisabled(true)}, this);	
		}
		
	},

	//TODO è anche in API, riunificare in quelche modo
	/**
	 * Method: autoIdentifyLayersPresent
	 * 
	 * Returns:
	 * {Boolean}
	 */
	autoIdentifyLayersPresent: function (){

		for(var index=0; index<this.paramsJS.azioniEventi.eventiLayerList.length; index++) {
			var paramJSLayer = this.paramsJS.azioniEventi.eventiLayerList[index];
			if (paramJSLayer.autoIdentifyAllowed==true)  {
				return true;
			}
		}
		return false;
	}, 
	
	/**
	 * Imposta un stato o cambia lo stato di un bottone identificato tramite il suo opCode
	 * 
	 * @param opCode: codice del bottone (vedi toloAPIOpCodes.js)
	 * @param state: lo stato da impostare (opzionale). Se	non impostato o null cambia lo stato corrente
	 * @param supress: stoppa il fire degli eventi innescati dal cambio di stato
	 */
	buttonToggle: function(opCode, state, supress) {
		
		for (var i=0; i<this.buttons.length; i++) {
			if (this.buttons[i].opCode==opCode) {
				this.buttons[i].toggle(state, supress);
			}
		}
		
	},
	
	a: function(data) {
	//	this.cmbLayerSel.lastQuery=null;
		if (this.cmbLayerSel) {
			this.cmbLayerSel.getStore().loadData(data, false);
			this.cmbLayerSel.getStore().filterBy(this.withSearchLayerFilter);
		}
		
	},
	
	/**
	 * Enable/Disable the button having that specific id 
	 * 
	 * @param id: id of the button
	 * @param on: boolean. True to enable button, false to disable
	 */
	enableButton : function(id,on){
		var comp = Ext.getCmp(id);
		if(comp) comp.setDisabled(!on);
	},
			
	/**
	 * Enable(Show)/Disable(Hide) buttons relate to toc 
	 * 
	 * @param tocExist: flag true/false 
	 * @param on: boolean. True to enable button, false to disable
	 */
	switchTocRelatedButtons : function(tocExist,on){
		this.enableButton(this.getId() + 'permalinkBtn',on);
		var comp = Ext.getCmp(this.getId() + 'toolsMenu');
		if(comp && tocExist){
			//var comp = Ext.getCmp(this.getId() + 'toolsMenu');
			if(comp) comp.setVisible(on);			
			this.enableButton(this.getId() + 'exportQgisBtn',on);
		}
	}
		
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITA' o
 IDONEITA' PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.define('TolomeoExt.ToloInsFromLayerPanel', {

	extend: 'Ext.FormPanel',
	
	/**
	 * @param jsGeometryList {JSGeometryArray}
	 */
	jsGeometryList: null,
	
	initComponent: function(){
		
		this.addEvents("geompartitemmouseenter");
		this.addEvents("geompartitemmouseleave");
		this.addEvents("exitOK");
		this.addEvents("exitCancel");
		
		thisFormPanel = this;
	    
		this.layout = { type: 'vbox', align: 'stretch'};
	    this.minButtonWidth = 50;
	    
		//this.layout = 'vbox';
	
		var objRadioItems = [];
		for (var i=0; i<this.jsGeometryList.size(); i++) {
			var o = this.jsGeometryList.getByIndex(i);
			objRadioItems.push(Ext.create('Ext.form.field.Checkbox',{boxLabel: o.description, name: 'jsGeomToInsert', inputValue: o.toString(), checked: (i==0)}));
		}
		
		var objRadioGroup = {
            xtype: 'radiogroup',
            //fieldLabel: 'Auto Layout',
            //cls: 'x-check-group-alt',
            
            items: objRadioItems
        }
		
		var objRadioFieldset = {	
            xtype: 'fieldset',
            
            title: ToloI18n.getMsg("ToloInsFromLayerPanel.objRadioFieldset.title"),
            layout: 'anchor',
            /*defaults: {
                anchor: '100%',
                hideEmptyLabel: false
            },*/
            items: [objRadioGroup]
		};
		
		// Cerco le parti del primo oggetto
		var o1 = this.jsGeometryList.getByIndex(0);
		var o1Parts = o1.getParts();
		//var partsCheckItems = [];
		var storeData = [];
		
		for (var i=0; i<o1Parts.size(); i++){
			var p = o1Parts.getByIndex(i);
			var nomeParte = ToloI18n.getMsg("ToloInsFromLayerPanel.objRadioFieldset.title", {NUMPARTE: i});
			storeData.push([true, nomeParte, p]);
		}
		
		
		var store = Ext.create('Ext.data.ArrayStore', {
		    // store configs
		    //storeId: 'myStore',
		    // reader configs
			autoSync: true,
		    fields: [
		       'active',
		       'description',
		       'val'
		    ],
		    data: storeData
		});
		
		
    	var listView = Ext.create('Ext.grid.Panel',{
    		store: store,
    		flex: 1,
    		//autoWidth: true,
    		//emptyText: 'Descrizione non recuperata',
    		viewConfig: {
    			//firstCls: null,
    			trackOver: true
    		},
    		
    		//reserveScrollOffset: true,
    		//multiSelect: false,
    		listeners: {
    			'itemmouseenter': { fn: this.itemmouseenter, scope: this},
    			'itemmouseleave': { fn: this.itemmouseleave, scope: this}//,
    			//'itemclick':      { fn: this.onQuerySingleResultByIndex, scope: this}
    		},
    		columns: [
    		          { xtype : 'checkcolumn',
    			//header: 'active',
    			width: 30,
    			//flex: 1,
    			dataIndex: 'active'
    		},{
    			header: ToloI18n.getMsg("ToloInsFromLayerPanel.listaParti.header.descrizione"),
    			//width: 1,
    			flex: 1,
    			dataIndex: 'description'
    		}]
    	});
		/*
		this.items= [ objRadioFieldset , 
		              listView,{
	        xtype: 'hiddenfield',
	        name: 'geoms'
	     
	    }];*/
		this.items= [objRadioFieldset, listView];
		
		this.buttons= [{
			text: ToloI18n.getMsg("ToloInsFromLayerPanel.btnNessuno.text"),
            flex: 1,
            handler: function(){
            	for (var i=0; i<store.getCount(); i++) {
        			var r = store.getAt(i);
        			r.set('active',false);
        		}
            }
        },{
        	text: ToloI18n.getMsg("ToloInsFromLayerPanel.btnTutti.text"),
            flex: 1,
            handler: function(){
            	for (var i=0; i<store.getCount(); i++) {
        			var r = store.getAt(i);
        			r.set('active',true);
        		}

            }
        },{
        	text: ToloI18n.getMsg("ToloInsFromLayerPanel.btnOk.text"),
            flex: 1,
            handler: function(){
               if(thisFormPanel.getForm().isValid()){
            	   var bAll = true;
            	   var g = new JSGeometryArray();
            	   for (var i=0; i<store.getCount();i++){
            		   r=store.getAt(i);
            		   if (r.get("active")){
            			   g.add(r.get("val"));
            		   } else {
            			   bAll = false;   
            		   }
            	   }
            	   /*
            	   if (g.size()>0) {
            		   thisFormPanel.getForm().setValues({geoms: g.toString()});
            	   }
            	   */
            	   thisFormPanel.fireEvent("exitOk", o1, g, bAll);
            	   /*
                    Ext.Msg.alert('Submitted Values', 'The following will be sent to the server: <br />'+
                    		thisFormPanel.getForm().getValues(true).replace(/&/g,', '));*/
                }
            }
        },{
            text: ToloI18n.getMsg("ToloInsFromLayerPanel.btnAnnulla.text"),
            flex: 1,
            handler: function(){
            	thisFormPanel.fireEvent("exitCancel");
            }
        }];
		
		this.callParent();
		//this.doLayout();
		
	},
	
	itemmouseenter: function(grid, record, item, index, e, eOpts) {
		this.fireEvent("geompartitemmouseenter", record);
	},
	
	itemmouseleave: function(grid, record, item, index, e, eOpts) {
		this.fireEvent("geompartitemmouseleave", record);
	}

});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITA' o
 IDONEITA' PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.define('TolomeoExt.ToloSelectGeoms', {

	extend: 'Ext.FormPanel',
	
	/**
	 * @param jsGeometryList {JSGeometryArray}
	 */
	jsGeometryList: null,
	
	initComponent: function(){
		
		this.addEvents("geomItemClick");
		this.addEvents("selectedGeomsChange");
		this.addEvents("selectAllPressed");
		this.addEvents("selectNonePressed");
		this.addEvents("mostraPressed");
		this.addEvents("exitOK");
		this.addEvents("exitCancel");
		
		var thisFormPanel = this;
	    
		this.layout = { type: 'vbox', align: 'stretch'};
	    this.minButtonWidth = 50;
	    
		//this.layout = 'vbox';
		
		var storeData = [];
		for (var i=0; i<this.jsGeometryList.size(); i++) {
			var o = this.jsGeometryList.getByIndex(i);
			storeData.push([true, i, o]);
			//objRadioItems.push(Ext.create('Ext.form.field.Checkbox',{boxLabel: o.description, name: 'jsGeomToInsert', inputValue: o.toString(), checked: (i==0)}));
		}
		
		this.store = Ext.create('Ext.data.ArrayStore', {
		    // store configs
		    //storeId: 'myStore',
		    // reader configs
			autoSync: true,
		    fields: [
		       'active',
		       'description',
		       'val'
		    ],
		    data: storeData
		});
		
		
    	var listView = Ext.create('Ext.grid.Panel',{
    		store: this.store,
    		flex: 1,
    		//autoWidth: true,
    		//emptyText: 'Descrizione non recuperata',
    		viewConfig: {
    			//firstCls: null,
    			trackOver: true
    		},
    		
    		//reserveScrollOffset: true,
    		//multiSelect: false,
    		listeners: {
    			'itemclick': { fn: this.itemclick, scope: this}
    		},
    		columns: [
    		          { xtype : 'checkcolumn',
    			//header: 'active',
    			width: 30,
    			//flex: 1,
    			dataIndex: 'active',
    			listeners: { 
    				'checkChange': { fn: this._selectedGeomsChange, scope: this}
    			}
    		},{
    			header: ToloI18n.getMsg("ToloSelectGeoms.header"),
    			//width: 1,
    			flex: 1,
    			dataIndex: 'description'
    		}]
    	});
		/*
		this.items= [ objRadioFieldset , 
		              listView,{
	        xtype: 'hiddenfield',
	        name: 'geoms'
	     
	    }];*/
		this.items= [listView];
		
		this.buttons= [{
            text: ToloI18n.getMsg("ToloSelectGeoms.btnNessuno"),
            flex: 1,
            handler: function(){
            	for (var i=0; i<thisFormPanel.store.getCount(); i++) {
        			var r = thisFormPanel.store.getAt(i);
        			r.set('active',false);
        		}
            	thisFormPanel._validaForm();
            	thisFormPanel.fireEvent("selectNonePressed");
            	thisFormPanel.fireEvent('selectedGeomsChange', thisFormPanel.getSelectedGeometryArray());
            }
        },{
            text: ToloI18n.getMsg("ToloSelectGeoms.btnTutti"),
            flex: 1,
            handler: function(){
            	for (var i=0; i<thisFormPanel.store.getCount(); i++) {
        			var r = thisFormPanel.store.getAt(i);
        			r.set('active',true);
        		}
            	thisFormPanel._validaForm();
            	thisFormPanel.fireEvent("selectAllPressed");
            	thisFormPanel.fireEvent('selectedGeomsChange', thisFormPanel.getSelectedGeometryArray());
            }
        },{
            text: ToloI18n.getMsg("ToloSelectGeoms.btnMostra"),
            flex: 1,
            handler: function(){
            	thisFormPanel.fireEvent("mostraPressed");

            }
        },{
            text: ToloI18n.getMsg("ToloSelectGeoms.btnOK"),
            flex: 1,
            id: this.id + 'idOkButton',
            handler: function(){
               if(thisFormPanel.getForm().isValid()){
            	   var g = thisFormPanel.getSelectedGeometryArray();
            	   /*var g = new JSGeometryArray();
            	   for (var i=0; i<thisFormPanel.store.getCount();i++){
            		   r=thisFormPanel.store.getAt(i);
            		   if (r.get("active")){
            			   g.add(r.get("val"));
            		   }
            	   }*/
            	   thisFormPanel.fireEvent("exitOk", g);
                }
            }
        },{
            text: ToloI18n.getMsg("ToloSelectGeoms.btnAnnulla"),
            flex: 1,
            handler: function(){
            	thisFormPanel.fireEvent("exitCancel");
            }
        }];
		
		this.callParent();
		
	},
	
	getSelectedGeometryArray: function(){
		var g = new JSGeometryArray();
 	   	for (var i=0; i<this.store.getCount();i++){
 		   r=this.store.getAt(i);
 		   if (r.get("active")){
 			   g.add(r.get("val"));
 		   }
 	   	}
 	   	return g;
		
	},
	
	itemclick: function(grid, record, item, index, e, eOpts) {
		this.fireEvent("geomItemClick", record.get("val"));
	},
	
	_validaForm: function(){
		var okBtn = Ext.getCmp(this.id + 'idOkButton');
		var g = this.getSelectedGeometryArray();
		
		if (g.size()==0) {
			okBtn.setDisabled(true);
		} else {
			okBtn.setDisabled(false);
		}
	},
	
	_selectedGeomsChange: function(rowIndex, checked, eOpts){
		this._validaForm();
		this.fireEvent('selectedGeomsChange', this.getSelectedGeometryArray());
	}
	

});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * TolomeoExt.lazyLoad
 *
 * @author Nieri Federico
 *******************************************************************
 * Class TolomeoExt.lazyLoad
 * Load javascript files onDemand
 **/
TolomeoExt.lazyLoad = function () {

    var debug = false;
    var containingScriptNames = ['build/toloExt-all.js','build/toloExt-all-debug.js','build/toloExt-all-noext.js','build/toloExt-all-noext-debug.js'];
    var thisScriptName = 'toloLazyLoad.js';
    
    var objects = {
 //       gmap: {
 //           loaded: false,
 //           checkLoad: 'GMap2', /*Variable that must exists to confirm load*/           
 //           url: 'http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAZtXebGuEUyPUsb16TunzchQH1vzzQPdXCsLMkqVnPRtWvfSdoRToJrguncXU9Z0FgvbgReXeej9BDQ&sensor=false&async=2'
 //       },        
        proj4js: {
            loaded: false,
            checkLoad: 'Proj4js',
            relativeToThisScript : true,
            url: '../ext/proj4js/proj4js-compressed.js'
        }, 
		swfobject: {
            loaded: false,
            checkLoad: 'swfobject',
            relativeToThisScript : true,
            url: '../ext/swfobject/swfobject.js'
        },
        mootools12corecompressed: {
            loaded: false,
            checkLoad: 'MooTools',
            relativeToThisScript : true,
            url: '../ext/mootools/mootools-1.2-core-compressed.js'
        },
        mootools12morecompressed: {
            loaded: false,
            checkLoad: 'Drag',
            relativeToThisScript : true,
            url: '../ext/mootools/mootools-1.2-more-compressed.js'
        },
        iipmooviewer11compressed: {
            loaded: false,
            checkLoad: 'IIP',
            relativeToThisScript : true,
            url: '../ext/iipmooviewer/iipmooviewer-1.1.js'
        },
        mootoolscore145: {
            loaded: false,
            checkLoad: 'MooTools',
            relativeToThisScript : true,
            url: '../ext/mootools/mootools-core-1.4.5-full-nocompat-yc.js'
        },
        mootoolsmore1401: {
            loaded: false,
            checkLoad: 'Drag',
            relativeToThisScript : true,
            url: '../ext/mootools/mootools-more-1.4.0.1-compressed.js'
        },
        iipmooviewer20: {
            loaded: false,
            checkLoad: 'IIPMooViewer',
            relativeToThisScript : true,
            url: '../ext/iipmooviewer-2.0/javascript/iipmooviewer-2.0-compressed.js'
            //url: '../ext/iipmooviewer-2.0/src/iipmooviewer-2.0.js'
        },
        /*
        i18nPropertyReader: {
            loaded: false,
            checkLoad: 'Ext.i18n.PropertyReader',
            relativeToThisScript : true,
            url: '../ext/resourceBundle/PropertyReader.js'
        },
        i18nBundle: {
            loaded: false,
            checkLoad: 'Ext.i18n.Bundle',
            relativeToThisScript : true,
            url: '../ext/resourceBundle/Bundle.js'
        },*/
        
        i18nPropertyReader: {
            loaded: false,
            checkLoad: 'Ext.i18n.PropertyReader',
            relativeToThisScript : true,
            url: '../ext/i18n/reader/Property.js'
        },
        i18nJsonReader: {
            loaded: false,
            checkLoad: 'Ext.i18n.reader.Json',
            relativeToThisScript : true,
            url: '../ext/i18n/reader/Json.js'
        },
        i18nBundle: {
            loaded: false,
            checkLoad: 'Ext.i18n.Bundle',
            relativeToThisScript : true,
            url: '../ext/i18n/Bundle.js'
        },
        cswExplorer: {
            loaded: false,
            checkLoad: 'CSWPanel',
            relativeToThisScript : true,
            url: 'build/cswExplorer.js'
        },
        cesium: {
            loaded: false,
            checkLoad: 'Cesium',
            relativeToThisScript : true,
            url: '../ext/cesium/Build/Cesium/Cesium.js'
        }
    }
    
    return {
    	getScriptLocation: function () {    
    		if(TolomeoExt.lazyLoad.libPath) return TolomeoExt.lazyLoad.libPath;
    		
	        var scripts = document.getElementsByTagName('script');
	        for (var i = 0; i < scripts.length; i++) {
	            var src = scripts[i].getAttribute('src');
	            if (src) {
	            	//Elimina eventuale querystring
		            var p = src.lastIndexOf("?");
		            if (p!=-1) {
		            	src=src.substring(0,p);
		            }
	                var index = src.lastIndexOf(thisScriptName);
	                var scriptNameLen = thisScriptName.length;	                
	                // is it found, at the end of the URL?
	                if ((index > -1) && (index + scriptNameLen == src.length)) {
	                	TolomeoExt.lazyLoad.libPath = src.slice(0, -scriptNameLen);
	                    return TolomeoExt.lazyLoad.libPath;
	                }	            	
	            }
	        }
	        for (var i = 0; i < scripts.length; i++) {
	            var src = scripts[i].getAttribute('src');
	            if (src) {
	            	//Elimina eventuale querystring
		            var p = src.lastIndexOf("?");
		            if (p!=-1) {
		            	src=src.substring(0,p);
		            }
		            
	            	for(var s = 0; s < containingScriptNames.length; s++){
	            		var scriptName  = containingScriptNames[s];
		                var index = src.lastIndexOf(scriptName);
		                var scriptNameLen = scriptName.length;	                
		                // is it found, at the end of the URL?
		                if ((index > -1) && (index + scriptNameLen == src.length)) {
		                	TolomeoExt.lazyLoad.libPath = src.slice(0, -scriptNameLen);
		                    return TolomeoExt.lazyLoad.libPath;
		                }
	            	}
	            }
	        }
	        return "";
	    },
	    
        /*
         * @name Ext.ux.lazyLoad.get
         * Retrieve js files on demand if not loaded
         * @params
         *  -jsKey {String}: Js File name to load
         *  -callback {function}: function to be executed after
         *  -scope {Object}: Object scope to run the callback function
         **/
        get: function (jsKey, onLoad, onFail, scope) {
            /*Return if is not a valid object*/
            if (!objects[jsKey]) return false;

            /*Run thread*/
            var waitCounter = 0;
            this.thread(jsKey, onLoad, onFail, scope, waitCounter, 'loadScript' + Ext.id());
            return true;
        }
        /*
         *@name Ext.ux.lazyLoad.thread
         *Reusable thread to check if js file has been loaded
         *@params
         *  -jsKey{String}: js key from the objects array
         *  -callback{Function}: function to execute when file is loaded
         *  -scope {Object}: Object scope to run the callback function
         *  -waitCointer{Integer}: Current cycle number
         *  -id{String}: Id used to identify the script tag
         **/
        ,
        thread: function (jsKey, onLoad, onFail, scope, waitCounter, id) {

            /*If its loaded*/
            if (this.isLoaded(jsKey)) {
                onLoad.call(scope);
            } else {
                /*If script object hasnt been included, do it*/
                if (!Ext.get(id)) {
                    /*If hasnt been loaded*/
                    /*Prepare url*/
                    var url = objects[jsKey].url;
                    if(objects[jsKey].relativeToThisScript){
                    	url = this.getScriptLocation()+ url;
                    }

                    this.head = document.getElementsByTagName('head').item(0);
                    script = document.createElement('script');
                    script.defer = false;
                    script.src = url;
                    script.type = 'text/javascript';
                    script.id = id;
                    script.idDefer = null;
                    var me = this;
                    script.onload = function(){me.endLoading(true,onLoad,scope,id,jsKey);};
      				script.onerror = function(){me.endLoading(false,onFail,scope,id,jsKey);};
      				
      				if (Ext.isIE) {
				        script.onreadystatechange = function(){me.checkReadyState(this.readyState,onLoad,onFail,scope,id,jsKey);};
				    }
				    
                    this.head.appendChild(script);
                }
                //if (debug) Ext.get('testdiv').insertHtml('beforeEnd', jsKey + ' wait counter:' + waitCounter + '<br>');
                //console.debug(jsKey + ' wait counter:' + waitCounter);
                if(this.checkLoad(jsKey)){
                    this.endLoading(true,onLoad,scope,id,jsKey);
                    return;
                   
                } else {
                    if (waitCounter++<10) {
                        /*Will check 10 times every 2 seconds if the js has been loaded*/
                    	var scriptTag = document.getElementById(id);
                        scriptTag.idDefer = Ext.defer(this.thread,2000, this, [jsKey, onLoad, onFail, scope, waitCounter, id]);
                    } else {
                    	this.endLoading(false,onFail,scope,id,jsKey);
                    }
                }

            }
        },
        
        checkLoad : function(jsKey){        
        	try {
                return eval(objects[jsKey].checkLoad);                
            } catch(e) {
            	return false;
          	}
        },
        
        endLoading : function(ok,callMe,scope,id,jsKey){
        	this.clear(id);
        	if(ok){        		
        		objects[jsKey].loaded = true;
        	}else{
        		this.clean(id);
        	}
        	if(callMe){
        		callMe.call(scope);
        	}else if(!ok){
        		alert(ToloI18n.getMsg("lazyLoad.ErroreCaricamento"));
        	}
        	
        },
        
        checkReadyState: function(readyState,onLoad,onFail,scope,id,jsKey) {
	      if (readyState == 'loaded') {
	        if(this.checkLoad(jsKey)){                
                this.endLoading(true,onLoad,scope,id,jsKey);                
            } else {
            	this.endLoading(false,onFail,scope,id,jsKey);
          	}
	      }
	    },
	    
        clear: function (id) {
            var scriptTag = document.getElementById(id);
            if (scriptTag && scriptTag.idDefer) clearTimeout(scriptTag.idDefer);
        }
        /*
         * @name Ext.ux.lazyLoad.clean
         * Removes recently used script tag from dom
         **/
        ,
        clean: function (id) {
            var scriptTag = document.getElementById(id);
            if (scriptTag) this.head.removeChild(scriptTag);
        }
        /*
         * @name Ext.ux.lazyLoad.isLoaded
         * Getter function to retrieve status
         **/
        ,
        isLoaded: function (jsKey) {
            return objects[jsKey].loaded;
        }
    }
}();/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.Projection
 * 
 * 
 */
Ext.define('TolomeoExt.Projection', {

	extend: 'Object',

	statics: {
	
		/**
		 * @property {Object} [transforms={}]
		 * Transforms is an object, with from properties, each of which may
		 * have a to property. This allows you to define projections without 
		 * requiring support for proj4js to be included.
		 *
		 * This object has keys which correspond to a 'source' projection object.  The
		 * keys should be strings, corresponding to the projection.getCode() value.
		 * Each source projection object should have a set of destination projection
		 * keys included in the object. 
		 * 
		 * Each value in the destination object should be a transformation function,
		 * where the function is expected to be passed an object with a .x and a .y
		 * property.  The function should return the object, with the .x and .y
		 * transformed according to the transformation function.
		 *
		 * Note - Properties on this object should not be set directly.  To add a
		 *     transform method to this object, use the "addTransform" method.  For an
		 *     example of usage, see the OpenLayers.Layer.SphericalMercator file.
		 */
		transforms: {},

		/**
		 * @method addTransform
		 * Set a custom transform method between two projections.  Use this method in
		 * cases where the proj4js lib is not available or where custom projections
		 * need to be handled.
		 * 
		 * @param {String} from
		 * The code for the source projection
		 * 
		 * @param {String} to
		 * the code for the destination projection
		 * 
		 * @param {Function} method
		 * A function that takes a point as an argument and
		 * transforms that point from the source to the destination projection
		 * in place.  The original point should be modified.
		 * 
		 */
		addTransform: function(from, to, method) {
		    if(!TolomeoExt.Projection.transforms[from]) {
		        TolomeoExt.Projection.transforms[from] = {};
		    }
		    TolomeoExt.Projection.transforms[from][to] = method;
		},

		/**
		 * @method transform
		 * Transform a point coordinate from one projection to another.  Note that
		 * the input point is transformed in place.
		 * 
		 * @param {TolomeoExt.Point/Object} point
		 * An object with x and y
		 * properties representing coordinates in those dimensions.
		 *
		 * @param {TolomeoExt.Projection/String} source
		 * Source map coordinate system
		 * 
		 * @param {TolomeoExt.Projection/String} dest
		 * Destination map coordinate system
		 *
		 * @return {TolomeoExt.Point/Object}
		 * A transformed coordinate.  The original point is modified.
		 * 
		 */
		transform: function(point, source, dest) {
			if(Ext.isString(source)){
				source = new TolomeoExt.Projection(source);
			}
			if(Ext.isString(dest)){
				dest = new TolomeoExt.Projection(dest);
			}
		    if (source.proj && dest.proj) {
		        var proj4Point = Proj4js.transform(source.proj, dest.proj, point);
		    } else if (source && dest && 
		        TolomeoExt.Projection.transforms[source.getCode()] && 
		        TolomeoExt.Projection.transforms[source.getCode()][dest.getCode()]) {
		        TolomeoExt.Projection.transforms[source.getCode()][dest.getCode()](point); 
		    }
		    return new Point(proj4Point.x,proj4Point.y);
		},

		/**
		 * @method addDefinition
		 * Add at runtime a custom definition of a source projection
		 * 
		 * @param {String} projCode
		 * The code for the source projection
		 * 
		 * @param {String} def
		 * source projection definition
		 * 
		 */
		addDefinition: function(projCode,def){
			TolomeoExt.Projection.defs[projCode] = def;
		},


		/**
		 * @method loadDefinition
		 * Load the definition of a source projection if it doesn't already exist
		 * 
		 * @param {String} projCode
		 * The code for the source projection
		 * 
		 */
		loadDefinition: function(projCode){
			if (window.Proj4js) {
				if(!window.Proj4js.defs[projCode] && TolomeoExt.Projection.defs[projCode]){
					window.Proj4js.defs[projCode] = TolomeoExt.Projection.defs[projCode];
				} else {
					new Proj4js.Proj(projCode);
				}
		    }
		},
		
    /**
     * @property {Object} defs
     * 
     */
		defs: { "EPSG:3003": "+proj=tmerc +lat_0=0 +lon_0=9 +k=0.9996 +x_0=1500000 +y_0=0 +ellps=intl +towgs84=-104.1,-49.1,-9.9,0.971,-2.917,0.714,-11.68 +units=m +no_defs",
				"EPSG:25832": "+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs"}
	},

	
    /**
     * @property {Object} proj
     * Proj4js.Proj instance.
     * 
     */
    proj: null,
    
    /**
     * @property {String} projCode
     * 
     */
    projCode: null,
	
	/**
	 * @constructor
	 * 
	 * 
	 * @param {Object} config
	 * 
	 * 
	 */
	constructor : function(config){
		if(Ext.isString(config)){            
			config = {projCode: config};
		}        
		Ext.apply(this, config);  
		if (window.Proj4js) {
			if(!window.Proj4js.defs[this.projCode] && TolomeoExt.Projection.defs[this.projCode]){
				window.Proj4js.defs[this.projCode] = TolomeoExt.Projection.defs[this.projCode];
			}
	        this.proj = new Proj4js.Proj(this.projCode);                        
	    }
	},
	 
	
    /**
     * @method getCode
     * Get the string SRS code.
     *
     * @return {String}
     * The SRS code.
     */
    getCode: function() {
        return this.proj ? this.proj.srsCode : this.projCode;
    },
   
    /**
     * @method getUnits
     * Get the units string for the projection -- returns null if 
     * proj4js is not available.
     *
     * @return {String}
     * The units abbreviation.
     * 
     */
    getUnits: function() {
        return this.proj ? this.proj.units : null;
    },
    
    /**
     * @method getTitle
     * 
     *
     * @return {String}
     * 
     * 
     */
    getTitle: function() {
        return this.proj ? (this.proj.title ? this.proj.title : this.proj.srsCode) : null;
    },

    /**
     * @method toString
     * Convert projection to string (getCode wrapper).
     * 
     * @return {String}
     * The projection code.
     * 
     */
    toString: function() {
        return this.getCode();
    },

    /**
     * @method equals
     * Test equality of two projection instances.  Determines equality based
     * soley on the projection code.
     *
		 * @param {TolomeoExt.Projection} projection
		 * 
		 * 
		 * @return {Boolean}
     * The two projections are equivalent.
     * 
     */
    equals: function(projection) {
        if (projection && projection.getCode) {
            return this.getCode() == projection.getCode();
        } else {
            return false;
        }    
    },
		
		/**
     * @method destroy
     * Destroy projection object.
     * 
     */
    destroy: function() {
        delete this.proj;
        delete this.projCode;
    }
});     

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Class: TolomeoExt.ToloMapAPIExt
 * Funzioni pubbliche di controllo della mappa (posizione, zoom, evidenziazioni, etc.). 
 *
 * Inherits from:
 *  - <Ext.util.Observable>
 *
 */

Ext.define('TolomeoExt.ToloMapAPIExt', {
	
	 //mixins: {
     //   observable: 'Ext.util.Observable'
    //},


	extend: 'Ext.util.Observable',
	
//TolomeoExt.ToloMapAPIExt = Ext.extend(Ext.util.Observable,{
		
	// Variabili e costanti

	
	/*
	 * 
	 * fonte: http://wiki.openstreetmap.org/wiki/Zoom_levels
	 * The 'degree' column gives the map width in degrees, for map at that zoom level which is 256 pixels wide. 
	 * Values listed in the column "m / pixels" gives the number of meters per pixel at that zoom level. 
	 * These values for "m / pixel" are calculated with an earth radius of 6372.7982 km. 
	 * "Scale" (map scale) is only an approximate size comparison and refers to distances on the equator. 
	 * In addition, the map scale will be dependent on the monitor. 
	 * These values are for a monitor with a 0.3 mm / pixel (85.2 pixels per inch or PPI)
	*/ 
	zoomLevelConvTable : [
	       {zoomLevel: 0,  metriperpixel: 156412,    scaledenom: 500000000},
	       {zoomLevel: 1,  metriperpixel: 78206,     scaledenom: 250000000},
	       {zoomLevel: 2,  metriperpixel: 39103,     scaledenom: 150000000},
	       {zoomLevel: 3,  metriperpixel: 19551,     scaledenom:  70000000},
	       {zoomLevel: 4,  metriperpixel:  9776,     scaledenom:  35000000},
	       {zoomLevel: 5,  metriperpixel:  4888,     scaledenom:  15000000},
	       {zoomLevel: 6,  metriperpixel:  2444,     scaledenom:  10000000},
	       {zoomLevel: 7,  metriperpixel:  1222,     scaledenom:   4000000},
	       {zoomLevel: 8,  metriperpixel:   610.984, scaledenom:   2000000},
	       {zoomLevel: 9,  metriperpixel:   305.492, scaledenom:   1000000},
	       {zoomLevel: 10, metriperpixel:   152.746, scaledenom:    500000},
	       {zoomLevel: 11, metriperpixel:    76.373, scaledenom:    250000},
	       {zoomLevel: 12, metriperpixel:    38.187, scaledenom:    150000},
	       {zoomLevel: 13, metriperpixel:    19.093, scaledenom:     70000},
	       {zoomLevel: 14, metriperpixel:     9.547, scaledenom:     35000},
	       {zoomLevel: 15, metriperpixel:     4.773, scaledenom:     15000},
	       {zoomLevel: 16, metriperpixel:     2.387, scaledenom:      8000},
	       {zoomLevel: 17, metriperpixel:     1.193, scaledenom:      4000},
	       {zoomLevel: 18, metriperpixel:     0.596, scaledenom:      2000},
	       {zoomLevel: 19, metriperpixel:     0.298, scaledenom:      1000} ],
	
	
	/** 
	 * Property: currentDigitizeOperation
	 * {String} Operazione di digitalizzazione corrente ex var wflgpoliadddel.
	 */
	currentDigitizeOperation: '',	

	/** 
	 * Property: digitizeOperationInsert
	 * {String} Operazione di Inserimento-> 'N' .
	 */
	digitizeOperationInsert: 'N',  
	
	/** 
	 * Property: digitizeOperationInsertFromLayer
	 * {String} Operazione di Inserimento da Layer-> 'F' .
	 */
	digitizeOperationInsertFromLayer: 'F', 

	/** 
	 * Property: digitizeOperationInsertFromImport
	 * {String} Operazione di Inserimento da Import-> 'P' .
	 */
	digitizeOperationInsertFromImport: 'P', 
	
	/** 
	 * Property: digitizeOperationVertexEdit
	 * {String} Operazione di VertexEdit -> 'V'.
	 */
	digitizeOperationVertexEdit: 'V',	 

	/** 
	 * Property: digitizeOperationDragDrop
	 * {String} Operazione di DragDrop -> 'R'.
	 */
	digitizeOperationDragDrop: 'R',	

	/** 
	 * Property: operationGeometryModify
	 * {String} Operazione di generica modifica geometria -> 'G' .
	 */
	operationGeometryModify: "G",  

	/** 
	 * Property: digitizeOperationSubtract
	 * {String} Operazione di Subtract -> 'D' .
	 */
	digitizeOperationSubtract: 'D',	

	/** 
	 * Property: digitizeOperationAdd
	 * {String} Operazione di Add -> 'O' .
	 */
	digitizeOperationAdd: 'O',	

	/** 
	 * Property: digitizeOperationAddSub
	 * {String} Operazione di AddSub -> 'M' .
	 */
	digitizeOperationAddSub: 'M',

	/** 
	 * Property: operationFeatureDelete
	 * {String} Operazione di Cancellazione Feature .
	 */
	operationFeatureDelete: 'C',  

	/** 
	 * Property: operationUpdateAlfa
	 * {String} Operazione di Aggiornamento alfanumerico.
	 */
	operationUpdateAlfa: 'A',  

	/** 
	 * Property: operationIdentify
	 * {String} Operazione di Identify .
	 */
	operationIdentify: 'I', 

	/** 
	 * Property: currentSelectedKeys
	 * {String} Chiavi degli oggetti correntemente selezionati per il layer correntemente sotto editing.
	 */
	currentSelectedKeys: null, 

	/** 
	 * Property: bEditingSingoloInsertDone
	 * {} Indica se in modalità di editing singolo e' già stata effettuata la fase di insert.
	 */
	bEditingSingoloInsertDone:null,

	/** 
	 * Property: bEditingSingoloInsertDone
	 * {Boolean} Indica se in modalità di editing singolo e' già stata effettuata la fase di insert.
	 */
	bEditingSingoloInsertDone: false,

	/** 
	 * Property: eventVis
	 * {Number} Costante che indica l'evento di visualizzazione .
	 */
	eventVis: 0,

	/** 
	 * Property: eventCanc
	 * {Number} Costante che indica l'evento di cancellazione.
	 */
	eventCanc: 1,

	/** 
	 * Property: eventUpdateGeom
	 * {Number} Costante che indica l'evento di aggiornamento geometrico .
	 */
	eventUpdateGeom: 2,

	/** 
	 * Property: eventUpdateAlpha
	 * {Number} Costante che indica l'evento di aggiornamento alfanumerico.
	 */
	eventUpdateAlpha: 3,

	/** 
	 * Property: eventIns
	 * {Number} Costante che indica l'evento di inserimento.
	 */
	eventIns: 4, 	

	/** 
	 * Property: eventRicerca
	 * {Number} Costante che indica l'evento di ricerca.
	 */
	eventRicerca: 5,	

	/** 
	 * Property: eventCustomButton
	 * {Number} Costante che indica l'evento di custom button.
	 */
	eventCustomButton: 6,	
	
	/** 
	 * Property: eventInsFromLayer
	 * {Number} Costante che indica l'evento di inserimento prelevando geometria da altro layer.
	 */
	eventInsFromLayer: 7,

	/** 
	 * Property: eventInsFromImprt
	 * {Number} Costante che indica l'evento di inserimento prelevando geometria da import.
	 */
	eventInsFromImport: 8,

	
	/** 
	 * Property: currentSelectedLayer
	 * {} layer corrente (codTPN).
	 */
	currentSelectedLayer: null,

	/** 
	 * Property: selezioneCorrente
	 * {JSGeometryArray} Contiene l'oggetto JSGeometry {@link JSGeometry} attualmente selezionato  oppure null se non e' selezionato niente.
	 */
	selezioneCorrente: new JSGeometryArray(),

	/** 
	 * Property: evidenziazioneCorrente
	 * {JSGeometryArray} Contiene l'oggetto JSGeometry {@link JSGeometry} attualmente evidenziato  oppure null se non e' selezionato niente.
	 */
	evidenziazioneCorrente: new JSGeometryArray(),

	/** 
	 * Property: autoIdentifyCorrente
	 * {JSGeometryArray} 
	 */
	autoIdentifyCorrente: new JSGeometryArray(),

 	/** 
	 * Property: routingCorrente
	 * {JSGeometryArray} Contiene l'oggetto JSGeometry {@link JSGeometry} attualmente evidenziato  oppure null se non e' selezionato niente.
	 */
	routingCorrente: new JSGeometryArray(),
	
	/** 
	 * Property: implicitCustomQuery
	 * {Object} 
	 */
	implicitCustomQuery: new Object(),

	
	/*
	 * 
	 * 
	 * 
	 */
	/**
	 * Property: explicitCustomQueryParams
	 * {Object}
	 * Condizioni esplicite da aggiungere alle chiamate WMS, eventualmente da inserire solo x le chiamate ad alcuni server.
	 * L'oggetto passato deve essere della seguente forma, che ha il seguente significato: aggiunta di param1 con valore val1 per tutti i server,
	 * di chkproc e parametro due per il server che ha SERVERID=WMSLOCALHOST del parametro chkproc per il server che ha SERVERID=RTAMBITIAMM. 
	 * Sono disponibili anche metodi API per la gestione programmatica addExplicitCustomQueryParam, removeExplicitCustomQueryParam, getExplicitCustomQueryParam
	 *     {
		   "param1": 'val1',
		   "WMSLOCALHOST": { "chkproc": chkproc,
							  "parametro2": 'param2'},
		   "RTAMBITIAMM":  { "chkproc": chkproc}
			}															  
														} 
	 */
	explicitCustomQueryParams: {},
	
	/** 
	 * Property: paramsPrev
	 * {} 
	 */
	paramsPrev: null,

	/** 
	 * Property: submitForm
	 * {Ext.form.FormPanel} 
	 */
	submitForm: null,

	// Config parameters
	/** 
	 * Property: paramsJS
	 * {ToloParamsJS}
	 */
	paramsJS: null, 

	/** 
	 * Property: TOLOMEOServer
	 * {String}
	 */
	TOLOMEOServer: null,
	
	/** 
	 * Property: TOLOMEOStaticRoot
	 * {String}
	 */
	TOLOMEOStaticRoot: null,
	

	/** 
	 * Property: TOLOMEOContext
	 * {String}
	 */
	TOLOMEOContext: null,

	/** 
	 * Property: viewer
	 * {TolomeoExt.ToloViewerOLPanel} 
	 */
	viewer: null,

	/** 
	 * Property: viewer3D
	 */
	viewer3D: null,

	viewer3DWidget: null,

	/** 
	 * Property: TOCPanel
	 * {}
	 */
	TOCPanel: null,
	
	tocGuiCreateAction: null,

	/** 
	 * Property: queryPanel
	 * {TolomeoExt.ToloQueryPanelExt}
	 */
	queryPanel: null,
	
 	/** 
	 * Property: olsPanel
	 * {TolomeoExt.OLS.ToloOLSPanelExt}
	 */
	olsPanel: null,
	
	/** 
	 * Property: queryBuilderPanel
	 * {TolomeoExt.ToloQueryBuilderExt}
	 */
	queryBuilderPanel: null,

	/** 
	 * Property: codeLessPanel
	 * {TolomeoExt.ToloCodeLessPanel}
	 */
	codeLessPanel: null,

	/** 
	 * Property: viewCodeLessPanel
	 * {TolomeoExt.ToloVviewCodeLessPanel}
	 */
	viewCodeLessPanel: null,

	/** 
	 * Property: buttonsPanel
	 * {TolomeoExt.ToloButtonPanelExt}
	 */
	buttonsPanel: null,

	/** 
	 * Property: selectedChoiceWindow
	 * {}
	 */
	selectedChoiceWindow: null,

	/** 
	 * Property: autoIdentifyWindow
	 * {}
	 */
	autoIdentifyWindow: null,

	/**
	 * @cfg {TolomeoExt.ToloStylePanel} 
	 * 
	 * Pannello di gestione degli stili (se richiesto)
	 * 
	 */
	stylePanel: null,
	
	/** 
	 * Property: geoOpField
	 * {Ext.form.TextField}
	 */
	geoOpField: null,

	/** 
	 * Property: geoCoordField
	 * {Ext.form.TextField}
	 */
	geoCoordField: null,

	/** 
	 * Property: geoCoordParts
	 * {Ext.form.TextField}
	 */
	geoCoordParts: null,

	
	geomPartsAllFlag: null,
	
	
	/** 
	 * Property: selectedListField
	 * {Ext.form.TextField}
	 */
	selectedListField: null,
	
	/** 
	 * Property: openActionsJS
	 * {String}
	 */
	openActionsJS: null,
	
	/** 
	 * Property: titoloMappa
	 * {String}
	 */
	titoloMappa: 'Mappa di Prato',
	
	/** 
	 * Property: sottotitoloMappa
	 * {String}
	 */
	sottotitoloMappa: null,
	
	/** 
	 * Property: descrizioneMappa
	 * {String}
	 */
	descrizioneMappa: null,
	
	/** 
	 * Property: stampaReferer
	 * {boolean}
	 */
	//stampaReferer: true,
	
	 * Property: urlLogo
	 * Url del logo principale che appare in alto a sinistra nella stampa
	 * {String}
	 */
	urlLogo: "",
	
	/** 
	 * Property: urlLogoSecondario
	 * Url del logo che appare in alto a destra nella stampa
	 * {String}
	 */
	urlLogoSecondario: "",
	
	/**
	 * id dei campi che devono contenere il risultati a fine azione
	 * E' composto da un oggetto di questo tipo
	 * {  	geoOp: "actionEndGeoOp", 
	 *		geoCoord: "actionEndGeoCoord", 
	 *		selectedList: "actionEndSelectedList",
	 *		IDTPN: "actionEndIDTPN",
	 *		codTPN: "actionEndCodTPN"}
	 * @type {Object}
	 */
	actionsEndReturnFields: null,
	
	/** 
	 * Property: temporalFilterDtInizio
	 * {String}
	 */
	temporalFilterDtInizio: null,
	
	/** 
	 * Property: temporalFilterDtFine
	 * {String}
	 */
	temporalFilterDtFine: null,
	
	/** 
	 * Property: permalinkParameterSuffix
	 * Suffisso da utilizzare per recuperare o costruire i lnome dei parametri del permalink
	 * {String}
	 */
	permalinkParameterSuffix: null,
	
	autoCloseChoiceWindow: true,
	
	wmsExplorerWidget: null,
	
	cswWidget: null,
	
	/**
	 * Property: projectionCrs
	 * {Obect} Specifica i sistemi di riferimento da utilizzare nella riproiezione (vedi Mostra Coordinate)
	 */
	
	
	/*'EPSG:26591':{precision: 2, description: 'Gauss Boaga [26591]'},*/
	projectionCrs: {'EPSG:25832': {precision: 2, description: 'ETRS89 / UTM zone 32N [25832]'},
					'EPSG:6707':  {precision: 2, description: 'RDN2008 / UTM zone 32N [6707]'},
					'EPSG:3003':  {precision: 2, description: 'Gauss Boaga [3003]'},
					'EPSG:4326':  {precision: 7, description: 'WGS84'}},
	
	contextMenu: null,
	
	statusPanel: null,
	
	// Separatore utilizzato per separare stringe di layer e stili
	layerStringSeparator: ',',
   
	/**
	 * Constructor: TolomeoExt.ToloMapAPIExt
	 * Create a new TolomeoExt.ToloMapAPIExt
	 * 
	 *  Parameters:
	 *  config - {Object} La configurazione
	 *  
	 *  Returns:
	 *  {<TolomeoExt.ToloMapAPIExt>} A new TolomeoExt.ToloMapAPIExt
	 */
	constructor: function(config) {

		me = this;
		Ext.apply(this, config);
	    
		// Assegna un id casuale
		this.apiid = 'tolomeoapi-' + Math.floor((Math.random() * 100000) + 1);
		
		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
        TolomeoExt.applyIfEmpty(this, {
        	actionsEndReturnFields: {
        		geoOp:        "actionEndGeoOp", 
				geoCoord:     "actionEndGeoCoord", 
				selectedList: "actionEndSelectedList",
				IDTPN:        "actionEndIDTPN",
				codTPN:       "actionEndCodTPN"
			},
        	temporalFilterDtInizio: "01/01/0001",
        	temporalFilterDtFine  : "31/12/9999"
		});
		
        this.callParent(arguments);
	 
        
		this.bEditingSingoloInsertDone = false;
		
		// Definisco eventi 
	    this.addEvents('onOperationEnable');   		 // passa codice operazione da abilitare
	    this.addEvents('onOperationDisable');		 // passa codice operazione da disabilitare
	    this.addEvents('onOperationPressDefault');	 // passa codice gruppo di cui premere default
	    this.addEvents('onObjectSelect'); 			 // oggetto selezionato utilizando lo strumento di selezione
	    
	    this.addEvents('onBeforeClearSelected'); 	 // prima che la selezione corrente sia cancellata (svuotata). PAssa i parametri , bRedraw, codTPN, bClearUrl
	    this.addEvents('onAfterClearSelected'); 	 // dopo che la selezione corrente cancellata (svuotata). PAssa i parametri , bRedraw, codTPN, bClearUrl
	    this.addEvents("actionsEnd"); 				 // lanciato a fine azioni, passa un oggetto contenente geoOp, geoCoord etc. 
	    this.addEvents("digitizedFeatureModifyEnd"); // Eseguita quando una feature digitalizzata viene in un secondo momento modificata
		this.addEvents("onEventActionAjaxSuccess");  // Evento lanciato quando una azioneevento conclude con successo una chiamata ajax.L'evento viene chiamato con i seguenti parametri
													 // eventoLayer, tipoEvento, idBtn, nStep, records, store. 
													 // Se la chiamata non è crossDomain records contiene trasport della chiamata normale e store non c'e'
													 // Utilizzare con codice come il seguente
													 // function a(eventoLayer, tipoEvento, idBtn, nStep, records, store) {
													 //     alert("ale");
													 //	}
													 // function apriMappa(codici) {
													 //	tolomeo = new TolomeoExt.ToloPanelIntra({
													 //		withDataPanel: true,
													 //		withToolsPanel: false,
													 //		APIConfig: {
													 //			listeners: {
													 //       	 		onEventActionAjaxSuccess: {
													 //								fn: a
													 //							      }
													 //				}
													 //			}
													 //	});
	    this.addEvents("onEventActionAjaxFailure");
	    this.addEvents("beforeOpenUrl");			// Evento lanciato prima di una openURL (apertura di url su un certo target alla fine di una azione)
	    this.addEvents("openUrl");					// Evento lanciato alla fine di una openURL (apertura di url su un certo target alla fine di una azione)
	                                                // Parametri: url e target 
	    
	    this.addEvents("beforeClearUrl");			// Evento lanciato prima di una clearURL 
	    this.addEvents("clearUrl");					// Evento lanciato alla fine di una clearURL
	                                                // Parametri: url e target 
	    this.addEvents("visualize");				// Evento lanciato quando viene richiesto di visualizzare i dati di un oggetto. Vengono passati in automatico codTPN e IDTPN
	    this.addEvents("selectRequestBeforeStart"); // Evento lanciato prima di fare la richiesta per recuperare una selezione
	    this.addEvents("selectRequestStart");       // Evento lanciato quando la richiesta per recuperare una selezione è partita
	    this.addEvents("selectRequestEnd");			// Evento lanciato quando la richiesta per recuperare una selezione è finita. Viene passato l'oggetto con l'esito, il numero di risultati ed il messaggio di errore.
	    this.addEvents("tocGuiCreate");	    
	    
	    this.addEvents("afterContextMenuCreate");   // Evento lanciato dopo che il menu di contesto è stato creato
	    this.addEvents("j2qConnected");             // Evento lanciato doop che si è verificata una connessione con il plugin J2Q di QGis
	    this.addEvents("selectedObjectsRemoved");   // Evento lanciato quando vengono rimossi oggetti dalla selezione. Viene passata la lista degli oggetti rimossi e l'ultimo oggetto rimasto in selezione corrente.
	    
	    this.addEvents("previousNavigationStateAvailable");		// Evento lanciato quando è disponibile lo stato di navigazione mappa precedente
		this.addEvents("previousNavigationStateNotAvailable");	// Evento lanciato quando non è disponibile lo stato di navigazione mappa precedente
		this.addEvents("nextNavigationStateAvailable");       	// Evento lanciato quando è disponibile lo stato di navigazione mappa successivo
		this.addEvents("nextNavigationStateNotAvailable");		// Evento lanciato quando non è disponibile lo stato di navigazione mappa successivo
	    
		this.addEvents("digitizeEnd");				// Evento lanciato quando finisce la digitalizzazione di una geometria, passa la gemetria stessa
		this.addEvents("digitizeEndInsert");		// Evento lanciato quando finisce la digitalizzazione di una geometria per funzione di insert (nuova geometria), pass la gemetria stessa
		
		this.addEvents("digitizeEndAdd");			// Evento lanciato quando finisce la digitalizzazione di una geometria per funzione di Add passa la gemetria stessa
		this.addEvents("digitizeEndVertexEditing");	// Evento lanciato quando finisce la digitalizzazione di una geometria per funzione di vertezediting, passa la gemetria stessa
		this.addEvents("digitizeEndDragDrop");		// Evento lanciato quando finisce la digitalizzazione di una geometria per funzione di dragdrop, passa la gemetria stessa
		this.addEvents("digitizeEndSubtract");		// Evento lanciato quando finisce la digitalizzazione di una geometria per funzione di subtract, passa la gemetria stessa
		this.addEvents("digitizeEndAddSub");		// Evento lanciato quando finisce la digitalizzazione di una geometria per funzione di addsub, passa la gemetria stessa

		
	    this.geoOpField = Ext.create('Ext.form.TextField', {
	    	name: 'geoOp'
	    });
	    							
	    this.geoCoordField = Ext.create('Ext.form.TextField', {
	    	name: 'geoCoord'
	    });
	    
	    this.geoCoordParts = Ext.create('Ext.form.TextField', {
	    	name: 'geoParts'
	    });
	    
	    this.geomPartsAllFlag = Ext.create('Ext.form.TextField', {
	    	name: 'geomPartsAllFlag'
	    });
	    
	    
	    							
		this.selectedListField = Ext.create('Ext.form.TextField', {
			name: 'selectedList'
		});
		
	    this.submitForm = Ext.create('Ext.form.FormPanel', {
			hidden: true,
			renderTo: Ext.getBody(),
			bodyStyle: 'position:absolute; top: 0px; left 0px;',
			standardSubmit: true,
			method: 'POST',
			items: [this.geoOpField, this.geoCoordField, this.geoCoordParts, this.geomPartsAllFlag, this.selectedListField]
	    });
		
		this.bindToViewerPanel();
		this.bindToButtonsPanel();
		this.bindToQueryPanel();
		this.bindToOLSPanel();
		this.bindToTOCPanel();
		this.bindToStylePanel();
		this.bindToContextMenu();
		this.bindToStatusPanel();
		
		// ///////////// //
		// QUERY BUILDER //
		// ///////////// //
		this.bindToQueryBuilderPanel();
		
		// ///////////// //
		// CODELESS FORM //
		// ///////////// //
		this.bindToCodeLessPanel();
		this.bindToViewCodeLessPanel();
		
		// TODO Collegare setCurrentLayer all'evento di cambio layer su combo
		
		// TODO Definire eventuali  eventi
		//this.addEvents('permittedOperationsChange'); // TODO Fire
	
		//this.callParent(arguments);
		
		
		// vecchia funzione initTolomeo inizio 
		// this.doOpenActions();
	   	
		//TODO NB: Questi eventi vengono intercettati con successo solo se il layout tolomeo che estende toloPanelBase è incluso in un pannello e non utilizzato direttamente. 
		// Questo perchè nel secondo caso quando arriva qua gli eventi sul viewer sono gia stati lanciati. 
	   	this.viewer.on('onAfterPreInit', function()   { this.applyCustomQuery(); }, this, {single: true});  //this.map.render(this.mapPanel.body.dom);},this,{single: true});
		this.viewer.on('onBeforePostInit', function() { this.doOpenActions(); this.doOpenActionsJS(); },  this, {single: true});  //this.map.render(this.mapPanel.body.dom);},this,{single: true});

	
		// le operazioni consentite potrebbero essere in funzione delle openActions (come per esempio in caso di editSingolo)
		this.togglePermittedOperations();	
		
		// Disegno la mappa
		//this.applyCustomQuery();
		
		// postinizializzazione viewer
		//if (this.viewer) this.viewer.pluginPostInit(this.paramsJS);
	
		// Inizializzazione eventuale legenda
		//if (this.viewer!=null) {
		//	if (this.TOCPanel!=null) {
		//		this.TOCPanel.showTOC(this.viewer.pluginGetCurrentZoom());
		//	}
		//}
		// vecchia funzione initTolomeo fine
		
		htmlInfoTolomeoWin = 
			"<center>" +
			"<b>Comune di Prato</b>" +
			"<br/>" +			
			"<b>Tolomeo versione " + TolomeoExt.Vars.TOLOMEOVersion + "</b>" +
			"</center>" +
			"<br/>" +
			"<b>Sito di riferimento</b><br/>" +
			"<a href=\"http://tolomeogis.comune.prato.it\" target=\"_blank\">http://tolomeogis.comune.prato.it</a>"+
			"<br/>" +
			"<br/>" +
			"<b>Librerie utilizzate</b><br/>" +
			"<b><i>Java</i></b><br/>" +
			"SIT core " + this.paramsJS.sitCoreVersion + "<br/>" +			
			"<b><i>Javascript</i></b><br/>" +
			"ExtJS " + Ext.getVersion() + "<br/>" +
			"OpenLayers " + OpenLayers.VERSION_NUMBER + "<br/>" +
			(((typeof Cesium !== "undefined")) ? "Cesium " + Cesium.VERSION + "<br/>" : "") +
			"<br/>" +
			"<b>Sviluppatori</b>" +
			"<br/>" +
			"Alessandro Radaelli" +
			"<br/>" +
			"Federico Nieri" +
			"<br/>" +
			"Mattia Gennari";
			
			//"<b>Copyright Ortofoto</b><br/>" +
			//"&copy; Copyright Regione Toscana - ofc anno 2010<br/>" +
			//"&copy; Copyright Regione Toscana - ofc 1:2.000 anno 2009<br/>" +
			//"&copy; Copyright Regione Toscana - ofc anno 2004 <br/>" +
			//"&copy; Copyright Regione Toscana - ofc anno 1998 <br/>";
			
			
		this.tolomeoInfoWin = new Ext.Window({
			
			title: 'Tolomeo',
			bodyStyle: 'padding: 0px',
			cls: 'clearCSS',
			width: 550,
			height: 450,			
			modal: true,
			closeAction: 'hide',
			constrain: true,
			layout: 'fit',
			buttons: [{
	        	text: ToloI18n.getMsg("ToloMapAPIExt.tolomeoInfoWin.btnChiudi.text"),
	       		listeners: {click: {fn: function() {
	       			this.tolomeoInfoWin.hide();
	       		},scope: this}}	       		
	        }],
	        items : {
	        	xtype : 'tabpanel',
	        	activeItem : 0,
	        	minTabWidth : 120,
    			tabWidth    : 135,
    			enableTabScroll: true,
    			border: false,
    			defaults: {
    				xtype: 'panel',
    				layout: 'fit',
    				autoScroll: true
    			},
    			items: [{
    				title: ToloI18n.getMsg("ToloMapAPIExt.tolomeoInfoWin.tabInfo.title"),
    				html: htmlInfoTolomeoWin,
    				bodyStyle: 'padding:10px;font-size:12px;line-height:150%',
    				frame: false,
    				border: false,
    				plain: true
    			},{
    				title: ToloI18n.getMsg("ToloMapAPIExt.tolomeoInfoWin.tabLicenza.title"),
    				autoScroll: false,    				
    				items: [{
						xtype : 'box',
						id : 'licenseTolomeo',						
						autoEl : {
							tag : 'iframe',
							style : 'border-width: 0px;font-size: 10px',
							src : this.TOLOMEOServer + this.TOLOMEOStaticRoot + '/html/license.html'
						}
					}]
					
    			}]
	        }
		});
		
		
		this.tocInfoField = new Ext.form.TextField({
	    	name: 'tocInfo'
	    });
	    							
	    this.paramsJSField = new Ext.form.TextField({
	    	name: 'paramsJS'
	    });
		
		this.nMappaField = new Ext.form.TextField({
			name: 'nMappa'
		});
		
		this.idxCategoriaBase = new Ext.form.TextField({
			name: 'idxCategoriaBase'
		});
		
		this.idxLayerBase = new Ext.form.TextField({
			name: 'idxLayerBase'
		});

	    this.exportForm = new Ext.form.FormPanel({
			hidden: true,
			renderTo: Ext.getBody(),
			//bodyStyle: 'position:absolute; top: 0px; left 0px;',
			standardSubmit: true,
			method: 'POST',
			defaults: {
				xtype: 'hiddenfield'
			},
			items: [{	
					name: 'tocInfo'
				},{
					name: 'paramsJS'
				},{
					name: 'nMappa'
				},{
					name: 'idxCategoriaBase'
				},{
					name: 'idxLayerBase'
			},{
					name: 'version'
			}]
	    });		
	    
	    Ext.onReady(function(){
	    
	    	var j2qRunner = new Ext.util.TaskRunner();				
			var j2qTask = j2qRunner.newTask({
				
			    run: function(){ 
			    	if(window.JSTOQGIS_INTERFACE){		
			    		j2qTask.stop();
		    			j2qTask.destroy();
		    			me.bindToJ2Q();	            
		    			me.fireEvent('j2qConnected');
			    	} 
			    }, 
			    interval: 250,
			    repeat: 40,
			    scope: this
			});		
			
			j2qTask.start();
				
	    });
		
	},
	
	bindToJ2Q: function(){
		
		this.QGisConnected = true;
		
		var j2qBtn = Ext.getCmp('qgisConnection');
		if(j2qBtn){
			j2qBtn.setVisible(true);
		}
		Ext.MessageBox.show({
			title: ToloI18n.getMsg("ToloMapAPIExt.QGisConnection.title"),
            msg: ToloI18n.getMsg("ToloMapAPIExt.QGisConnection.msg"), 
            width:300,				                
            animateTarget: j2qBtn?'qgisConnection':null,
            iconCls : 'iconConnected2QGis'
        });
        setTimeout(function(){
        	Ext.MessageBox.hide();				                 
        }, 2500);
        
	},
	
	/**
	  * Method: bindToTOCPanel
	  * 
	  */
	bindToTOCPanel: function() {
		
		if (this.TOCPanel!=null) {			
			
			if(this.TOCPanel.isTOCCreated){
				if(this.buttonsPanel) this.buttonsPanel.switchTocRelatedButtons(true,true);
				this.attributionsChange();
			} else {
				this.TOCPanel.on('tocCreate', function() { 
					if(this.buttonsPanel) this.buttonsPanel.switchTocRelatedButtons(true,true);
					this.attributionsChange();
				}, this);
			}
			
			this.relayEvents(this.TOCPanel,['tocGuiCreate']);
			this.TOCPanel.on('afterrender', function() { 
				if (this.viewer.isAlreadyDrawn) {
					this.TOCPanel.createOrUpdate(this.viewer.pluginGetCurrentZoom()); 
				}
			}, this);

			this.TOCPanel.on('layerOpacityChange', this.setLayerOpacity, this);
			this.TOCPanel.on('layerEnhanceChange', this.setLayerEnhance, this);
			
			
			this.TOCPanel.on('layerCheckedChange', function(layer){
				if(layer.checked) return;
				var removedGeoms = [];
				if(layer.codTPN && !this.TOCPanel.layerIsVisible(layer.codTPN)){
					removedGeoms.push(this.clearSelected(false,layer.codTPN));
					if(this.selezioneCorrente.size()>0){
						//this.fireEvent('onObjectSelect', this.selezioneCorrente.getByIndex(this.selezioneCorrente.size()-1));
						this.fireEvent('selectedObjectsRemoved',removedGeoms, this.selezioneCorrente.getByIndex(this.selezioneCorrente.size()-1));
					}
				}
			}, this);
			this.TOCPanel.on('categoryCheckedChange', function(category, tocInfo){				
				if(category.checked) return;
				var removedGeoms = [];
				
				tocInfo.onEachLayer(function(cat,lay,catIdx,layIdx){
					if(lay.codTPN && !this.TOCPanel.layerIsVisible(lay.codTPN)){
						removedGeoms.push(this.clearSelected(false,lay.codTPN));
					}
				},this,category.catTreeIdx,true);
				
				if(this.selezioneCorrente.size()>0){
					this.fireEvent('selectedObjectsRemoved',removedGeoms, this.selezioneCorrente.getByIndex(this.selezioneCorrente.size()-1));
				}
			}, this);
			
			this.on('onObjectSelect',function(geoms){
				var geom = geoms.geometries?geoms.geometries[0]:geoms;
				this.TOCPanel.setLayerVisibility(geom.codTPN);
			},this);	
									
			this.TOCPanel.on('stylePanelRequest', this.mostraStylePanel, this);

			this.TOCPanel.on('contextMenuZoomToExtent', this.TOCContextMenuZoomToExtent, this);
			
			this.TOCPanel.on('contextMenuShowInfo', this.TOCContextMenuShowInfo, this);
			this.TOCPanel.on('contextMenuZoomMaxScaleMax', this.TOCContextMenuZoomScaleMax, this);
			this.TOCPanel.on('contextMenuZoomMinScaleMin', this.TOCContextMenuZoomScaleMin, this);
			this.TOCPanel.on('contextMenuMetadataClick', this.showMetadata, this);
			
			
			this.TOCPanel.on('itemClicked', this.TOCItemClicked, this);		
			this.TOCPanel.on('exportForQgisClicked', function (catIdx, layIdx, version) { this.exportForQgis(catIdx, layIdx, version); }, this);
			
			
			this.on('j2qConnected',function(geoms){
				if(this.TOCPanel.isTOCCreated){
					this.TOCPanel.enableJ2QFeatures();			
				} else {
					this.TOCPanel.on('tocCreate', function() { 
						this.TOCPanel.enableJ2QFeatures();
					}, this);
				}
				
			},this);
			
			this.TOCPanel.on('addLayerToQgisClicked', this.addLayerToQGis, this);		
			
		} else {
			if(this.buttonsPanel) this.buttonsPanel.switchTocRelatedButtons(false,true);
		}
	},
	
	TOCContextMenuZoomToExtent: function (cat, lay, tocInfo) {
		
		var bbox = tocInfo.getBoundingBox(cat, lay) ;
		if (bbox!=null) this.zoomToExtent(bbox, 0);
			
	},
	
	TOCContextMenuShowInfo: function (cat, lay, tocInfo) {
		
		if (lay!=undefined && lay!=null) {
			var layInfo = tocInfo.getCategoryInfo(cat).layers[lay];
			var info = layInfo.layerAbstract;
			
			if (info && info!="") {
			
				var idwin = this.id+'-contextmenushowinfo'+Math.random();
				
				Ext.create('Ext.Window',{
					id: idwin,
					title: ToloI18n.getMsg("ToloMapAPIExt.TOCContextMenuShowInfo.win.title", {LAYDESCR: layInfo.descr}),
					layout: 'fit',
					//iconCls: 'iconPrint',
					frame: true,
					border: true,
					maximizable: true,
					autoScroll:  true,
					//monitorResize: true,
					//plain: true,
					modal: false,
					width: 300,
					height: 150,
					cls: 'clearCSS',
					html: layInfo.layerAbstract,
					buttonAlign: 'right',
					buttons: [{
			            text: ToloI18n.getMsg("ToloMapAPIExt.TOCContextMenuShowInfo.btnChiudi.text"),
			            width: 75,
			            listeners: { click: { fn: function() { Ext.getCmp(idwin).close(); } } }
			        	}]
				}).show();
			} else {
				Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.TOCContextMenuShowInfo.msgNoInfo.title"),
						ToloI18n.getMsg("ToloMapAPIExt.TOCContextMenuShowInfo.msgNoInfo.msg"));
			}
		} else {
			Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.TOCContextMenuShowInfo.msgNoInfo.title"),
					ToloI18n.getMsg("ToloMapAPIExt.TOCContextMenuShowInfo.msgNoInfo.msg"));
		}
			
	},

	TOCContextMenuZoomScaleMin: function(cat, lay, tocInfo, scalaminmax) {
		/*
		 *var layInfo = tocInfo.getCategoryInfo(cat).layers[lay];
		
		if (layInfo!=null) {
			// imposto un poco di margine per tenere conto che mapserver ha dpi interi quindi è impostato 91 invece di 90.74 e considero un poco di margine
			var s = layInfo.minScaleMin * 1.01;
			// Scelgo una scala arrontondata a 10 (meno brutta da vedere)
			s = Math.floor(s/10+1) * 10;
			this.zoomToScale(s);
		}*/
		
		var scala = scalaminmax.scalaMinima;
		if (scala!=null) {
			// imposto un poco di margine per tenere conto che mapserver ha dpi interi quindi è impostato 91 invece di 90.74 e considero un poco di margine
			var s = scala * 1.01;
			// Scelgo una scala arrontondata a 10 (meno brutta da vedere)
			s = Math.floor(s/10+1) * 10;
			this.zoomToScale(s);
		}
		
	},
	
	TOCContextMenuZoomScaleMax: function(cat, lay, tocInfo, scalaminmax) {
		/*
		var layInfo = tocInfo.getCategoryInfo(cat).layers[lay];
		
		if (layInfo!=null) {
			// imposto un poco di margine per tenere conto che mapserver ha dpi interi quindi è impostato 91 invece di 90.74 e considero un poco di margine
			var s = layInfo.maxScaleMax * 0.99;
			// Scelgo una scala arrontondata a 10 (meno brutta da vedere)
			s = Math.floor(s/10) * 10;
			this.zoomToScale(s);
		}
		*/
		
		var scala = scalaminmax.scalaMassima;
		if (scala!=null) {
			// imposto un poco di margine per tenere conto che mapserver ha dpi interi quindi è impostato 91 invece di 90.74 e considero un poco di margine
			var s = scala * 0.99;
			// Scelgo una scala arrontondata a 10 (meno brutta da vedere)
			s = Math.floor(s/10) * 10;
			this.zoomToScale(s);
		}
	},
	
	showMetadata: function(metadata) {
		
		var iframe = Ext.create('TolomeoExt.ToloIFrame', {
			frameName: 'metadata',
			url: metadata.url
		});
		Ext.create('Ext.Window', {
			layout: 'fit',
			title: ToloI18n.getMsg("ToloButtonPanelExt.mnuGuida.text", {TIPO:  metadata.type}),
			maximizable: true,
			constrain: true,
			width: 600,
			height: 500,
			items: [iframe]
		}).show();
		
	},
	
	TOCItemClicked: function(catTreeIdx, layTreeIdx, classi, e) {
	
		// TODO Supportata solo mappa 0
		var mappa = this.paramsJS.mappe.mappaList[0];
		
    	if (catTreeIdx && layTreeIdx && !classi ) {
	    	
    		var currCat = this.TOCPanel.tocInfo.getCategoryInfo(catTreeIdx);
			var currLayer =  currCat.layers[layTreeIdx];	
    		
			// Verifica se è definito un link e se l'elemento clickato è un anchor
	    	if (currLayer.clickUrl && e.target.nodeName.toUpperCase()=="A") {
	    		var params = "";
	        	params += (currLayer.codTPN) ? "codTPN=" + currLayer.codTPN : "";
	        	params += ((params!="") ? "&" : "") + "catIdx=" + encodeURIComponent(currLayer.catTreeIdx);  
	        	params += "&layIdx=" + encodeURIComponent(currLayer.layTreeIdx);
	       
	        	// {left: XXX, bottom: XXXXX, right: XXX, top: XXX};
	        	var extent = this.viewer.pluginGetMapExtent();
	        	// tolobbox=x1:x2:y1:y2 che sarebbe il box in coordinate , separate da carattere ":" della mappa nel momento in cui si cliccka su un layer.
	        	params += "&tolobbox=" + extent.left + ":" + extent.right + ":" + extent.bottom + ":" + extent.top;
	        	
	        	// tolosrid
				params += "&tolosrid=" + encodeURIComponent(this.paramsJS.mappe.SRID);
	       
	       		// tolozoom=xxxxx dove xxxxx è il denominatore: se la scala e' 1:12345  sarebbe ...&zoom=12345
				params += "&tolozoom=" + this.viewer.pluginGetCurrentZoom();
	       
				var currCatPreset = this.TOCPanel.tocInfo.getCategoryPresetInfo(catTreeIdx);
				var currLayerPreset =  currCatPreset.layerList[layTreeIdx];
				var server = this.paramsJS.getServer(currLayerPreset.serverID, mappa);
				
				if (server) {
					// tololayerserver
					params += "&tololayerserver=" + encodeURIComponent(server.url);
		       
					// tololayername
		       		params += "&tololayername=" + encodeURIComponent(currLayerPreset.name);
				}	
	        	var retVal = currLayer.clickUrl;
	        	if (params!="") {
	        		retVal += ((retVal.indexOf("?")==-1) ? "?" : "&")  + params ;	
	        	} 
	     		
	        	this.openURL(retVal, currLayer.clickTarget);
	    	}
    	} else if ( catTreeIdx && layTreeIdx==null && !classi){
    		var currCat = this.TOCPanel.tocInfo.getCategoryInfo(catTreeIdx);
			
	    	if (currCat.clickUrl) {
	    		var params = "";
	        	params += "catIdx=" + encodeURIComponent(currCat.catTreeIdx);  
	        	
	        	// {left: XXX, bottom: XXXXX, right: XXX, top: XXX};
	        	var extent = this.viewer.pluginGetMapExtent();
	        	// tolobbox=x1:x2:y1:y2 che sarebbe il box in coordinate , separate da carattere ":" della mappa nel momento in cui si cliccka su un layer.
	        	params += "&tolobbox=" + extent.left + ":" + extent.right + ":" + extent.bottom + ":" + extent.top;
	        	
	        	// tolosrid
				params += "&tolosrid=" + encodeURIComponent(this.paramsJS.mappe.SRID);
	       
	       		// tolozoom=xxxxx dove xxxxx è il denominatore: se la scala e' 1:12345  sarebbe ...&zoom=12345
				params += "&tolozoom=" + this.viewer.pluginGetCurrentZoom();
	       
	        	var retVal = currCat.clickUrl;
	        	if (params!="") {
	        		retVal += ((retVal.indexOf("?")==-1) ? "?" : "&")  + params ;	
	        	} 
	     		
	        	this.openURL(retVal, currCat.clickTarget);
	    	}
    	}
		
	},
	
	/**
	 * Mostra il pannello di gestione degli stili
	 * 
	 * @param {String} nome del layer
	 * @param {Number} numero categoria del layer 
	 * @param {Number} numero del layer
	 * @param {String[][]} Elenco degli stili definiti per il layer nella forma [['stile1'],['stile2']] 
	 */ 
	 mostraStylePanel: function(layer, cat, lay, definedStyles) {
		
		if (this.stylePanel) {
			this.stylePanel.mostra(layer, cat, lay, definedStyles);	
		}
		
	},
	
	/**
	 * Nasconde il pannello di gestione degli stili
	 * 
	 */
	nascondiStylePanel: function() {
		
		if (this.stylePanel) this.stylePanel.nascondi();
		
	},
	
	/**
	 * Method: setLayerOpacity
	 * Metodo per impostare l'opacità del layer. 
	 *
	 * Parameters:
	 * nomeLayer - {} il nome del layer.
	 * newValue - {} il nuovo valore.
	 */
	setLayerOpacity: function (catIdx, layIdx, newValue, oldValue, tocInfo  ) {
		 
		 
		// Se il layer era totalmente opaco o se lo diventa occorre ricalcolare i raggruppamenti
		if ((newValue!=1 && oldValue==1) ||
			(oldValue!=1 && newValue==1)) {
	
			// Ricrea la mapDefinition a partire dalle info di tocInfo (quindi con nuovo valore di trasparenza)
			this.viewer.pluginRemoveAllLayers();
			this.paramsJS.createMapDefinition(tocInfo);
			this.viewer.pluginAddAllMaps();
		} else {
			// ... altrimenti è solo un cambio di opacità senza ricalcolo gruppo
			// TODO gestisce solo mappa 0
			var layAgg = this.paramsJS.mapDefinitions[0].whichLayerAggregationContains(catIdx, layIdx);
			layAgg.opacity = newValue;
			this.viewer.pluginSetLayerOpacity(layAgg.nPluginLayer, newValue);
		} 
			
		if (this.viewer3D) {
			this.viewer3D.removeAllLayers();
			this.viewer3D.addAllLayers();	
		} 
	},
	
	/**
	 * @method setLayerEnhance
	 * Metodo per impostare il miglioramento immagine del layer. 
	 *
	 * Parameters:
	 * @param effect 
	 * @param catIdx
	 * @param layIdx
	 * @param newValue
	 * @param oldValue
	 * @param tocInfo
	 * 
	 */
	setLayerEnhance: function (effect, catIdx, layIdx, newValue, oldValue, tocInfo  ) {
		 
		 
		// Se il layer era in uno stato diverso dal default o lo diventa o se lo diventa occorre ricalcolare i raggruppamenti
		if ((newValue!=effect.defaultValue && oldValue==effect.defaultValue) ||
			(oldValue!=effect.defaultValue && newValue==effect.defaultValue)) {
	
			// Ricrea la mapDefinition a partire dalle info di tocInfo (quindi con nuovo valore di trasparenza)
			this.viewer.pluginRemoveAllLayers();
			this.paramsJS.createMapDefinition(tocInfo);
			this.viewer.pluginAddAllMaps();
		} else {
			var layAgg = this.paramsJS.mapDefinitions[0].whichLayerAggregationContains(catIdx, layIdx);
			layAgg[effect.key] = newValue;
			this.viewer.pluginSetLayerEnhance(effect, layAgg.nPluginLayer, newValue);
		} 
		if (this.viewer3D) {
			this.viewer3D.removeAllLayers();
			this.viewer3D.addAllLayers();	
		} 
	},
	
	

 	/**
     * Method: bindToQueryPanel
     * 
     */
	bindToQueryPanel: function() {

		if (this.queryPanel!=null) {
			this.queryPanel.on('queryMultipleResultHoverStart', this.addHighlighted, this);
			this.queryPanel.on('queryMultipleResultOut', this.clearHighLigthed, this);
			
			this.queryPanel.on('geomFilterFieldCreated', this.queryPanelSetGeomField, this);
			
			this.queryPanel.on(
				'querySelected',
				function(geoms) { 
					var geom = geoms.geometries[0]; 						
					var selectable = this.paramsJS.isSelectable(geom.codTPN);
					
					//senza redraw perchè faceva chiamate fantasma.
					if(selectable){
						this.clearSelected(false); 
						this.addSelected(geom); 
						this.zoomToSelected(null,200);																	
					}else{						
						this.clearHighLigthed(false);
						this.addHighlighted(geom); 
						this.zoomToHighlighted(null,200);
					}
					
					if(this.QGisConnected){						
						JQ.AddWKTLayer(geom.geometry,geom.description,this.getProjectionCode(),{}, false);			
						var bbox = selectable ? this.viewer.pluginGetSelectedFeaturesBounds(200) : this.viewer.pluginGetHighlightedFeaturesBounds(200);
						var epsg = this.getProjectionCode().substring(5);
						
						setTimeout(function(){
				        	// Ext.Msg.alert('Faccio lo zoom','EPSG = ' + epsg + '<br>QGis version = ' + JQ.GetQgisVersion());
							JQ.ZoomToExtent(bbox.left,bbox.bottom,bbox.right,bbox.top,epsg);				                 
				        }, 2000);
        						
					}
					
					/*
					var selLayer = this.getCurrentSelectLayer();
					if (selLayer!=null) {
						if (selLayer.azioniEventiVis.autoVisOnSelect) {
							var layer = this.paramsJS.getParamJSLayer(geoms.geometries[0].codTPN);
							//this.setCurrentSelectLayer(geoms.geometries[0].codTPN);
							this.identify(layer);
						}
					}
					*/
				}, 
				this
			);
		}
	},

	/**
	 * Method: bindToOLSPanel
	 * 
	 */
	bindToOLSPanel: function() {
		if (this.olsPanel!=null) {
			this.olsPanel.on("addressSelected", function(street, lon, lat, crs, zoomLevel) {
				this.gotoPosition(lon, lat, zoomLevel, false, crs);
				var htmlText = "<div style='font: 11px tahoma,arial,helvetica,sans-serif;'><b><u>" + street + "</u></b><br></div>";
				this.addPopup(lon, lat, htmlText, false, false);
			}, this);
			this.olsPanel.on("startAddressSelected", function(street, lon, lat, crs, zoomLevel) {
				this.addStartRoutingMarker(lon, lat, crs);
				this.gotoPosition(lon, lat, zoomLevel, false, crs);
			}, this);
			this.olsPanel.on("endAddressSelected", function(street, lon, lat, crs, zoomLevel) {
				this.addEndRoutingMarker(lon, lat, crs);
				this.gotoPosition(lon, lat, zoomLevel, false, crs);
			}, this);
			this.olsPanel.on("viaAddressAdded", function(viaId, street, lon, lat, crs, zoomLevel) {
				this.addViaRoutingMarker(viaId, lon, lat, crs);
				this.gotoPosition(lon, lat, zoomLevel, false, crs);
			}, this);
			this.olsPanel.on("routeReceived", function(routeResponse) {
				this.addRouting(routeResponse, false);
					this.zoomToRouting();
			}, this);
			this.olsPanel.on("navigationSelected", function(instruction, crs, zoomLevel) {
				this.viewer.pluginZoomToInstruction(instruction.geometry, zoomLevel);
			}, this);
			this.olsPanel.on("navigationFocus", function(instruction, crs) {
				//this.addHighlighted(instruction.geometry, false);
				this.viewer.pluginRoutingInstructionHighlight(instruction, true);
			}, this);
			this.olsPanel.on("navigationBlur", function(instruction, crs) {
				//this.clearHighLigthed(false);
				this.viewer.pluginRoutingInstructionHighlight(instruction, false);
			}, this);
			this.olsPanel.on("viaPointSelected", function(viaId, street, lon, lat, crs, zoomLevel) {
				this.gotoPosition(lon, lat, zoomLevel, false, crs);
			}, this);
			this.olsPanel.on("viaPointsChanged", function(viaPoints) {
				this.setViaRoutingMarkers(viaPoints, this.paramsJS.mappe.SRID);
			}, this);
				this.olsPanel.on("reset", function() {
				this.clearRouting();
				this.viewer.pluginClearStartRoutingMarker();
				this.viewer.pluginClearEndRoutingMarker();
				this.viewer.pluginClearViaRoutingMarkers();
			}, this);
			
			if (this.viewer) {
				this.viewer.on('startPointMoved', this.olsPointMoved, this);
				this.viewer.on('endPointMoved', this.olsPointMoved, this);
				this.viewer.on('viaPointMoved', this.olsPointMoved, this);
				
				this.viewer.on('routingInformationSelect', 
							function(instructionId) {
								this.olsPanel.routingInformationSelect(instructionId);
							}, this);
				this.viewer.on('routingInformationUnSelect', 
							function(instructionId) {
								this.olsPanel.routingInformationDeSelect(instructionId);
							}, this);
				
			}
		}
	},
	
	/**
	 * Method: olsPointMoved
	 * 
	 */
	olsPointMoved: function(startPoint, endPoint, viaPoint, viaId) {
	    if (startPoint) this.olsPanel.setStartAddress(startPoint, this.paramsJS.mappe.SRID);
	    if (endPoint) this.olsPanel.setEndAddress(endPoint, this.paramsJS.mappe.SRID);
	    if (viaPoint) this.olsPanel.moveViaAddress(viaId, viaPoint, this.paramsJS.mappe.SRID);
	},
		
	/**
	 * Method: olsPointSelected
	 * 
	*/
	olsPointSelected: function(posX, posY, type) {	
		var viewPos = this.getViewerPosition(); 
		var coordinate = this.viewer.pluginGetCoordinateFromPixel({x:(posX-viewPos.x),y:(posY-viewPos.y)}); 
		var currPoint = new Point(coordinate.x,coordinate.y);
		
		switch (type) {
			case 'REVERSE':
			    this.olsPanel.reverseGeocode(currPoint, this.paramsJS.mappe.SRID);
				break;
			case 'START':
				this.addStartRoutingMarker(coordinate.x, coordinate.y, this.paramsJS.mappe.SRID);
			    this.olsPanel.setStartAddress(currPoint, this.paramsJS.mappe.SRID);
				break;
			case 'END':
				this.addEndRoutingMarker(coordinate.x, coordinate.y, this.paramsJS.mappe.SRID);
			    this.olsPanel.setEndAddress(currPoint, this.paramsJS.mappe.SRID);
				break;
			case 'VIA':
			    var viaId = this.olsPanel.addViaAddress(currPoint, this.paramsJS.mappe.SRID);
				this.addViaRoutingMarker(viaId, coordinate.x, coordinate.y, this.paramsJS.mappe.SRID);
				break;
		}
	},
	
	queryPanelSetGeomField: function() {
		var box = this.viewer.pluginGetMapExtent();
		var p1 = "" +  box.left + " " + box.top;
		var p2 = "" +  box.left + " " + box.bottom;
		var p3 = "" +  box.right + " " + box.bottom;
		var p4 = "" +  box.right + " " + box.top;
		
		var geom = "POLYGON((" + p1 + "," + p2 + "," + p3 + "," + p4 + "," + p1+ "))";
		this.queryPanel.setGeomFilterField(geom);
	},
	
	
	/**
	 * Method: bindToViewerPanel
	 * 
	 */
	bindToViewerPanel: function() {
		
		if (this.viewer!=null) {
				
			// Sarebbe più corretto gestire il selectRequestStart, ma è poco reattivo perché l'esecuzione della richiesta prende tempo 
			this.on('selectRequestBeforeStart', function(){				
				//this.viewer.pluginOnRequestStart();		
				Ext.each(this.viewer.getEl().query('div'),function(e){Ext.fly(e).addCls('cursorProgress');});
			}, this);
			
			this.on('selectRequestEnd', function(){
				//this.viewer.pluginOnRequestEnd();				
				Ext.each(this.viewer.getEl().query('div'),function(e){Ext.fly(e).removeCls('cursorProgress');});
			}, this);
			
			if (this.TOCPanel!=null) {
				var viewer = this.viewer;
				var api = this;
				//this.viewer.on('scalechange', function(scale) { this.requestVisibleData(scale); }, this.TOCPanel);
				this.viewer.on('scalechange', function(scale) { 
					this.createOrUpdate(scale, undefined, api.viewer.pluginGetObjPos()); 
				}, this.TOCPanel);			
				
				this.viewer.on('onMapMoveEnd', function(scale) { 
					this.createOrUpdate(api.viewer.pluginGetCurrentZoom(), undefined, api.viewer.pluginGetObjPos()); 
				}, this.TOCPanel);			
					
				if (this.queryPanel) {
					this.viewer.on('onMapMoveEnd', this.queryPanelSetGeomField, this);
				}
				
				//loadend
				this.viewer.addListener('onAfterPostInit', function() {
					this.TOCPanel.createOrUpdate(this.viewer.pluginGetCurrentZoom(), false, this.viewer.pluginGetObjPos());
				}, this, { single: true });
													
				//TODO ma quando arriva qua probabilmente afterlayout lo ha già fatto!!!
				//this.viewer.on('afterlayout', function() { api.TOCPanel.showTOC(viewer.pluginGetCurrentZoom()); }, this.TOCPanel, {single:true});
		
				this.TOCPanel.on('layerCheckedChange', this.onLegendaCheckLayerChange, this);
				this.TOCPanel.on('categoryCheckedChange', this.onLegendaCheckCategoryChange, this);
				this.TOCPanel.on('layerOrderChanged', this.onLegendaOrderChange, this);
				this.TOCPanel.on('layerStyleChanged', this.onLegendaStyleChange, this);
				
				this.TOCPanel.on('categoryAdded', this.onLegendaCategoryAdded, this);
				this.TOCPanel.on('layerAdded', this.onLegendaLayerAdded, this);
				
				this.TOCPanel.on('contextMenuAddWMSFromCatalogClick', this.addWMSFromCatalogChoose, this);
				this.TOCPanel.on('contextMenuAddWMSFromWMSWidgetClick', this.addWMSFromWMSWidgetChoose, this);
				
				
			}
						
			this.viewer.on('onAutoIdentify', this.onAutoIdentify, this);
			this.viewer.on('onAutoIdentifyCancel', this.onAutoIdentifyCancel, this);
			
			// Registrazione in viewerPanel
			//this.viewer.on('onMeasureStart', this.onMeasureCallback, this);
			//this.viewer.on('onMeasureStop', this.onMeasureCallback, this);
			//this.viewer.on('onMeasureChanging', this.onMeasureCallback, this);
			//this.viewer.on('onMeasureChanged', this.onMeasureCallback, this);
			//this.viewer.on('onMeasureClear', this.onMeasureCallback, this);
			
			this.viewer.on('onDigitizeEndPoint',         this.onDigitizeEndPoint, this);
			this.viewer.on('onDigitizeEndLine',          this.onDigitizeEndLine, this);
			this.viewer.on('onDigitizeEndPolygon',       this.onDigitizeEndPolygon, this);
			this.viewer.on('onDigitizeEndVertexEditing', this.onDigitizeEndVertexEditing, this);
			this.viewer.on('onDigitizeEndDragDrop',      this.onDigitizeEndDragDrop, this);
			//this.viewer.on('onMappaSelect',              function (point,selectionMode,addToSelected,visualize) {this.onMappaSelect(point,selectionMode,addToSelected,visualize); }, this );
			this.viewer.on('onMappaSelect',              this.onMappaSelect, this );
			this.viewer.on('onPrintMap',                 this.showPrintWindow,  this );
			//this.viewer.on('onCoordinateChange',		 this.showCoordinate, this)
			
			this.viewer.on('onTimeMachineHide', function() {this.buttonsPanel.buttonToggle(TolomeoExt.ToloAPIOpCodes.btnTimeMachine, null, true)},  this );			
			this.viewer.on('popupClicked', this.updateNoteOnMap,  this );
			this.viewer.on('previousNavigationStateAvailable',		function(){this.fireEvent('previousNavigationStateAvailable')}, this);
			this.viewer.on('previousNavigationStateNotAvailable',	function(){this.fireEvent('previousNavigationStateNotAvailable')}, this);
			this.viewer.on('nextNavigationStateAvailable',			function(){this.fireEvent('nextNavigationStateAvailable')}, this);
			this.viewer.on('nextNavigationStateNotAvailable',		function(){this.fireEvent('nextNavigationStateNotAvailable')}, this);
			
			if (this.statusPanel) {
				//id: id+"-statusPanelCoordPnl",
												
				this.statusPanel.add({
					text: '',
					id: 'qgisConnection',
					hidden : true,
	    			iconCls : 'iconConnected2QGis',
					cls : "clearCss",
					listeners: { 								
						click: {
							fn: function() {     	    								
								Ext.MessageBox.show({
					    			title: ToloI18n.getMsg("ToloMapAPIExt.QGisConnection.click.title"),
					                msg: ToloI18n.getMsg("ToloMapAPIExt.QGisConnection.click.msg"),				                
					                width:300,				                
					                animateTarget: 'qgisConnection',
					                iconCls : 'iconConnected2QGis'
					            });																										
							},
							scope: this
			    		}
					},
					tooltip: {text: ToloI18n.getMsg("ToloMapAPIExt.QGisConnection.tootip.text"), title: ToloI18n.getMsg("ToloMapAPIExt.QGisConnection.tootip.title")}					
				});
				
				this.statusPanel.add('-');				
				
				var coordPnl = new Ext.Toolbar.TextItem({ cls: 'x-status-text-panel'});			
				var bFatto=false;
				// Dovuto sovrascrivere il metodo facendo in modo che updateLAyout fosse lanciato una volta solo perchè altrimenti era lentissimo
				coordPnl.setText= function(text) {
			        var me = coordPnl;
			        me.text = text;
			        if (me.rendered) {
			            me.el.update(text);
			            if (!bFatto) {
			            	bFatto=true;
			            	me.updateLayout();
			            }
			        }
			    }
				this.statusPanel.add(coordPnl);
				
				this.viewer.on('onMouseCoordChange', 
						function(coords,units,srid){
							var nDec = (units=='degrees' || units=='dd') ? 4 : 0;
							
							var x = this.addThousandSeparator(coords.x.toFixed(nDec), '.', ',');
							var y = this.addThousandSeparator(coords.y.toFixed(nDec), '.', ',');
							
							var msg = (srid ? srid + '&nbsp;&nbsp;' : '') + "X: " + x+'&nbsp;&nbsp;'+"Y: " + y;
							
							coordPnl.setText(msg);
							} , this);
							//this.statusPanel.find("id",this.id+"-statusPanelCoordPnl")[0].setText("X: " + x+'&nbsp;&nbsp;'+"Y: " + y);} , this);
			}
			
			
		}
	},

	
	/*
	 * addPointCatIdx: (addPointCatIdx) ? addPointCatIdx : "0",
	 							addPointLayIdx: (addPointLayIdx) ? addPointLayIdx : undefined,
	 							bBefore: bBefore
	 */
	
	addWMSFromWMSWidgetChoose: function(addPointCatIdx, addPointLayIdx, bBefore) {
		
		this.wmsExplorerWidgetActivate(addPointCatIdx, addPointLayIdx, bBefore);
		
	},
	
	addWMSFromCatalogChoose: function(addPointCatIdx, addPointLayIdx, bBefore) {
		
		this.cswWidgetActivate(addPointCatIdx, addPointLayIdx, bBefore);
		
	},
	
	addThousandSeparator: function (str, thousandSeparator, decimalSeparator) {
		var sRegExp = new RegExp('(-?[0-9]+)([0-9]{3})'),
		sValue = str + "", // to be sure we are dealing with a string
		arrNum = [];

		if (thousandSeparator === undefined) {thousandSeparator = ","; }
		if (decimalSeparator === undefined) {decimalSeparator = "."; }

		arrNum = sValue.split(/\.|,/);
		// let's be focused first only on the integer part
		sValue = arrNum[0];

		while(sRegExp.test(sValue)) {
		sValue = sValue.replace(sRegExp, '$1' + thousandSeparator + '$2');
		}

		// time to add back the decimal part
		if (arrNum.length > 1) {
		sValue = sValue + decimalSeparator + arrNum[1];
		}
		return sValue;
	},
	
	onLegendaCheckLayerChange: function(layerInfo) {
		
		if (layerInfo.itemType=='layer') {
			//this.attributionsChange();
			// TODO gestita solo mappa 0
			var nMappa = 0;
			
			var layerAggreg = this.paramsJS.updateMapDefinitionLayerCheckState(nMappa, layerInfo);		
			var layAndStyle = this.paramsJS.getLayerAggregLayersAndStylesStrings(nMappa, layerAggreg.nPluginLayer, this.layerStringSeparator,this.viewer.pluginGetCurrentZoom()); 
			this.viewer.pluginRefreshMap(layAndStyle.layers, layAndStyle.stili, this.layerStringSeparator, layerAggreg.nPluginLayer);
			this.viewer.pluginRefreshAttribution(layAndStyle.attribution, layerAggreg.nPluginLayer);	
			if (this.viewer3D) {
				this.viewer3D.refreshMap(layerAggreg.nPluginLayer);	
			}
		}
	},
	
	onLegendaCheckCategoryChange: function(catInfo, tocInfo) {
		// TODO gestita solo mappa 0
		var nMappa = 0;
		
		var layerAggregArray = this.paramsJS.updateMapDefinitionCategoryCheckState(nMappa, tocInfo, catInfo);
		for (var i=0; i<layerAggregArray.length; i++) {
			var layerAggreg = layerAggregArray[i];
			var layAndStyle = this.paramsJS.getLayerAggregLayersAndStylesStrings(nMappa, layerAggreg.nPluginLayer, this.layerStringSeparator, this.viewer.pluginGetCurrentZoom()); 
			this.viewer.pluginRefreshMap(layAndStyle.layers, layAndStyle.stili, this.layerStringSeparator, layerAggreg.nPluginLayer);
			this.viewer.pluginRefreshAttribution(layAndStyle.attribution, layerAggreg.nPluginLayer);
			if (this.viewer3D) {
				this.viewer3D.refreshMap(layerAggreg.nPluginLayer);	
			}
		}
	},

	onLegendaOrderChange: function(tocInfo) {
		
		// Ricrea la mapDefinition a partire dalle info di tocInfo (quindi con il nuovo ordine
		this.viewer.pluginRemoveAllLayers();
		this.paramsJS.createMapDefinition(tocInfo);
		this.viewer.pluginAddAllMaps();
		if (this.viewer3D) {
			this.viewer3D.removeAllLayers();
			this.viewer3D.addAllLayers();	
		}
		
	},
	
	//TODO verificare se serve sempre ed eventualmente eliminare
	onLegendaStyleChange: function(layerInfo, style) {
		
		// TODO gestita solo mappa 0
		var nMappa = 0;
		
		var layAggr = this.paramsJS.updateMapDefinitionLayerStyle(nMappa, layerInfo, style); 		
		var layAndStyle = this.paramsJS.getLayerAggregLayersAndStylesStrings(nMappa, layAggr.nPluginLayer, this.layerStringSeparator,this.viewer.pluginGetCurrentZoom()); 
		this.viewer.pluginRefreshMap(layAndStyle.layers, layAndStyle.stili, this.layerStringSeparator, layAggr.nPluginLayer);	
		if (this.viewer3D) {
			this.viewer3D.refreshMap(layerAggreg.nPluginLayer);	
		}

	},
	
	/**
	 * @method attributionsChange Aggiorna tutte le attribuzioni. Attualmente gestita solo mappa numero 0  
	 * 
	 * @param {Object} layerInfo Informazioni del layer, contenenti la nuova attribuzione
	 */
	attributionsChange: function() {
		
		// TODO gestita solo mappa 0
		var nMappa = 0;
		this.paramsJS.updateMapDefinitionAttributions(nMappa, this.TOCPanel.tocInfo);
		
		for (var i=0; i<this.paramsJS.mapDefinitions[nMappa].getLayerAggregationCount(); i++) {
			var layAggr = this.paramsJS.mapDefinitions[nMappa].getLayerAggregation(i);
			var layAndStyle = this.paramsJS.getLayerAggregLayersAndStylesStrings(nMappa, layAggr.nPluginLayer, this.layerStringSeparator,this.viewer.pluginGetCurrentZoom()); 
			this.viewer.pluginRefreshAttribution(layAndStyle.attribution, layAggr.nPluginLayer);	
			if (this.viewer3D) {
				this.viewer3D.refreshMap(layerAggreg.nPluginLayer);	
			}
		}

	},
	
	onLegendaCategoryAdded: function(tocInfo, catInfo) {
		
		// TODO Si può ottimizzare? Così ricalcolo tutto...
		// Se si assume che venga inserita una categoria vuota basterebbe (forse) reindicizzare le layerAggregations
		this.viewer.pluginRemoveAllLayers();
		this.paramsJS.createMapDefinition(tocInfo);
		this.viewer.pluginAddAllMaps();

		if (this.viewer3D) {
			this.viewer3D.removeAllLayers();
			this.viewer3D.addAllLayers();	
		}
		
	},
			
	onLegendaLayerAdded: function(tocInfo, paramsLayInfo, layTocInfo, serverInfo) {
		//TODO ALE
		this.buttonsPanel.a(this.paramsJS.azioniEventi.eventiLayerList);
		this.togglePermittedOperations();
		
		// TODO Si può ottimizzare? Così ricalcolo tutto... tenere presente che il layer aggiunto non è presente negli attuali raggruppamenti
		this.viewer.pluginRemoveAllLayers();
		this.paramsJS.createMapDefinition(tocInfo);
		this.viewer.pluginAddAllMaps();
		if (this.viewer3D) {
			this.viewer3D.removeAllLayers();
			this.viewer3D.addAllLayers();	
		}
		
	},
	
	bindToStylePanel: function() {
		
		if (this.stylePanel) {
			//styleApply: { fn: this.setLayerStyle, scope: this }
			if (this.TOCPanel) {
				this.stylePanel.on('styleApply', this.TOCPanel.setLayerStyle, this.TOCPanel); 
			}
		}
	},
	
	 /**
	    * Method: bindToButtonsPanel
	    * 
	    */
	bindToButtonsPanel: function() {
		// Bind con eventi buttonPanel (se presente
		if (this.buttonsPanel) {
			this.buttonsPanel.on('onTimeMachinePressFn', function() { this.viewer.pluginShowTimeMachine(true); }, this);
			this.buttonsPanel.on('onTimeMachineReleaseFn', function() { this.viewer.pluginShowTimeMachine(false); }, this);
			
			
			this.buttonsPanel.on('onPanPressFn', this.viewer.pluginToolSelectPan, this.viewer);
			this.buttonsPanel.on('onPanReleaseFn', this.viewer.pluginToolSelectPanStop, this.viewer);
			
			this.buttonsPanel.on('onCustomButtonPressFn',   function (btn) { this.onCustomButtonPress(btn)  }, this);
			this.buttonsPanel.on('onCustomButtonReleaseFn', function (btn) { this.onCustomButtonRelease(btn)}, this);
			
			this.buttonsPanel.on('onNuovoPressFn',   function (button) {
				switch(button.newType){
					case 0:
						this.onDigitizeStart(this.digitizeOperationInsert);
					break;
					case 1:
						this.onDigitizeByCADStart(this.digitizeOperationInsert);
					break;
				} 
			}, this );
			this.buttonsPanel.on('onNuovoReleaseFn', function (button) {
				switch(button.newType){
					case 0: 
						this.onDigitizeStop(this.digitizeOperationInsert);
					break;
					case 1:
						this.onDigitizeByCADStop(this.digitizeOperationInsert);
					break;		
				}			  
			}, this );
			
			this.buttonsPanel.on('onNuovoDaLayerPressFn', this.nuovoDaLayerStart, this );
			this.buttonsPanel.on('onNuovoDaLayerReleaseFn', this.nuovoDaLayerStop, this );
			
			this.buttonsPanel.on('onNuovoDaImportPressFn', this.nuovoDaImportStart, this );
			this.buttonsPanel.on('onNuovoDaImportReleaseFn', this.nuovoDaImportStop, this );
			
			this.buttonsPanel.on('onPanNordPressFn',  function () { this.panNord(null);  }, this);
			this.buttonsPanel.on('onPanSudPressFn',   function () { this.panSud(null);   }, this);
			this.buttonsPanel.on('onPanOvestPressFn', function () { this.panOvest(null); }, this);
			this.buttonsPanel.on('onPanEstPressFn',   function () { this.panEst(null);   }, this);		

			this.buttonsPanel.on('onHistoryNextPressFn',   this.historyNext, this);		
			this.buttonsPanel.on('onHistoryPrevPressFn',   this.historyPrev, this);	
			
			this.buttonsPanel.on('onZoomInPressFn',  this.viewer.pluginToolSelectZoomIn,  this.viewer);
			this.buttonsPanel.on('onZoomOutPressFn', this.viewer.pluginToolSelectZoomOut, this.viewer);
			this.buttonsPanel.on('onZoomBoxPressFn', this.viewer.pluginToolSelectZoomBoxActivate, this.viewer);
			this.buttonsPanel.on('onZoomBoxReleaseFn', this.viewer.pluginToolSelectZoomBoxDeactivate, this.viewer);			
			this.buttonsPanel.on('onZoomAllPressFn', this.viewer.pluginToolSelectZoomAll, this.viewer);
		    
		    // TODO DA TRASFERIRE su viewer? Non sono coinvolti metodi dell'API
			this.buttonsPanel.on('onMeasureActivate',   function (type) { this.viewer.pluginMeasureToolSelect(type); }, this);
			this.buttonsPanel.on('onMeasureDeactivate', this.viewer.pluginMeasureStop, this.viewer);
			this.buttonsPanel.on('onMeasureTypeChange', function (type) { this.viewer.pluginMeasureStop(); this.viewer.pluginMeasureToolSelect(type); }, this );
		    
			this.buttonsPanel.on('onPrintPressFn', this.viewer.pluginToolSelectPrint, this.viewer);
		    
		    //TODO
			if (this.TOCPanel!=null)   this.buttonsPanel.on('onLegendPressFn',   function() { this.TOCPanel.showTOC(this.viewer.pluginGetCurrentZoom()); }, this);
			if (this.TOCPanel!=null)   this.buttonsPanel.on('onLegendReleaseFn', this.TOCPanel.hideTOC, this.TOCPanel);
			if (this.queryPanel!=null) this.buttonsPanel.on('onQueryPressFn',    this.queryPanel.showQuery, this.queryPanel);
			if (this.queryPanel!=null) this.buttonsPanel.on('onQueryReleaseFn',  this.// TODO STILI
					queryPanel.hideQuery, this.queryPanel);
		    
			this.buttonsPanel.on('onSelectPressFn',   this.viewer.pluginToolSelectSelect,     this.viewer);
		    this.buttonsPanel.on('onSelectReleaseFn', this.viewer.pluginToolSelectSelectStop, this.viewer);
			this.buttonsPanel.on('onIdentifyPressFn', this.onIdentify,                        this);
		    //this.buttonsPanel.on('onIdentifyReleaseFn', this);
		    
			this.buttonsPanel.on('onDeletePressFn',     this.onDelete,     this);
			this.buttonsPanel.on('onUpdateAlfaPressFn', this.onUpdateAlfa, this);
			this.buttonsPanel.on('onAddPressFn',        function () { this.onDigitizeStart(this.digitizeOperationAdd); }, this);
		    
			this.buttonsPanel.on('onAddReleaseFn',        function () { this.onDigitizeStop(this.digitizeOperationAdd);         }, this);
			this.buttonsPanel.on('onSubtractPressFn',     function () { this.onDigitizeStart(this.digitizeOperationSubtract);   }, this);
			this.buttonsPanel.on('onSubtractReleaseFn',   function () { this.onDigitizeStop(this.digitizeOperationSubtract);    }, this);
			this.buttonsPanel.on('onAddSubPressFn',       function () { this.onDigitizeStart(this.digitizeOperationAddSub);     }, this);
			this.buttonsPanel.on('onAddSubReleaseFn',     function () { this.onDigitizeStop(this.digitizeOperationAddSub);      }, this);
			this.buttonsPanel.on('onVertexEditPressFn',   function () { this.onDigitizeStart(this.digitizeOperationVertexEdit); }, this);
			this.buttonsPanel.on('onVertexEditReleaseFn', function () { this.onDigitizeStop(this.digitizeOperationVertexEdit);  }, this);
			this.buttonsPanel.on('onDragDropPressFn',     function () { this.onDigitizeStart(this.digitizeOperationDragDrop);   }, this);
		    
			this.buttonsPanel.on('onDragDropReleaseFn',       function () { this.onDigitizeStop(this.digitizeOperationDragDrop); } , this);
			this.buttonsPanel.on('onAnnullaSelezioniPressFn', this.onAnnullaSelezioni, this);
		    
			this.buttonsPanel.on('onTemporalFilterApply', this.temporalFilterApply, this);
			
			this.buttonsPanel.on('onAutoIdentifyPressFn',   function () {this.autoIdentifyEnable(true); }, this);
			this.buttonsPanel.on('onAutoIdentifyReleaseFn', function () {this.autoIdentifyEnable(false); }, this);			
			this.buttonsPanel.on('onSelectLayer',           function (codTPN) { this.setCurrentSelectLayer(codTPN); }, this);
			
			this.buttonsPanel.on('onSnapPressFn',   function () { this.viewer.pluginSnapActivate(this.currentSelectedLayer); }, this);
			this.buttonsPanel.on('onSnapReleaseFn', function () { this.viewer.pluginSnapClear(); }, this);
			this.buttonsPanel.on('onCswPressFn', function () { this.cswWidgetActivate(); }, this);
			this.buttonsPanel.on('onCswReleaseFn', function () { this.cswWidgetDeactivate(); }, this);
			this.buttonsPanel.on('onWMSPressFn', function () { this.wmsExplorerWidgetActivate(); }, this);
			this.buttonsPanel.on('onWMSReleaseFn', function () { this.wmsExplorerWidgetDeactivate(); }, this);
			
			this.buttonsPanel.on('on3DPressFn', function () { this.viewer3DWidgetActivate(); }, this);
			this.buttonsPanel.on('on3DReleaseFn', function () { this.viewer3DWidgetDeactivate(); }, this);
			
			this.buttonsPanel.on('showPermalinkClicked', function () { this.showPermalink(); }, this);
			this.buttonsPanel.on('exportForQgisClicked', function (catIdx, layIdx, version) { this.exportForQgis(catIdx, layIdx, version); }, this);
			this.buttonsPanel.on('showTolomeoInfoClicked', function () { this.tolomeoInfoWin.show();}, this);
			this.buttonsPanel.on('showCustomInfoClicked', function (args) { this.showCustomInfo(args);}, this);
			this.buttonsPanel.on('regeneratePageClicked', function () { this.regeneratePage();}, this);
			this.buttonsPanel.on('showGuideClicked', function (url) { this.showGuide(url);}, this);
			this.buttonsPanel.on('showFaqClicked', function (url) { this.showFaq(url);}, this);
			this.buttonsPanel.on('mailToAdministratorClicked', function (to,subject) { this.mailToAdmin(to,subject);}, this);
			
			this.buttonsPanel.on('showWithOSMClicked', function () { this.apriMappaOSM(); }, this);
			this.buttonsPanel.on('showWithGoogleSatelliteClassicClicked', function () { this.apriMappaGoogle('k', true); }, this);
			this.buttonsPanel.on('showWithGoogleMapClassicClicked', function () { this.apriMappaGoogle('h', true); }, this);
			this.buttonsPanel.on('showWithGoogleSatelliteClicked', function () { this.apriMappaGoogle('k', false); }, this);
			this.buttonsPanel.on('showWithGoogleMapClicked', function () { this.apriMappaGoogle('m', false); }, this);
			this.buttonsPanel.on('showWithBingObClicked', function () { this.apriMappaBing('b', 0); }, this);
			this.buttonsPanel.on('showWithBingSatelliteClicked', function () { this.apriMappaBing('h', 0); }, this);
			this.buttonsPanel.on('showWithBingMapClicked', function () { this.apriMappaBing('r', 0); }, this);
			this.buttonsPanel.on('showWithHereSatelliteClicked', function () { this.apriMappaHere('satellite'); }, this);
			this.buttonsPanel.on('showWithHereMapClicked', function () { this.apriMappaHere('normal'); }, this);
			
			this.buttonsPanel.on('languageChange', this.languageChange, this);
			
			this.buttonsPanel.bindToAPI(this);
		}
	},
	
  /**
    * Method: bindToContextMenu
    * Crea il contextMenu se non esiste, lo lega al viewer e ne gestisce gli eventi
    * 
    */
	bindToContextMenu: function() {
		// Bind con eventi contextMenu 
		if (!this.contextMenu) {
			this.contextMenu = new TolomeoExt.ToloContextMenu({projectionCrs:this.projectionCrs,allowOtherMenus: false,paramsJS: this.paramsJS});
			this.fireEvent("afterContextMenuCreate", this.contextMenu);
		}
		if (this.viewer.map) {
			this._registerContextMenu();		
		} else {
			this.viewer.on('onAfterPostInit', 
				function(ev) {
					this._registerContextMenu();
					/*
					this.contextMenu.setCrsSelected(this.getProjectionCode());
					this.viewer.getEl().on('contextmenu', // TODO STILI
							
						function(e) {
							e.preventDefault(); 
							this.contextMenu.showAt(e.getXY());						
						}, this);*/
					}
				, this,{single: true});
		}
			
		this.contextMenu.on('onGotoLocClickFn', this.gotoLocationEnable, this);
		this.contextMenu.on('onReleaseLocClickFn', this.releaseLocationEnable, this);
		this.contextMenu.on('onReleaseStreetviewClickFn', this.releaseStreetviewEnable, this);
		this.contextMenu.on('onNotaClickFn', this.insertNoteOnMap, this);
		
		if (this.olsPanel!=null) {
			this.contextMenu.on('onOlsMethod', function(method) {
				this.olsPanel.setMethod(method);
			}, this);
			this.contextMenu.on('onOlsReverseGeocoding', function(posX, posY) {	
				this.olsPointSelected(posX, posY, 'REVERSE');
			}, this);
			this.contextMenu.on('onOlsStartPoint', function(posX, posY) {	
				this.olsPointSelected(posX, posY, 'START');
			}, this);
			this.contextMenu.on('onOlsViaPoint', function(posX, posY) {	
				this.olsPointSelected(posX, posY, 'VIA');
			}, this);
			this.contextMenu.on('onOlsEndPoint', function(posX, posY) {	
				this.olsPointSelected(posX, posY, 'END');
			}, this);
			this.contextMenu.on('onOlsReset', function() {
				this.olsPanel.reset();
				this.clearRouting();
				this.viewer.pluginClearStartRoutingMarker();
				this.viewer.pluginClearEndRoutingMarker();
				this.viewer.pluginClearViaRoutingMarkers();	
				this.viewer.pluginRoutingDeactivate();
			}, this);
		}
		
	},
	
	
	_registerContextMenu: function() {
		this.contextMenu.setCrsSelected(this.getProjectionCode());
		this.viewer.getEl().on('contextmenu', // TODO STILI				
			function(e) {
				e.preventDefault(); 
				this.contextMenu.showAt(e.getXY());						
			}, this);
	},

	bindToStatusPanel: function(){
		if(this.statusPanel){
		
			this.setStatusMode = function(functionMode,functionType){
				if(!functionMode) return;
				this.statusPanel.defaultText=ToloI18n.getMsg("ToloMapAPIExt.statusPanel.defaultText", {FUNCTIONMODE: functionMode, FUNCTIONTYPE: (functionType ? " : " + functionType : "")} );
				this.statusPanel.clearStatus({useDefaults:true});
			}
			
			this.on('selectRequestBeforeStart',function(){
				this.statusPanel.showBusy(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.showBusy"));
				
			},this);
			
			this.on('selectRequestEnd',function(esito){				
				this.statusPanel.setStatus({
				    text: esito.ok?ToloI18n.getMsg("ToloMapAPIExt.statusPanel.requestEnd.text", {NRESULT: esito.nResults}):ToloI18n.getMsg("ToloMapAPIExt.statusPanel.requestEnd.Errore.text"),
				    iconCls: esito.nResults==0?'x-status-error':'x-status-valid',
				    clear: true // auto-clear after a set interval
				});
			},this);
			
			if(this.statusPanel.rendered){
				this.setStatusMode("panoramica");
			}else{
				this.statusPanel.on('afterrender',function(){this.setStatusMode("panoramica");},this)
			}

			
			
			if(this.buttonsPanel){
				this.buttonsPanel.on('onPanPressFn',        function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.panoramica"));},this);
				this.buttonsPanel.on('onZoomBoxPressFn',    function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.panoramica"),
																				ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.panoramica.zoombox"));} , this);
				this.buttonsPanel.on('onSelectPressFn',     function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.selezione"));} , this);
				this.buttonsPanel.on('onNuovoPressFn',      function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing"),
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing.nuovo"));} , this);
				this.buttonsPanel.on('onNuovoDaLayerPressFn',      function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing"),
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing.nuovoDaLayer"));} , this);
				this.buttonsPanel.on('onNuovoDaImportPressFn',      function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing"),
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing.nuovoDaImport"));} , this);
				this.buttonsPanel.on('onUpdateAlfaPressFn', function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing"),
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing.updateAlpha"));} , this);
				this.buttonsPanel.on('onAddPressFn',        function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing"),
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing.unione"));} , this);
				this.buttonsPanel.on('onSubtractPressFn',   function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing"),
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing.sottrazione"));} , this);
				this.buttonsPanel.on('onAddSubPressFn',     function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing"),
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing.copertura"));} , this);
				this.buttonsPanel.on('onVertexEditPressFn', function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing"),
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing.vertexEd"));} , this);
				this.buttonsPanel.on('onDragDropPressFn',   function(){this.setStatusMode(ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing"),
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.editing.dragdrop"));} , this);
				this.buttonsPanel.on('onMeasureActivate',   function(measureType){
					var mt = "";
					switch (measureType){
						case 0: mt = ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.misura.misurapol");
							break;
						case 1: mt = ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.misura.misuracer");
							break;
						default: mt = ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.misura.misuralin");
					}
					
					this.setStatusMode(
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.misura"), mt);}, this);
				this.buttonsPanel.on('onMeasureTypeChange', function(measureType){
					var mt = "";
					switch (measureType){
						case 0: mt = ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.misura.misurapol");
							break;
						case 1: mt = ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.misura.misuracer");
							break;
						default: mt = ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.misura.misuralin");
					}

					this.setStatusMode(
						ToloI18n.getMsg("ToloMapAPIExt.statusPanel.mode.misura"),mt);}, this);
				
			}
		}
	},
	
	bindToQueryBuilderPanel: function(){
		var mapApiExt = this;
		var tolomeoViewer = mapApiExt.viewer;
		if (this.queryBuilderPanel) {
			tolomeoViewer.on("onAfterPreInit", function(){
				var qbEventManager = mapApiExt.queryBuilderPanel.getQbEventManager();
				qbEventManager.setMap(tolomeoViewer.map);
				if(mapApiExt.projectionCrs[mapApiExt.getProjectionCode()]){
					mapApiExt.queryBuilderPanel.spatialSelector.setDecimalPrecision(mapApiExt.projectionCrs[mapApiExt.getProjectionCode()].precision);
				}
			});											
		}
		
	},

	bindToCodeLessPanel: function(){
		var mapApiExt = this;
		var tolomeoViewer = mapApiExt.viewer;
		tolomeoViewer.on("onAfterPreInit", function(){
			if(mapApiExt.codeLessPanel){
				mapApiExt.codeLessPanel.setMapApiExt(mapApiExt);
			}
		})	
	},
	
	bindToViewCodeLessPanel: function(){
		var mapApiExt = this;
		var tolomeoViewer = mapApiExt.viewer;
		tolomeoViewer.on("onAfterPreInit", function(){
			if(mapApiExt.viewCodeLessPanel){
				mapApiExt.viewCodeLessPanel.setMapApiExt(mapApiExt);
			}
		})	
	},
	
	viewer3DWidgetActivate: function() {
		if (this.viewer3DWidget == null) {
		
			var me = this;
			
			this.lazyLoadScript('cesium',
					function() {
						// // {left: bounds.left, bottom: bounds.bottom, right: bounds.right, top: bounds.top} 
						var extent = me.viewer.pluginGetMapExtent();
						var ul = new Point(extent.left, extent.top);
						var br = new Point(extent.right, extent.bottom);  
					    ul.transform(me.viewer.pluginGetProjectionCode(),"EPSG:4326");    
					    br.transform(me.viewer.pluginGetProjectionCode(),"EPSG:4326");
					    this.viewer3D =	new TolomeoExt.ToloCesiumPanelExt({
					    		paramsJS: this.paramsJS,
					    		TOLOMEOServer: this.TOLOMEOServer,
					    		TOLOMEOStaticRoot: this.TOLOMEOStaticRoot,
					    		TOLOMEOContext: this.TOLOMEOContext,
					    		startExtentWest: ul.x,
						  		startExtentEast: br.x,
						  		startExtentNorth: ul.y,
						  		startExtentSouth: br.y,
						  		listeners: {
										'closePressed': function() {
												me.viewer3DWidgetActivate();	
										}
									}
						  	});  
							
					    this.viewer3DWidget = Ext.create('Ext.Window', {
								width: 500,
								height: 500,
								layout: 'fit',
								title: ToloI18n.getMsg("ToloMapAPIExt.viewer3DWidget.title"),
								constrain: true,
								maximizable: true,
								items: [ this.viewer3D  ],
								closeAction: 'destroy',
								listeners: {'close' : function() {
										me.viewer3DWidget = null;
										me.viewer3D = null;
										me.buttonsPanel.buttonToggle(TolomeoExt.ToloAPIOpCodes.btn3D, null, true);
										
									}
								}
						});	
				
					    this.viewer3DWidget.show();
				
					},
					function(){Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.viewer3DWidget.alert.title"), ToloI18n.getMsg("ToloMapAPIExt.viewer3DWidget.alert.msg"),
								function(){this.buttonsPanel.buttonToggle(TolomeoExt.ToloAPIOpCodes.btn3D, null, true);}, this);},
					this
				);
		} else {
			this.viewer3DWidget.show();
		}
		
		
	},
	
	viewer3DWidgetDeactivate: function() {
		if (this.viewer3DWidget != null) {
			this.viewer3DWidget.close();
			this.viewer3DWidget = null;
			this.viewer3D = null;
			this.buttonsPanel.buttonToggle(TolomeoExt.ToloAPIOpCodes.btn3D, null, true);
		}
	},
	
	setViewer3D: function(viewer3D) {
		this.viewer3D = viewer3D;
	},

	/**
	 * Method: temporalFilterApply
	 * Applica il filtro temporale
	 * 
	 */
	temporalFilterApply: function(dtInizio, dtFine){
		this.temporalFilterDtInizio = (dtInizio != null && dtInizio!="") ? Ext.Date.format(dtInizio,"d/m/Y") : "01/01/0001";
		this.temporalFilterDtFine   = (dtFine   != null && dtFine!="") ? Ext.Date.format(dtFine,"d/m/Y") : "31/12/2099";
		this.applyCustomQuery();
	},
	
	/**
	 * Method: zoomIn
	 * Esegue lo zoom avanti.
	 * 
	 */
	zoomIn: function(){
		this.viewer.pluginToolSelectZoomIn();		
	},
	
	/**
	 * Method: zoomOut
	 * Esegue lo zoom indietro.
	 * 
	 */
	zoomOut: function(){
		this.viewer.pluginToolSelectZoomOut();
	},
	
	/**
	 * Method: zoomBox
	 * Esegue lo zoom box.
	 * 
	 */
	zoomBox: function(){
		this.viewer.pluginToolSelectZoomBox();
	},

	/**
	 * Method: pan
	 * Esegue il pan.
	 * 
	 * Parameters:
	 * direction - {} direzione di spostamento.
	 * slideFactorPan - {} tolleranza di spostamento
	 */
	pan: function(direction, slideFactorPan){
		this.viewer.pluginPan(direction,slideFactorPan);
	},

	/**
	 * Method: panNord
	 * Esegue il pan verso nord.
	 * 
	 * Parameters:
	 * slideFactor - {} tolleranza di spostamento
	 */
	panNord: function(slideFactorPan){
		this.pan("N",slideFactorPan);
	},

	/**
	 * Method: panSud
	 * Esegue il pan verso sud.
	 * 
	 * Parameters:
	 * slideFactor - {} tolleranza di spostamento
	 */
	panSud: function(slideFactorPan){
		this.pan("S",slideFactorPan);
	},

	/**
	 * Method: panOvest
	 * Esegue il pan verso ovest.
	 * 
	 * Parameters:
	 * slideFactor - {} tolleranza di spostamento
	 */
	panOvest: function(slideFactorPan){
		this.pan("O",slideFactorPan);
	},

	/**
	 * Method: panEst
	 * Esegue il pan verso est.
	 * 
	 * Parameters:
	 * slideFactor - {} tolleranza di spostamento
	 */
	panEst: function(slideFactorPan){
		this.pan("E",slideFactorPan);
	},

	/**
	 * @method historyNext
	 * Sposta alla visualizzazione successiva.
	 * 
	 */
	historyNext: function(){
		if (this.viewer) {
			this.viewer.pluginHistoryNext();
		}
	},

	/**
	 * @method historyPrev
	 * Sposta alla visualizzazione successiva.
	 * 
	 */
	historyPrev: function(){
		if (this.viewer) {
			this.viewer.pluginHistoryPrev();
		}
	},
	
	showTimeMachine: function(show) {
		if (this.viewer) this.viewer.pluginShowTimeMachine(show);
	},
	
	/**
	 * Method: showPrintWindow
	 * Crea una wizard di stampa su una finestra. Nel primo passo imposto la stampa nel secondo la genero.
	 *
	 * Parameters:
	 * url - {String} url.
	 */
	showPrintWindow: function(url) {
		
		var server        = this.TOLOMEOServer;
		var context       = this.TOLOMEOContext;
		var urlMappa      = url;
		var scala         = this.viewer.pluginGetCurrentZoom();
		var unita         = this.viewer.pluginGetUnits();
		var mapx          = this.viewer.pluginGetCurrentX();
		var mapy          = this.viewer.pluginGetCurrentY();
		var mapext        = this.viewer.pluginGetMapExtent();
		var mappaTypeCode = this.paramsJS.mappe.mappaList[0].typeCode; //TODO Funziona solo con mappe con unico strato
		var me =  this;
		
		// Definisce formati di stamapa
		var printFormats = [];
		var n = 3;
		var maxPrintFormat = this.paramsJS.mappe.maxPrintFormat;
		if (maxPrintFormat) {
			n = maxPrintFormat.substring(1,2);	
		} 
		for (var i = 4; i >= n; i--) {
			printFormats.push({
		                checked: (i==4 ? true: false),
		                fieldLabel: '',
		                boxLabel: 'A'+i,
		                name: 'formato',
		                inputValue: 'a'+i
		            });
		}
		
		var comboLegenda = this.geoOpField = Ext.create('Ext.form.field.ComboBox', {
	    	name: 'legendType',
            value: 'separate-A4-1col',
            anchor: '99%',
            fieldLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.comboLegenda.fieldLabel"),            
            labelSeparator: ': ',
            padding: '10 0 10 0',
            mode: 'local',
            triggerAction: 'all',
            forceSelection: true,
            allowBlank: false,
            editable: false,
            lazyInit: false,
            disabled:true,
            listConfig: {
            	getInnerTpl: function() {
        			return '<div data-qtip="{suggerimento}">{descrizione}</div>'; 
            		}
        	},		            
            displayField: 'descrizione',
            valueField: 'valore',
            store: Ext.create('Ext.data.ArrayStore', {
		        //id: 0,			        
		        fields: ['id', 'valore', 'descrizione', 'suggerimento'],    //suggerimento
			    data: [			    	
			    	[1, 'embed', ToloI18n.getMsg("ToloMapAPIExt.printWindow.comboLegenda.sumappa.descrizione"), ToloI18n.getMsg("ToloMapAPIExt.printWindow.comboLegenda.sumappa.suggerimento")],
			    	[2, 'separate-A4-1col', ToloI18n.getMsg("ToloMapAPIExt.printWindow.comboLegenda.unacol.descrizione"), ToloI18n.getMsg("ToloMapAPIExt.printWindow.comboLegenda.unacol.suggerimento")],
			    	[3, 'separate-A4-2col', ToloI18n.getMsg("ToloMapAPIExt.printWindow.comboLegenda.duecol.descrizione"), ToloI18n.getMsg("ToloMapAPIExt.printWindow.comboLegenda.duecol.suggerimento")]			    	
			    ]
		    })
	    });
		
		var parametri = [{
			columnWidth: '1',
			labelWidth: 60,
			xtype: 'container',				
	        items: {
	            xtype: 'fieldset',
	            title: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetPersonalizza.title"),
	            autoHeight: true,
	            items: [{
	            	xtype: 'numberfield',
	                fieldLabel: 'Scala 1',
		            name: 'scala',
		            value: Math.round(scala),
		            allowBlank: false,
		            allowDecimals: false,
		            allowNegative: false
		            /*,
		            minValue: 200,
		            maxValue: 300000
		            */
	            },{
	            	xtype: 'textfield',
			    	fieldLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetPersonalizza.titolo"),
			    	anchor: '99%',
			    	name: 'titolo',
			    	value: this.titoloMappa,
			    	allowBlank: false,
			    	maxLength: 100
	            },{
	            	xtype: 'textfield',
			    	fieldLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetPersonalizza.sottotitolo"),
			    	anchor: '99%',
			    	name: 'sottotitolo',
			    	value: this.sottotitoloMappa,			    	
			    	maxLength: 500
	            },{
			    	xtype: 'htmleditor',
			    	fieldLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetPersonalizza.descrizione"),
			    	height: 100,
			    	anchor: '99%',
			    	name: 'descrizione',
			    	value: this.descrizioneMappa,
			    	defaultFont: 'Arial',
			    	enableAlignments: false,
			    	enableFont: false,
			    	enableLinks: false,
			    	enableSourceEdit: false
				},{
		            xtype: 'checkboxgroup',
		            anchor: '99%',
		            fieldLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetPersonalizza.fldIncludi"),
		            items: [{
                        xtype: 'checkbox',
                        boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetPersonalizza.fldIncludi.url"),
                        name: 'stampaReferer',
                        checked: false
                    },{
                        xtype: 'checkbox',
                        boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetPersonalizza.fldIncludi.dataora"),
                        name: 'aggDataOra'
		            },{
                        xtype: 'checkbox',
                        boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetPersonalizza.fldIncludi.perma"),
                        name: 'printPermalink',
                        checked: false
		            },{
                        xtype: 'checkbox',
                        boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetPersonalizza.fldIncludi.legenda"),
                        name: 'printLegend',
                        checked: false,
                        listeners: {
					        change: {            
					            fn: function(e,newVal,oldVal){ comboLegenda.setDisabled(!e.checked) }
					        }
    					}
		            }]
	            },comboLegenda]
			}
		},{
			columnWidth: '.3',
			xtype: 'container',				
	        items: {
	            xtype: 'fieldset',
	            title: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetOrien"),
	            height: 100,
	            labelWidth: 10,
	            defaultType: 'radio',
	            items: [{
	                checked: true,
	                fieldLabel: '',
	                boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetOrien.V"),
	                name: 'orientamento',
	                inputValue: 'v'
	            },{
	                fieldLabel: '',
	                labelSeparator: '',
	                boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetOrien.O"),
	                name: 'orientamento',
	                inputValue: 'o'
	            }]
			}
		},{
			bodyStyle: 'padding-left:5px;',
			columnWidth: '.3',
			xtype: 'container',	
	        items: {
	            xtype: 'fieldset',
	            title: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetFormato"),
	            height: 100,
	            labelWidth: 10,
	            defaultType: 'radio',	            
	            items: printFormats
	            /*[{
	                checked: true,
	                fieldLabel: '',
	                boxLabel: 'A4',
	                name: 'formato',
	                inputValue: 'a4'
	            },{
	                fieldLabel: '',
	                labelSeparator: '',
	                boxLabel: 'A3',
	                name: 'formato',
	                inputValue: 'a3'
	        	},{
	                fieldLabel: '',
	                labelSeparator: '',
	                boxLabel: 'A2',
	                name: 'formato',
	                inputValue: 'a2'
	        	}]*/
			}
		},{
			bodyStyle: 'padding-left:5px;',
			columnWidth: '.4',
			xtype: 'container',	
	        items: {
	        	xtype: 'fieldset',
	            title: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetQual"),
	            //autoHeight: true,
	            //fieldLabel: 'Risoluzione',
		        labelWidth: 60,
	            height: 100,
	            items: [{
		            xtype: 'combo',
		            //width: 150,
		            anchor: '-10',
		            labelSeparator: ':\n',
		            mode: 'local',
		            triggerAction: 'all',
		            forceSelection: true,
		            editable: false,
		            lazyInit: false,
		            value: '96',
		            listConfig: {
		            	getInnerTpl: function() {
	            			return '<div data-qtip="{suggerimento}">{descrizione}</div>'; 
		            		}
	            	},
		            fieldLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetQual.label"),
		            name: 'dpiStampa',
		            hiddenName: 'dpiStampa',
		            displayField: 'descrizione',
		            valueField: 'valore',
		            store: Ext.create('Ext.data.ArrayStore', {
				        //id: 0,			        
				        fields: ['id', 'valore', 'descrizione', 'suggerimento'],    //suggerimento
					    data: [
					    	[1, '96' , ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetQual.dpi96.desc"), ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetQual.dpi96.sugg")],
					    	[2, '150', ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetQual.dpi150.desc"), ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetQual.dpi150.sugg")],
					    	[3, '300', ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetQual.dpi300.desc"), ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetQual.dpi300.sugg")]
					    ]
				    })
	            }]
            }
		},{
			columnWidth: '1',
			xtype: 'container',	
			items: {
	            xtype: 'fieldset',
	            title: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetEsp"),
	            autoHeight: true,
	            items: {
		        	xtype: 'radiogroup',
		            columns: 2,
		            items: [
		                {boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetEsp.pdf"),  name: 'esportazione', inputValue: 'pdf', checked: true/*, 
		                listeners: {
					        change: {            
					            fn: function(e,newVal,oldVal){ comboLegenda.setDisabled(!e.checked) }
					        }
    					}*/},
		                {boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetEsp.imm"),   name: 'esportazione', inputValue: 'png'},
		                {boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetEsp.docx"), name: 'esportazione', inputValue: 'docx'},
		                {boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetEsp.rtf"),                name: 'esportazione', inputValue: 'rtf'},
		                {boxLabel: ToloI18n.getMsg("ToloMapAPIExt.printWindow.fldsetEsp.odt"),                name: 'esportazione', inputValue: 'odt'}
		            ]
	            }
			}
	    },
	    	{ xtype: 'hidden', name: 'iehack',            value: String.fromCharCode(153)},
	    	{ xtype: 'hidden', name: '_charset_',         value: ''},
			//{ xtype: 'hidden', name: 'mappaTypeCode',     value: mappaTypeCode},
	    	{ xtype: 'hidden', name: 'mapx',              value: mapx},
	    	{ xtype: 'hidden', name: 'mapy',              value: mapy},
	    	{ xtype: 'hidden', name: 'bbox',            value: (mapext.left + "," + mapext.bottom + "," + mapext.right + "," + mapext.top)},
	    	{ xtype: 'hidden', name: 'unita',             value: unita},
			//{ xtype: 'hidden', name: 'urlMappa',          value: escape(unescape(urlMappa))},
			{ xtype: 'hidden', name: 'urlLogo',           value: this.urlLogo},
			{ xtype: 'hidden', name: 'urlLogoSecondario', value: this.urlLogoSecondario},	
			{ xtype: 'hidden', name: 'projectionCode',    value: this.getProjectionCode()},
			{ xtype: 'hidden', name: 'permalinkHref',     value: ''/*, id : 'permalinkHref'*/}
		];
		
		for (var i=0; i < urlMappa.length; i++ ) {
			
			parametri.push({ xtype: 'hidden', 
							 name: 'urlMappa', 
							 value: escape(unescape(urlMappa[i].url))});
			
			parametri.push({ xtype: 'hidden', 
							name: 'mappaTypeCode', 
							value: urlMappa[i].typeCode});
			
			// TODO gestita solo mappa 0
			var nMappa = 0;
			var lag = this.paramsJS.mapDefinitions[nMappa].whichLayerAggregationContainsNPluginLayer(urlMappa[i].nPluginLayer);
			parametri.push({ xtype: 'hidden', 
							name: 'opacity', 
							value: lag.opacity});			
							
			var tileStampaAltezza 	= 0;
			var tileStampaLarghezza = 0;
			
			if (lag!=null && this.TOCPanel && this.TOCPanel.tocInfo!=null) {
				
				var servInfo = this.TOCPanel.tocInfo.getServer(lag.server.id);
				if (servInfo!= null) {
					tileStampaLarghezza = servInfo.tileStampaLarghezza;
					tileStampaAltezza   = servInfo.tileStampaAltezza;
				}
				
			}
			  
			parametri.push({ xtype: 'hidden', 
				name: 'tileStampaAltezza', 
				value: tileStampaAltezza});

			parametri.push({ xtype: 'hidden', 
				name: 'tileStampaLarghezza', 
				value: tileStampaLarghezza});							
									
		}
		
		var popups = this.viewer.pluginGetOpenedPopups();
		for (var j=0; j < popups.length; j++) {
			var p = popups[j];
			parametri.push({ xtype: 'hidden', 
				name: 'popupxy', 
				value: p.lonlat.lon + "|" + p.lonlat.lat});
			
			parametri.push({ xtype: 'hidden', 
				name: 'popuptext', 
				value: p.contentHTML});
			
		}
		
		var fp = Ext.create('Ext.FormPanel', {
			id: 'fp',
	        border: false,
	        frame: true,
	        items: [{
	        	xtype: 'container',	
	        	layout: 'column',
	        	border: false,
	            defaults: {
	                columnWidth: '.5',
	                border: true
	            },            
	            items: parametri
	        }]
	    });
	    
	    fp.removeLegendParams = function(){
	    	
	    	var fields = this.getForm().getFields().items;
	    	var toRemove = [];
	    	
	    	for(var f=0; f < fields.length; f++){	 	    		
	    		if(!fields[f].name) continue;	    		
	    		if((fields[f].name == 'legendLayerTitles') || (fields[f].name == 'legendGraphicUrls')){
	    			// lo registro in un array e lo rimuovo successivamente, perché se lo rimuovo al volo cambia la lunghezza dell'array medesimo e non funziona correttamente
	    			toRemove.push(fields[f]);
	    		}	    			    		
	    	}	
	    	
	    	for(var t = 0; t < toRemove.length; t++){    			
    			this.remove(toRemove[t],true);
    		}
	    }
	    
	    fp.generateLegendParams = function(){
	    	
	    	this.removeLegendParams();
	    	
	    	if(!this.down('[name=printLegend]').checked) return;
	    	
	    	var scale = 1 * this.down('[name=scala]').getValue();
	    	
	    	var legendParams = [];
	    	
	    	for (var i=urlMappa.length-1; i >= 0 ; i-- ) {
					    			    					
				// TODO gestita solo mappa 0
				var nMappa = 0;
				var lag = me.paramsJS.mapDefinitions[nMappa].whichLayerAggregationContainsNPluginLayer(urlMappa[i].nPluginLayer);				
				
				if (lag!=null && me.TOCPanel && me.TOCPanel.tocInfo!=null) {
																				
					// Codice per aggiunta url della legendGraphic
					for(var j = 0; j < lag.layers.length ; j++){
						
						var layInfo = me.TOCPanel.tocInfo.searchLayerInfo(lag.layers[j].serverID,lag.layers[j].name);
						
						// Non gestito il caso di MAPSERVER CGI
						if(	!layInfo || 
							(layInfo.classi.length > 1) || 
							lag.layers[j].hidden || 
							!lag.layers[j].checked || 
							//layInfo.raster ||
							!me.TOCPanel.layerIsVisibleAtScale(scale, lag.layers[j].scalaMinima, lag.layers[j].scalaMassima, layInfo.minScaleMin, layInfo.maxScaleMax)) continue;						
						
						var legendGraphicUrls = layInfo.classi[0].nome;
						var legendLayerTitles = layInfo.descr;
						
						legendParams.push({ xtype: 'hidden', 
								name: 'legendLayerTitles', 
								value: legendLayerTitles});														
						
						legendParams.push({ xtype: 'hidden', 
								name: 'legendGraphicUrls', 
								value: legendGraphicUrls});
								
						legendParams.push({ xtype: 'hidden', 
								name: 'legendLayerStileIndipDaScala', 
								value: layInfo.stiliIndipDaScala});
								
						legendParams.push({ xtype: 'hidden', 
								name: 'legendLayerStileIndipDaPosizione', 
								value: layInfo.stiliIndipDaPosizione});
																												
					}
															
				}				  					
										
			}
			
			this.add(legendParams);
			
	    }

		var printWindow = Ext.create('TolomeoExt.Window', {
			id: 'printWindow',
			title: ToloI18n.getMsg("ToloMapAPIExt.printWindow.title"),
			layout: 'fit',
			iconCls: 'iconPrint',
			frame: true,
			border: false,
			constrain: true,
			maximizable: false,
	//		monitorResize: true,
			plain: true,
			modal: true,
			width: 600,
			height: 580,
			items: fp, //printWizard
			buttonAlign: 'right',
			buttons: [{
	            text: ToloI18n.getMsg("ToloMapAPIExt.printWindow.btnOK"),
	            width: 75,
	            handler: function(){
	            	if(fp.getForm().isValid()){
	            		//getForm().
	               		Ext.getCmp('fp').getForm().standardSubmit = true;
	               		//Ext.getCmp('fp').getForm().getEl().dom.acceptCharset = 'utf-8';
	               		//Ext.getCmp('fp').getForm().getEl().dom.target = '_self';
	               		//Ext.getCmp('fp').getForm().getEl().dom.action = server + context + '/StampaMappaServlet';
	               		var submitParams = {
	               					headers: { acceptCharset: 'utf-8' },
	               					target: '_self',
	               					url: server + context + '/StampaMappaServlet',
	               					paramPreset: me.paramsJS.nomePreset
	               		}
	               		
	               		// Aggiunta parametri WMS da url
						Ext.apply(submitParams, me.paramsJS.urlAdditionalParams);						
						
						if(fp.down('[name=printPermalink]').getValue()){
							fp.down('[name=permalinkHref]').setValue(me.generatePermalink());
						}
	               		
						fp.generateLegendParams();
	               		fp.getForm().submit(submitParams);
	            	}
	            }
	        },{
	        	text: ToloI18n.getMsg("ToloMapAPIExt.printWindow.btnAnnulla"),
	            width: 75,
	            handler: function(){
					Ext.getCmp('printWindow').close();
				}
	        }]
		}).show();
	},
	
	languageChange: function(lng){
		var obj = Ext.Object.fromQueryString(window.location.search);
		obj.lang = lng;
		window.location = (window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '')) + window.location.pathname + "?" + Ext.Object.toQueryString(obj);
	}, 
	
	/**
	 * Method: wmsExplorerWidgetActivate
	 * Attivazione widget di gestione aggiunta WMS
	 * 
	 * @param {} addPointCatIdx
	 * @param {} addPointLayIdx
	 * @param {} bBefore
	 */
	wmsExplorerWidgetActivate: function(addPointCatIdx, addPointLayIdx, bBefore) {
		
		if(this.paramsJS.layOut.WMSExplorer && !this.wmsExplorerWidget){	
		
			var me = this;
			
			this.wmsExplorerWidget = Ext.create('TolomeoExt.Window',{
				layout: 'fit',
		        closeAction: 'hide',
				title: ToloI18n.getMsg("ToloMapAPIExt.WMSExplorer.title"),
				resizable: true,
				constrain: true,
				width: 600, 
				height: 400,
				items: [
				        Ext.create('TolomeoExt.ToloWMSExplorerPanel', {
				        		addPointCatIdx: (addPointCatIdx) ? addPointCatIdx : "0",
	 							addPointLayIdx: (addPointLayIdx) ? addPointLayIdx : undefined,
	 							bBefore: bBefore,
	 							listeners: {
	 								'addLayer' : function(layers) {
		 									for (var i=0; i<layers.length; i++) {
		 										var layer = layers[i];
			 									var options = {
															serverurl:  layer.url,
			 												layername: layer.name,
			 												addPointCatIdx: this.addPointCatIdx,
			 												addPointLayIdx: this.addPointLayIdx,
			 												bBefore: this.bBefore
														}
														me.addLayer(options);
		 									}
	 									},
	 								'closePressed': function() {
	 										me.wmsExplorerWidget.hide();	
	 										me.buttonsPanel.buttonToggle(TolomeoExt.ToloAPIOpCodes.btnWMSExplorer, null, true)
	 								}
	 							}
				        	})],
				 listeners: {'hide' : function() {
										me.buttonsPanel.buttonToggle(TolomeoExt.ToloAPIOpCodes.btnWMSExplorer, null, true)
									}
				 			}
			}).show();

		} else{
			if (addPointCatIdx) this.wmsExplorerWidget.addPointCatIdx = addPointCatIdx;
 			if (addPointLayIdx) this.wmsExplorerWidget.addPointLayIdx = addPointLayIdx;
 			if (bBefore) this.wmsExplorerWidget.bBefore = bBefore;
			this.wmsExplorerWidget.show();
		}
		
	},
	
	/**
	 * Disattivazione widget di gestione aggiunta WMS
	 */
	cswWidgetDeactivate : function(){
		if(this.cswWidget){
			this.cswWidget.hide();
		}
	},
	
	/**
	 * Method: wmsExplorerWidgetDeactivate
	 * Chiude il componente di gestione dei Catalog Services.
	 */
	wmsExplorerWidgetDeactivate: function(){
		if(this.wmsExplorerWidget){
			this.wmsExplorerWidget.hide();
		}
	},
	
	/**
	 * Method: cswWidgetActivate
	 * Crea una finestra di gestione dei Catalog Services.
	 *
	 * 
	 */
	cswWidgetActivate : function(addPointCatIdx, addPointLayIdx, bBefore) {
		
		var cswCrsCode = "EPSG:4326";		
		var f = function(){
			
			var cswConfig = this.paramsJS.layOut.csw;
			
			if(!this.cswWidget){			
	
				var toloBBox = BBox.create(this.viewer.pluginGetMapExtent());
				
				var currCrsCode = this.getProjectionCode();
						
				if(currCrsCode != cswCrsCode){
					toloBBox.transform(currCrsCode,cswCrsCode);						
				}
				
				var config = {			
					XDProxy: { url:this.TOLOMEOServer + this.TOLOMEOContext + "/TolomeoProxyServlet", callback: "" },
					//TODO ricorda di mettere a posto. Sembra che se c'e' anche il server http:// non funzioni bene e che parte un options invece di un get
					//XDProxy: { url:"/tolomeobinj/TolomeoProxyServlet", callback: "" },	
					catalogs : cswConfig.catalogList,			   
			     	dcProperty: "title",
				   
				    initialBBox: {
						minx: Math.min(toloBBox.left,toloBBox.right),
						miny: Math.min(toloBBox.bottom,toloBBox.top),
						maxx: Math.max(toloBBox.left,toloBBox.right),
						maxy: Math.max(toloBBox.bottom,toloBBox.top)
					}, 
			
			   		cswVersion: cswConfig.cswVersion,  		  		
					filterVersion: cswConfig.filterVersion,  		
					start: 1,  		
					limit: cswConfig.limit,		
					timeout: cswConfig.timeout
				};
					
				/*
				var i18n = Ext.create('Ext.i18n.Bundle', {
					bundle : "CSWViewer",
					path : this.TOLOMEOServer + this.TOLOMEOStaticRoot + "js/ext/csw/i18n",
					lang : "it-IT"
				});*/
				
				
				//var i18n = TolomeoExt.getApplication().bundle;
				
				
				var me = this;
			
				//i18n.onReady( function() {
				//i18n.on('loaded', function() { 
					
					// Declares a panel for querying CSW catalogs
					var cswPanel = new CSWPanel({
							title: "",
							header: false,
							config: config,
							region:'center',
							i18n: ToloI18n,
							addPointCatIdx: (addPointCatIdx) ? addPointCatIdx : "0",
	 						addPointLayIdx: (addPointLayIdx) ? addPointLayIdx : undefined,
	 						bBefore: bBefore,
							listeners: {
								'zoomToExtent': function(el){
										
										// il bound ritornato da geonetwork ha left e right invertito, per evitare problemi...
										var b = {
											left: Math.min(el.bbox.left, el.bbox.right),
											bottom: Math.min(el.bbox.bottom, el.bbox.top),
											right: Math.max(el.bbox.left, el.bbox.right),
											top: Math.max(el.bbox.bottom, el.bbox.top)
										};
									
										var toloBBox = BBox.create(b);
										var currCrsCode = me.getProjectionCode();					
										if(currCrsCode != cswCrsCode){
											toloBBox.transform(cswCrsCode,currCrsCode);						
										}
										me.zoomToExtent(toloBBox,null);
									},
							
								'viewMap': function(el){
									// addPointCatIdx, addPointLayIdx, bBefore
									if(el.layers){
										for(var l=0; l < el.layers.length; l++ ){
											var layer = el.layers[l];
											if(layer.wms){
												var options = {
													serverurl:  layer.wms,
	 												layername: layer.layer,
	 												addPointCatIdx: this.addPointCatIdx,
	 												addPointLayIdx: this.addPointLayIdx,
	 												bBefore: this.bBefore
												}
												me.addLayer(options);
												/*
												var mappa = {
													mapOptions : "layers: '" + layer.layer + "'",
													overlay : false,
													SRID : me.getProjectionCode(),
													units : 'm',
													mostraInLegenda : true,
													viewerOptions : "",
													typeCode : 11,		// WMS
													nome : layer.description,
													url : layer.wms
												};
												me.addLayer(mappa);
												*/
											}
										}
									}
								},
							
								'beforesearch' : function(params){
									if(!(params.useAdvancedSearch && params.useBbox)) return;
							
									var toloBBox = BBox.create(me.viewer.pluginGetMapExtent());					
									var currCrsCode = me.getProjectionCode();
									
									if(currCrsCode != cswCrsCode){
										toloBBox.transform(currCrsCode,cswCrsCode);						
									}
									
									var cswBBox = {									
										minx: Math.min(toloBBox.left,toloBBox.right),
										miny: Math.min(toloBBox.bottom,toloBBox.top),
										maxx: Math.max(toloBBox.left,toloBBox.right),
										maxy: Math.max(toloBBox.bottom,toloBBox.top)
									}
									
									this.setBBox(cswBBox);
								}
						}
							
					});
			
					cswPanel.mon(this.TOCPanel, 'itemSelected', function(catTreeIdx, layTreeIdx, classi) {
														cswPanel.addPointCatIdx = catTreeIdx;
									 					cswPanel.addPointLayIdx = (layTreeIdx) ? layTreeIdx : undefined;
													});
													
					me.cswWidget = Ext.create('Ext.Window', {
						id: 'cswWindow',
						title: ToloI18n.getMsg("ToloMapAPIExt.CSWWidget.title"),
						layout: 'fit',
						iconCls: 'iconCsw',
						frame: true,
						border: false,
						maximizable: false,
						constrain: true,
						minimizable: false,
				//		monitorResize: true,
						plain: true,
						width : 620,				
						boxMaxHeight:562,
						boxMinWidth: 600,
		            	autoScroll: true,
		            	closeAction: 'hide',
						cls: 'clearCSS',
						cswPanel: cswPanel, 
						items: [cswPanel],
						listeners : {
							'hide' : function() {
								me.buttonsPanel.buttonToggle(TolomeoExt.ToloAPIOpCodes.btnCsw, null, true)
							}
						}
					});
										
					me.cswWidget.show();
			//	});
			}else{
				if (addPointCatIdx) this.cswWidget.cswPanel.addPointCatIdx = addPointCatIdx;
	 			if (addPointLayIdx) this.cswWidget.cswPanel.addPointLayIdx = addPointLayIdx;
	 			if (bBefore) this.cswWidget.cswPanel.bBefore = bBefore;
				this.cswWidget.show();
			}
		}
		
		this.lazyLoadScript(['proj4js', 'cswExplorer'],
			f,
			function(){Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.LazyLoad.alert.title"), ToloI18n.getMsg("ToloMapAPIExt.LazyLoad.alert.msg"), function(){this.buttonsPanel.buttonToggle(TolomeoExt.ToloAPIOpCodes.btnCsw, null, true);}, this);},
			this
		);
					
	},
	
	/**
	 * Aggiunge un layer nella posizione indicata dai parametri.
	 * 
	 * @param {} options oggetto contenente le opzioni nei seguenti attributi <br />
	 * 		<ul>
	 * 			<li>serverurl: url del server WMS</li>
	 * 			<li>layername: nome del layer WMS</li>
	 * 			<li>addPointCatIdx: Indice della categoria nella quale viene aggiunto il layer </li>
	 * 	 		<li>addPointLayIdx: Indice layer prima o dopo del quale aggiungere il nuovo layer</li>
	 * 			<li>bBefore: indica se aggiungere prima o dopo</li>
	 * 		</ul>
	 * 			
	 */
	addLayer: function (options) {
		
		if (this.TOCPanel) {
			this.TOCPanel.addLayer(options);
		}
		
	},
	
	/**
	 * Method: setCurrentSelectLayer
	 * Setta il layer corrente (codTPN). 
	 *
	 * Parameters:
	 * layer - {} il layer.
	 */
	setCurrentSelectLayer: function (layer) {
		this.currentSelectedLayer = layer;
		this.togglePermittedOperations();
		this.fireEvent('onOperationPressDefault', 2);
	},
	
	/**
	 * Method: getCurrentSelectLayer
	 * 
	 * Returns:
	 * Ritorna l'oggetto ParametriEventiLayer relativo al layer scelto in combobox selectLayerChoose
	 */
	getCurrentSelectLayer: function () {
		if ((this.paramsJS.azioniApertura.modoEditingSingolo == null) ||(this.paramsJS.azioniApertura.modoEditingSingolo == ""))  {
			if(this.currentSelectedLayer==null) return null;
			return this.paramsJS.getParamJSLayer(this.currentSelectedLayer);
		} else {
			var ret = this.paramsJS.getParamJSLayer(this.paramsJS.azioniApertura.modoEditingSingolo.layerCODTPN);
			if (ret) return ret;
		}
		// non dovrebbe arrivare fino a qua a meno che non sia modoEditSingolo ed il corrispondente layer non ha azioni definite
		alert(ToloI18n.getMsg("ToloMapAPIExt.getCurrentSelectLayer.error",{LAYER: this.paramsJS.azioniApertura.modoEditingSingolo.layerCODTPN}));
		return null;
	},
			
	/**
	 * Method: onAnnullaSelezioni
	 * 
	 */
	onAnnullaSelezioni: function() {
		this.clearSelected(true);
		this.clearHighLigthed(true);
	},

	/**
	 * Method: onIdentify
	 * Esegue identify dell'oggetto correntemente selezionato, invocando la doEventActions con gli appositi parametri
	 */
	onIdentify: function () {	
		var azSelLayer = this.getCurrentSelectLayer();
		this.identify(azSelLayer);
		/*
		this.geoOpToPostVar(this.operationIdentify);
		this.geometryToPostVar ("");
		//ALE1
		// suppongo che un elemento del giusto layer sua selezionato altrimenti non sarebbe attivo il pulsante
		if(this.selezioneCorrente.getByCodTPN(azSelLayer.codTPN)){
			this.doEventActions(azSelLayer, this.eventVis,  this.selezioneCorrente.getByCodTPN(azSelLayer.codTPN).geometries[0].key);
			if (azSelLayer.chiudiSuDblClick) {
				close();
			}
		}
		*/
	},
	
	identify: function (layer){
		this.geoOpToPostVar(this.operationIdentify);
		this.geometryToPostVar ("");

		if(this.selezioneCorrente.getByCodTPN(layer.codTPN)){
			var selCorrente = this.selezioneCorrente.getByCodTPN(layer.codTPN).geometries[0];
			this.doEventActions(layer, this.eventVis,  selCorrente.key, undefined, undefined, undefined, selCorrente);
			
			if (layer.chiudiSuDblClick) {
				close();
			}
		}
	},

	/**
	 * Method: onCustomButtonPress
	 *
	 * Parameters:
	 * btn - {} il pulsante premuto.
	 */
	onCustomButtonPress: function (btn) {	
	
		//paramsJS.azioniEventi.eventiLayerList[layerPos].azioniEventiRicercaList.ricercaList[ricercaPos]
		var idBtn = btn.idCustomButton;
	
		//var cb = this.paramsJS.layOut.customButtonList[idBtn];
		if (((typeof(btn.pressFunction)=="undefined"))||(btn.pressFunction == '')) {
			var currLayer = this.getCurrentSelectLayer();
			
			if (currLayer!=null) { 
				this.geoOpToPostVar("");
				this.geometryToPostVar ("");
				var selCorrente = this.selezioneCorrente.getByCodTPN(currLayer.codTPN);
				var key = selCorrente != null ? selCorrente.geometries[0].key : null;
				this.doEventActions(currLayer, this.eventCustomButton, key, null, null, idBtn, undefined, undefined, selCorrente);
			} else {
				alert(ToloI18n.getMsg("ToloMapAPIExt.onCustomButtonPress.nolayer"));
			}
		} else {
			(new Function(btn.pressFunction)).call(this);
			//btn.pressFunction();			
		}
		
	},
	
	/**
	 * Method: onCustomButtonRelease
	 *
	 * Parameters:
	 * btn - {} il pulsante rilasciato.
	 */
	onCustomButtonRelease: function (btn) {	
		//paramsJS.azioniEventi.eventiLayerList[layerPos].azioniEventiRicercaList.ricercaList[ricercaPos]
		//var idBtn = btn.idCustomButton;
	
		//var cb = this.paramsJS.layOut.customButtonList[idBtn];
		if (((typeof(btn.releaseFunction)=="undefined"))||(btn.releaseFunction == '')) {
		//if (((btn.releaseFunction == null)||(btn.releaseFunction == ''))) {
				
		} else {
			(new Function(btn.releaseFunction)).call(this);
			//btn.releaseFunction();			
		}		
	},
	
	nuovoDaLayerStart: function() {
		var	layer = this.getCurrentSelectLayer();
		// Ricerca su selezione corrente le geometrie appartenenti ai layer che possono essere inserite sul layer corrente
		var jsGeometryList = new JSGeometryArray();
		var codTPNList = layer.azioniEventiInsFromLayer.enabledLayerList;
		for (var i=0; i< codTPNList.length ; i++) {
			var buff = this.selezioneCorrente.getByCodTPN(codTPNList[i].codTPN);
			jsGeometryList.add(buff);
		}
		var insFromLayerPanel = new TolomeoExt.ToloInsFromLayerPanel({
												jsGeometryList: jsGeometryList
												});
		
		
		insFromLayerPanel.on("geompartitemmouseenter", 
				function(record) {
				this.addHighlighted(record.get("val"));
			},
			this);
		
		insFromLayerPanel.on("geompartitemmouseleave", 
				function(record) {
				this.clearHighLigthed();
			},
			this);
				
		// Mostra finestra per scelta geometrie da inserire e per conferma
    	var insFromLayerWin =  Ext.create("Ext.Window",{
    		width: 300,
    		height: 300,
    		layout: 'fit',
    		constrain: true,
    		modal:true,
    		items: [insFromLayerPanel]
    		
    	});

		insFromLayerPanel.on("exitCancel", 
				function() {
					insFromLayerWin.close();
			},
			this);
    	
		insFromLayerPanel.on("exitOk", 
				function(geom, parts, bAll) {
					insFromLayerPanel.mask("attendere prego...");
					// Inizializza in postVar il tipo di operazione
					this.geoOpToPostVar(this.digitizeOperationInsertFromLayer);
					// Inizializza in postVar la geometria originale
					this.geometryToPostVar (geom);
					//Inizializza in postVar la/le geometrie da inserire
					this.geomPartsToPostVar(parts);
					//Inizializza in postVar il flag che indica se le geometrie sono tutte
					this.geomPartsAllFlagToPostVar(bAll);
					// Invoca la DoEventActions
					this.doEventActions(layer, this.eventInsFromLayer, null);
					// Chiude la finestra
					insFromLayerWin.close();
			},
			this);
		
		insFromLayerWin.show();
		
		
		
	},
	
	nuovoDaLayerStop: function(){
		
	},
	
	nuovoDaImportStart: function() {
		var thisPanel = this;
		var	layer = this.getCurrentSelectLayer();
		
		var tipoGeomDescr = '';
		switch (layer.tipoGeometria){
		  case 1: tipoGeomDescr = ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.tipoGeom.punti");
			break;
		  case 2: tipoGeomDescr = ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.tipoGeom.linee");
			break;
		  case 3: tipoGeomDescr = ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.tipoGeom.poligoni");
		   	break;
		}
		/*
		var labelTextTmpl = 'Scegli un file:<br/>'+
							'- compresso formato zip contenente shapefiles<br />' +
							'- dimensione massima: {0} MB<br />' +
							'- dimensione massima totale dei file contenuti: {1} MB<br />' +
							'- contenente esclusivamente {2}<br />'+
							'- contenente massimo {3} shapefile<br />' + 
							'- contenente complessivamente al massimo {4} geometrie<br />' +
							'- sistema di riferimento dei dati: {5}<br />' +
							'- contenente geometrie totalmente comprese all\'interno della coordinate<br/>{6}';
		
		var labelText = Ext.String.format(labelTextTmpl ,
				this.TOLOMEOServerSettings.importGeomUploadMaxSize, 
				this.TOLOMEOServerSettings.importGeomUploadUncompressedMaxSize,
				tipoGeomDescr,
				this.TOLOMEOServerSettings.importGeomUploadShpMaxNumber, 
				this.TOLOMEOServerSettings.importGeomUploadGeomsMaxNumber,
				this.paramsJS.mappe.SRID,
				'('+this.paramsJS.mappe.maxExtentLeft + ' ' +  this.paramsJS.mappe.maxExtentBottom + ' '+ this.paramsJS.mappe.maxExtentRight + ' ' + this.paramsJS.mappe.maxExtentTop +')');
		*/
		
		var labelText = ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.msg", {
			importGeomUploadMaxSize: this.TOLOMEOServerSettings.importGeomUploadMaxSize, 
			importGeomUploadUncompressedMaxSize: this.TOLOMEOServerSettings.importGeomUploadUncompressedMaxSize,
			tipoGeomDescr: tipoGeomDescr,
			importGeomUploadShpMaxNumber: this.TOLOMEOServerSettings.importGeomUploadShpMaxNumber, 
			importGeomUploadGeomsMaxNumber: this.TOLOMEOServerSettings.importGeomUploadGeomsMaxNumber,
			SRID: this.paramsJS.mappe.SRID,
			bbox: '('+this.paramsJS.mappe.maxExtentLeft + ' ' +  this.paramsJS.mappe.maxExtentBottom + ' '+ this.paramsJS.mappe.maxExtentRight + ' ' + this.paramsJS.mappe.maxExtentTop +')'
		});
		
		var formpanel = Ext.create('Ext.form.Panel', {
			url: this.TOLOMEOServer + this.TOLOMEOContext + '/ToJSGeometryServlet',
			bodyPadding: 10,
		    frame: true,
		    items: [{
		        xtype: 'filefield',
		        name: 'uploadfield',
		        allowBlank: false,
		        //blankText: "Seleziona un file .zip contentente gli shapefile da importare ",
		        labelAlign: 'top',
		        labelStyle: 'margin-bottom: 5px ',
		        fieldLabel: labelText,
		        labelSeparator: '',
		        //labelWidth: 50,
		        buttonText: ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.btnImporta"),
		        anchor: '100%',
		        regex: /\.zip$/i,
		        regexText: ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.regexText"),
		        msgTarget: 'under',
		        listeners:{
		            afterrender:function(cmp){
		              cmp.fileInputEl.set({
		                accept:'.zip,.ZIP' // or w/e type
		              });
		            }
		        },/*
		        reset: function () {
		            var me = this,
		                clear = me.clearOnSubmit;
		            if (me.rendered) {
		                me.button.reset(clear);
		                me.fileInputEl = me.button.fileInputEl;
		                me.fileInputEl.set({
		                    accept: '.zip,.ZIP,.txt'
		                });
		                if (clear) {
		                    me.inputEl.dom.value = '';
		                }
		                me.callParent();
		            }},*/
				buttonConfig: {
					text: ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.btnScegli"),
			        disabled: false,
			        iconCls: 'iconConferma',
			        //width: 75,
			        tooltip : {text: 'Scegli'}
				},
				buttonOnly: false},
				{ xtype: 'hidden', 
					 name: 'bbox', 
					 value: this.paramsJS.mappe.maxExtentLeft + ' ' +
					 	    this.paramsJS.mappe.maxExtentBottom + ' '+
					 	    this.paramsJS.mappe.maxExtentRight + ' ' +
					 	    this.paramsJS.mappe.maxExtentTop},
		 	   { xtype: 'hidden', 
					 name: 'tipoGeometria', 
					 value: layer.tipoGeometria }
				],
			buttons: [{
			        text: ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.btnChiudi"),
			        handler: function() {
			        	formpanel.up('window').close();
			        }
			    }, {
			        text: ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.btnInvia"),
			        formBind: true, //only enabled once the form is valid
			        disabled: true,
			        handler: function() {
			            var form = formpanel.getForm();
			            if (form.isValid()) {
			                form.submit({
			                	scope: thisPanel,
			                    success: function(form, action) {this.nuovoDaImportLoadSuccess(formpanel, action);},
			                    failure: function(form, action) {
			                    	
			                    	if (action.failureType === Ext.form.action.Action.CONNECT_FAILURE) {
			                            Ext.Msg.alert('Errore', action.response.responseText);
			                        }
			                        if (action.failureType === Ext.form.action.Action.SERVER_INVALID){
			                            // server responded with success = false
			                        	if (action.result && action.result.rows){
			                        		Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.errimp.title"), action.result.rows[0].msgErrore);
			                        	} else {
			                        		Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.err.title"), action.response.responseText);
			                        	}
			                        }
			                    	
			                        formpanel.up('window').close();
			                    }
			                });
			            }
			        }
			    }]});
		        
		Ext.create('Ext.window.Window',{
			width: 500,
			resizable: false,
			height: 300,
			constrain: true,
			modal:true,
			layout: 'fit',
			title: ToloI18n.getMsg("ToloMapAPIExt.nuovoDaImportStart.win"),
			items: [formpanel]
		}).show();
	},
	
	nuovoDaImportLoadSuccess: function(formpanel, action){
		
		var a = Ext.decode(action.response.responseText);
		var	layer = this.getCurrentSelectLayer();
		
		var store = new Ext.data.JsonStore({
		    proxy: {
		        type: 'memory',
		        reader: {
		            type: 'json'
		        }
		    },
		    data: a
		});
		
		store.load({
			scope: this,
			callback: function(records, loadOptions, success) {
				var geoms = new JSGeometryArray();
				geoms.FromStore(records, store);
				this.addHighlighted(geoms);
				this.zoomToHighlighted(null, 200);
				
				switch(geoms.size){
				case 0: // TODO gestire se ==0
					break;
				case 1:
					this.addSelected(geoms, false);
					break;
				default: 
					break;
				}
				
				
				var selectPanel = new TolomeoExt.ToloSelectGeoms({
					jsGeometryList: geoms
					});

				selectPanel.on("geomItemClick", 
						function(geom) {
							this.addHighlighted(geom);
							this.zoomToHighlighted(null, 200);
						},
				this);

				selectPanel.on("mostraPressed", 
						function() {
							this.zoomToHighlighted(null, 200);
						},
						this);
				/*
				selectPanel.on("selectNonePressed", 
						function() {
							this.clearHighLigthed();
						},
						this);
				
				selectPanel.on("selectAllPressed", 
						function() {
							this.addHighlighted(geoms);
						},
						this);
				*/
				selectPanel.on("selectedGeomsChange", 
						function(selGeoms) {
							this.clearHighLigthed();
							if (selGeoms.size()>0) this.addHighlighted(selGeoms);
						},
						this);

				// Mostra finestra per scelta geometrie da inserire e per conferma
				var selectWin =  Ext.create("Ext.Window",{
					width: 300,
					height: 300,
					layout: 'fit',
					constrain: true,
					modal:true,
					items: [selectPanel]				
				});

				selectPanel.on("exitCancel", 
						function() {
							this.clearHighLigthed();
							selectWin.close();
						},
						this);

				selectPanel.on("exitOk", 
						function(geom) {
							selectPanel.mask("attendere prego...");

							// Inizializza in postVar il tipo di operazione
							this.geoOpToPostVar(this.digitizeOperationInsertFromImport);
			
							// Inizializza in postVar la geometria originale
							this.geometryToPostVar (geom);
						
							// Invoca la DoEventActions
							this.doEventActions(layer, this.eventInsFromImport, null);
			
							this.clearHighLigthed();
							
							// Chiude la finestra
							selectWin.close();
					},	this);

				selectWin.show();

				formpanel.up('window').close();
			
			}
		});
		
		
		
		
				
		
	},
	
	nuovoDaImportStop: function(){
		
	},
	
	
	/**
	 * Method: onDigitizeStart
	 * Funzione per l'inizio della digitalizzazione.
	 *
	 * Parameters:
	 * digitizeOperation - {String} tipo di digitalizzazione richiesta (vedere costanti all'inizio della pagina paer valori possibili).
	 */
	onDigitizeStart: function (digitizeOperation) {
		
		this.currentDigitizeOperation = digitizeOperation;
		switch (digitizeOperation) {
	   		case this.digitizeOperationInsert:
	   			this.onDigitizeStartInsert();
	   		break;
	   		case this.digitizeOperationSubtract:
	   			this.onDigitizeStartSubtract();
	   		break;
	   		case this.digitizeOperationAdd:
	   			this.onDigitizeStartAdd();
	   		break;
	   		case this.digitizeOperationAddSub:
	   			this.onDigitizeStartAddSub();
	   		break;
	   		case this.digitizeOperationVertexEdit:
	   			this.onDigitizeStartVertexEdit();
	   		break;
	   		case this.digitizeOperationDragDrop:
	   			this.onDigitizeStartDragDrop();
	   		break;
	   	}
	},
		
	/**
	 * Method: onDigitizeStop
	 * Funzione per l'interruzione (annullamento) della digitalizzazione.
	 *
	 * Parameters:
	 * digitizeOperation - {String} digitizeOperation, tipo di digitalizzazione richiesta (vedere costanti all'inizio della pagina paer valori possibili).
	 */
	onDigitizeStop: function (digitizeOperation) {

		this.currentDigitizeOperation = digitizeOperation;
		switch (digitizeOperation) {
	   		case this.digitizeOperationInsert:
	   			this.onDigitizeStopInsert();
	   		break;
	   		case this.digitizeOperationSubtract:
	   			this.onDigitizeStopSubtract();
	   		break;
	   		case this.digitizeOperationAdd:
	   			this.onDigitizeStopAdd();
	   		break;
	   		case this.digitizeOperationAddSub:
	   			this.onDigitizeStopAddSub();
	   		break;
	   		case this.digitizeOperationVertexEdit:
	   			this.onDigitizeStopVertexEdit();
	   		break;
	   		case this.digitizeOperationDragDrop:
	   			this.onDigitizeStopDragDrop();
	   		break;
	   	}
	},
	
	/**
	 * Method: onDigitizeByCADStart
	 * Funzione per l'inizio della digitalizzazione per mezzo del CAD
	 *
	 * Parameters:
	 * digitizeOperation - {String} tipo di digitalizzazione richiesta (vedere costanti all'inizio della pagina paer valori possibili).
	 */
	onDigitizeByCADStart: function (digitizeOperation) {
		
		this.currentDigitizeOperation = digitizeOperation;
		switch (digitizeOperation) {
	   		case this.digitizeOperationInsert:
	   			this.onDigitizeByCADStartInsert();
	   		break;	   		
	   	}
	},
	
	/**
	 * Method: onDigitizeByCADStop
	 * Funzione per l'interruzione (annullamento) della digitalizzazione inizia per mezzo del CAD
	 *
	 * Parameters:
	 * digitizeOperation - {String} digitizeOperation, tipo di digitalizzazione richiesta (vedere costanti all'inizio della pagina paer valori possibili).
	 */
	onDigitizeByCADStop: function (digitizeOperation) {

		this.currentDigitizeOperation = digitizeOperation;
		switch (digitizeOperation) {
	   		case this.digitizeOperationInsert:
	   			this.onDigitizeByCADStopInsert();
	   		break;	   		
	   	}
	},

	/**
	 * Method: onUpdateAlfa
	 * Funzione chiamata alla fine della modifica alfanumerica. Innesca l'esecuzione delle azioni collegate a tale evento
	 */
	onUpdateAlfa: function () {
		var modLayer = this.getCurrentSelectLayer();
		this.geoOpToPostVar(this.operationUpdateAlfa);
		this.geometryToPostVar ("");
		//ALE1
		// suppongo che un elemento del giusto layer sua selezionato altrimenti non sarebbe attivo il pulsante
		var selCorrente = this.selezioneCorrente.getByCodTPN(modLayer.codTPN).geometries[0];
		this.doEventActions(modLayer, this.eventUpdateAlpha,  selCorrente.key, undefined, undefined, undefined, selCorrente);        
		this.clearSelected(modLayer.codTPN);
	},

	/**
	 * Method: onDelete
	 * Funzione chiamata per la cancellazione di un oggetto. Innesca l'esecuzione delle azioni collegate a tale evento. 
	 * Nel file xml l'azione vuota (default) provvede alla cancellazione dell'oggetto.
	 */
	onDelete: function () {

		var cancLayer = this.getCurrentSelectLayer();
		var messint = ToloI18n.getMsg("ToloMapAPIExt.onDelete.msg", {descrizioneLayer:cancLayer.descrizioneLayer});
		
		if (confirm(messint)) {			

			this.geoOpToPostVar(this.operationFeatureDelete);
			this.geometryToPostVar ("");
			//ALE1
			// suppongo che un elemento del giusto layer sua selezionato altrimenti non sarebbe attivo il pulsante
			var selCorrente = this.selezioneCorrente.getByCodTPN(cancLayer.codTPN).geometries[0];
			
			this.doEventActions(cancLayer, this.eventCanc,  selCorrente.key, undefined, undefined, undefined, selCorrente);
			
			var azioniEventi = this.getAzioniEventi(cancLayer,this.eventCanc, false);
			var redraw = true
			if(azioniEventi){
				redraw = azioniEventi.refreshAtTheEnd;
			}
			this.clearSelected(redraw, cancLayer.codTPN);
			
		} 
		
	},

	/**
	 * Method: onSelectLayerChange
	 * Funzione invocata ad ogni cambio del layer selezionato per le operazioni di editing. Provvede a chiamare le funzioni necessarie per aggiornare lo stato del sistema a questa nuova situazione
	 */
	// Chiamata da JSP quando viene cambiato il layer sul quale si vuole fare la selezione
	onSelectLayerChange: function () {
		// Modifica di conseguenza le operazioni possibili (icone)
		this.togglePermittedOperations();
	},

	// Funzioni che devono essere chiamate dal plugin alla fine della digitalizzazione

	/**
	 * Method: onDigitizeEndVertexEditing
	 * Funzione richiamata dal viewer alla fine dell'editing vertici 
	 *
	 * Parameters:
	 * geometry - {} la geometria.
	 */
	onDigitizeEndVertexEditing: function (geometry) {
		this.onDigitizeEndGeometry(geometry);
	},

	/**
	 * Method: onDigitizeEndDragDrop
	 * Funzione richiamata dal viewer alla fine del drag-drop 
	 *
	 * Parameters:
	 * geometry - {} la geometria.
	 */
	onDigitizeEndDragDrop: function (geometry) {
		this.onDigitizeEndGeometry(geometry);
	},

	/**
	 * Method: onDigitizeEndPolygon
	 * Funzione richiamata dal viewer alla fine della digitalizzazione di un poligono
	 *
	 * Parameters:
	 * geometry - {JSGeometry} la geometria.
	 */
	onDigitizeEndPolygon: function (geometry) {
		this.onDigitizeEndGeometry(geometry);
	},

	/**
	 * Method: onDigitizeEndCircle
	 * Funzione richiamata dal viewer alla fine della digitalizzazione di un cerchio
	 *
	 * Parameters:
	 * geometry - {JSGeometry} il centro.
	 * radius - {} il raggio.
	 */
	onDigitizeEndCircle: function (center, radius) {},

	/**
	 * Method: onDigitizeEndLine
	 * Funzione richiamata dal viewer alla fine della digitalizzazione di una linea
	 *
	 * Parameters:
	 * geometry - {JSGeometry} la geometria.
	 */
	onDigitizeEndLine: function (geometry) {
		this.onDigitizeEndGeometry(geometry);
	},

	/**
	 * Method: onDigitizeEndPoint
	 * Funzione richiamata dal viewer alla fine della digitalizzazione di un punto
	 *
	 * Parameters:
	 * geometry - {JSGeometry} la geometria.
	 */
	onDigitizeEndPoint: function (geometry) {
		
		this.onDigitizeEndGeometry(geometry);
	},

	/**
	 * Method: onMappaViewChanged
	 * Funzione richiamata dal viewer quando la visualizzazione cambia per quanche motivo
	 */
	onMappaViewChanged: function () {
		this.smallMapSetPosition();
	},

	/**
	 * Method: onMappaSelect
	 * Funzione che il viewer deve chiamare quando in modalità selezione e 
	 * viene fatto click sulla mappa	
	 *
	 * Parameters:
	 * point - {Point} point, Punto clickato sulla mappa (in coordinate).
	 * selectionMode - {String} modalità di selezione ["firstOnTop","allStacked"]
	 * addToSelected - {Boolean} true per aggiungere alle selezioni già presenti (vale per layers diversi)
	 * visualize - {Boolean} true per visualizzare sul data panel le informazioni correlate alla selezione fatta
     * mapXPixel - {Number} coordinata x in pixel cliccata sulla mappa
     * mapYPixel - {Number} coordinata y in pixel cliccata sulla mappa
	 * codTpnToForceSelection - {Number} codTPN su cui forzare la selezione se si è scelto un selectionMode
	 */
	onMappaSelect: function (point,selectionMode,addToSelected, visualize, mapXPixel, mapYPixel,codTpnToForceSelection) {

		// TODO supportata solo mappa 0
		var nmappa = 0;
		var mappa = this.paramsJS.mappe.mappaList[nmappa];
		var codTPN;
		var additionalWMSLayers = [];
		
		// Se è impostata una modalità di selezione controllo legenda e/o lista layers interrogabili
		if(selectionMode || codTpnToForceSelection){
			// Se c'è la legenda l'impilamento dipende dai layer visibili su di essa
			if(this.TOCPanel){
				
				var queryableLayers = [];
				
				// Se si è chiesto di forzare la selezione su un un certo layer non considero gli altri selezionabili 
				if(codTpnToForceSelection){
					
					if(this.paramsJS.isSelectable(codTpnToForceSelection)){
						queryableLayers.push(codTpnToForceSelection);
						
						// Se il layer chiesto è selezionabile, ma non visibile lo accendo
						if(!this.TOCPanel.layerIsVisible(codTpnToForceSelection)){
							this.TOCPanel.setLayerVisibility(codTpnToForceSelection);
						}
					}
										
				} else {
					
					var visibleLayers = this.TOCPanel.getVisibleLayers();				
					
					for(var i=0; i<visibleLayers.length; i++){
						var visibleLayer = visibleLayers[i];
						if (this.paramsJS.isSelectable(visibleLayer.codTPN)){
							if ((queryableLayers.indexOf(visibleLayer.codTPN)==-1) &&
								 !visibleLayer.isUserLayer ){
									queryableLayers.push(visibleLayer.codTPN);
							}
						}
					}
					
				}

				// Aggiunta di eventuali layer aggiunti dall'utente
				var userWMSList = this.TOCPanel.getVisibleUserWMSList();
				for (var i=0; i< userWMSList.length; i++) {
					var idxObj = userWMSList[i]; 
					var currCat = this.TOCPanel.tocInfo.getCategoryInfo(idxObj.catTreeIdx);
					var currLayer =  currCat.layers[idxObj.layTreeIdx];
					if (currLayer.serverID) { 
					//var server = this.TOCPanel.tocInfo.getServer(currLayer.serverID);
					
						if (currLayer.queryable) {
							var currUrl = (currLayer.url) ? currLayer.url : this.paramsJS.getServer(currLayer.serverID, mappa).url;
							//var infoformat ="application/vnd.ogc.gml";
							
							var infoformat = "";
							var formats = currLayer.getFeatureInfoFormats;
							// Se abilitato GML cerca se c'e'
							if (this.paramsJS.comportamento.wmsUtente.usaGML) {
								
								for (var j=0; j < formats.length; j++ ) {
									var currFormat = formats[j];
									if (currFormat.indexOf("gml") != -1) {
										infoformat = currFormat;
										if (infoformat == "text/gml") {
											break;
										}
									}
								}
							}	
							// Se non disponibile formato gml o non abilitato sul portale tolomeo
							// si accontenta di un formato text con preferenza di text/html
							if (infoformat == "") {
								for (var j=0; j < formats.length; j++ ) {
									var currFormat = formats[j];
									if (currFormat.indexOf("text/") != -1) {
										infoformat = currFormat;
										if (infoformat == "text/html") {
											break;
										}
									}
								}	
							}
							// Se disponibile un formato aggiunge il layer alla lista di quelli interrogabili
							if (infoformat != "") {
								// Workaround per evitare che venga preso versione GML3
								// Andrebbe gestito meglio, rendendo anche la parte server in grado di gestire le versioni gml successive alla 2
								// nella classe GetFeatureInfoLayer del package sit
								if (infoformat.indexOf("application/vnd.ogc.gml") != -1) {
									infoformat = "application/vnd.ogc.gml";
								}
								var entry = {
										url: currUrl,
										wmsname: currLayer.name,
										codTPN: currLayer.codTPN,
										descrizione: currLayer.descr,
										infoformat: infoformat
									};
								queryableLayers.push(currLayer.codTPN);
								additionalWMSLayers.push(entry);	
							}
						}
					}
				}
				
				if ((queryableLayers.length == 0) && 
					 additionalWMSLayers.length == 0) {
					Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.onMappaSelect.title"),
							ToloI18n.getMsg("ToloMapAPIExt.onMappaSelect.msg"));
					this.ajaxQuerySelectOK(null);
					return;
				}			
				
				codTPN = queryableLayers.join(",");
				
				// Se si chiede di forzare la selezione su un certo codTPN si fa in modo di passare solo quello se presente fra i layer interrogabili
				/*
				if(codTpnToForceSelection){
					if(queryableLayers.length > 0 && codTPN.indexOf(codTpnToForceSelection) != -1){
						codTPN = codTpnToForceSelection;
						additionalWMSLayers.length = 0;
					} else if(additionalWMSLayers.length > 0 && additionalWMSLayers.join(",").indexOf(codTpnToForceSelection) != -1) {
						additionalWMSLayers = [];
						additionalWMSLayers[0] = codTpnToForceSelection;
						codTPN = "";
					}
				}
				*/
			} else {
				codTPN = this.paramsJS.getSelectableCodTPN().join(",");
			}
		} else {
			codTPN = this.getCurrentSelectLayer().codTPN;
		}
		
		var buff = codTPN.split(",");
		var stylesArr = new Array();
		for (var i=0; i<buff.length; i++) {
				var legendaInfo = this.paramsJS.getLegendaLayerInfoByCodTPN(buff[i], nmappa, (this.TOCPanel ? this.TOCPanel.tocInfo : null))			
				var stylebuff = (legendaInfo.tocInfoLayerInfo) ? legendaInfo.tocInfoLayerInfo.style : (legendaInfo.presetLayerInfo ? legendaInfo.presetLayerInfo.defaultStyle : "");  			
				stylesArr.push(stylebuff);
		}
		var styles = stylesArr.join(","); 
		
		// raggio di tolleranza 6 pixel
		var tolleranceRange = this.viewer.pluginGetResolution() * 6;
			
		var bounds = this.viewer.pluginGetMapExtent();
		
		// Chiamata Ajax per effettuare l'intersezione e ricevere l'oggetto selezionato
		var ajaxOptions = { method: 'post',
			url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxSpatialQueryServlet',
			params: {
				dtInizioFiltro: this.temporalFilterDtInizio,
				dtFineFiltro: this.temporalFilterDtFine,
				customQueryParams: Ext.JSON.encode(this.getCustomQueryParams()[nmappa]),
				coordX: point.x, 
				coordY: point.y,
				codTPN: codTPN,
				styles: styles,
				range: tolleranceRange,
				SRID: this.paramsJS.mappe.SRID,
				format: 'ext',
				selectionMode: selectionMode,
				//Parametri aggiunti per GetFeatureInfo
				bbox:  bounds.left+","+bounds.bottom+","+bounds.right+","+bounds.top ,
				mapwidth:  this.viewer.pluginGetMapWidth()  ,
				mapheight: this.viewer.pluginGetMapViewerHeight() ,
				X: mapXPixel,
				Y: mapYPixel,
				additionalWMSLayers: (additionalWMSLayers && additionalWMSLayers.length > 0) ? Ext.JSON.encode(additionalWMSLayers) : undefined,
				paramPreset: this.paramsJS.nomePreset
			},
			success: function(results, store){this.ajaxQuerySelectOK(results, store, addToSelected, visualize);},
			failure: function(transport){
				this.fireEvent('selectRequestEnd',{
					ok:false,
					nResults:0,
					errText:transport.responseText?transport.responseText:""+transport
				});
				this.showAjaxError(transport);
			}, 
			scope: this
		}
		
		// Aggiunta parametri WMS da url
		Ext.apply(ajaxOptions.params, this.paramsJS.urlAdditionalParams);
				
		if(this.fireEvent('selectRequestBeforeStart')){
			new TolomeoExt.ToloCrossAjax().request(ajaxOptions);
			this.fireEvent('selectRequestStart');
		}
		this.onBusy(true);
	},	

	/**
	 * Method: ajaxQuerySelectOK
	 * Funzione di callback per la chiamata ajax onMappaSelect {link #onMappaSelect} che identifica gli oggetti presenti in una certa posizione
	 *
	 * Parameters:
	 * results - {} il risultato della richiesta.
	 * store - {} lo store dei dati.
	 */
	ajaxQuerySelectOK: function (results, store, addToSelected, visualize) {
		
		if(results){		
			var geoms = new JSGeometryArray();
			geoms.FromStore(results, store);			
			
			if (geoms.geometries.length==0) {
				this.clearSelected();
			} else {
				this.addSelected(geoms, addToSelected, visualize);	
			}
		} 
		
		this.fireEvent('selectRequestEnd',{
			ok:true,
			nResults:results?geoms.geometries.length:0,
			errText:null
		});		
		
		this.onBusy(false);	
		
		//this.fireEvent('onObjectSelect', geoms);				
		/*
		var selLayer = this.getCurrentSelectLayer();
		if (selLayer.azioniEventiVis.autoVisOnSelect) {
			this.onIdentify();
		}
		*/
	
	    this.fireEvent('onOperationPressDefault', 2);
	},

	/**
	 * Method: showAjaxError
	 * Visualizza messaggio di errore in caso di errore Ajax
	 *
	 * Parameters:
	 * transport - {} transport, risposta della chiamata ajax.
	 * store - {} store.
	 */
	showAjaxError: function (transport) {
		this.onBusy(false);
		if(transport.responseText){
			alert(transport.responseText);
		}else{
			alert(transport);
		}
	},

	// sarebbero da gestire come metodi un oggetto mappa
	//TODO
	//Funzioni di busy

	/**
	 * Property: mapBusy
	 * {Number} 
	 */
	mapBusy: 0,
	
	/**
	 * Method: isBusy
	 * 
	 * Returns:
	 * {Boolean}
	 */
	isBusy: function () { return this.mapBusy != 0; },
	
	/**
	 * Method: onBusy
	 *
	 * Parameters:
	 * areYouBusy - {} areYouBusy.
	 */
	onBusy: function (areYouBusy){
		/* TODO
		if(areYouBusy){
			this.mapBusy++;
			this.refreshBusy();
			//if (this.myMask) this.myMask.show();
		}else{
			this.mapBusy--;
			if(!this.isBusy()){
				this.refreshBusy();
				//if (this.myMask) this.myMask.hide();
			}
		}
		*/
	},
		
	/**
	 * Method: noneBusy
	 * 
	 */
	noneBusy: function (){
		/* TODO
		this.mapBusy = 0;
		if (this.myMask) this.myMask.hide();
		*/
	},
	
	/**
	 * Method: refreshBusy
	 * 
	 */
	refreshBusy: function() {
		/* TODO
		if (this.myMask) 
		if (this.mapBusy) this.myMask.show();
		else this.myMask.hide();
		*/
	},
	
	/**
	 * Method: onScaleChange
	 * Chiamato dal plugin ogni volta che cambia il livello di zoom
	 */
	onScaleChange: function () {
		updateTocScale(this.viewer.pluginGetCurrentZoom());
		updateZoomToScale(this.viewer.pluginGetCurrentZoom());
	},

	/**
	 * Method: addHighlighted
	 * Sulla mappa e' possibile evidenziare degli oggetti. Questo e' utile, per esempio, nelle ricerche, per mostrare quale e' l'oggetto trovato.
	 * Questa funzione consente di evidenziare l'oggetto, di aggiornare di conseguenza la mappa e di fare le altre azioni necessarie.
	 * L'attuale implementazione prevede che un solo oggetto possa essere evidenziato, quindi ogni nuovo va a sostituirsi a quello eventualmente presente.
	 *
	 * Parameters:
	 * geoms - {JSGeometryArray o JSGeometry} oggetto da evidenziare se passato un JSGeometryArray viene utilizzato il primo.
	 * bMulti - {boolean} se non definito o false non è consentita la presenza di più di un oggetto, se True è consentita.
	 */
	addHighlighted: function addHighlighted(geoms, bMulti) {
		var geom;
		
		if (!bMulti) this.evidenziazioneCorrente.clear();
		
		this.evidenziazioneCorrente.add(geoms);
		
		//accendo il layer sul quale eseguo l'highlighted nel caso fosse spento in legenda...
		var codTPN = this.evidenziazioneCorrente.geometries[this.evidenziazioneCorrente.geometries.length-1].codTPN;
		if (codTPN && this.TOCPanel) {
			// se codTPN non è nullo e non è zero
			this.TOCPanel.setLayerVisibility(codTPN);
		}
		
		if (this.viewer!=null)
			this.viewer.pluginAddHighlighted(this.evidenziazioneCorrente, bMulti);
			
		/* Vecchia versione monooggetto
		if(geoms instanceof JSGeometryArray){
			this.evidenziazioneCorrente = geoms.geometries[0];
		}else if(geoms instanceof JSGeometry){
			this.evidenziazioneCorrente = geoms;
		}else{
			alert("tipo sconosciuto " + geoms);
			return;
		}
		
		if (this.viewer!=null)
			this.viewer.pluginAddHighlighted(this.evidenziazioneCorrente);*/
	},

	/**
	 * Method: clearHighLigthed
	 * Svuota la evidenziazione corrente, 
	 * deselezionando gli oggetti dalla mappa ed effettuando il resto delle operazioni necessarie (come l'aggiornamento delle operazioni di editing, interrogazione etc. possibili)
	 *
	 * Parameters:
	 * bRedraw - {boolean} Se non definito o false non è consentita la presenza di più di un oggetto, se True è consentita.
	 */
	clearHighLigthed: function (bRedraw) {
	
		this.evidenziazioneCorrente.clear();
		if (this.viewer.pluginClearHighlighted(bRedraw)) this.viewer.pluginRefreshMap();
		
		/* Vecchia versione monooggetto
		this.evidenziazioneCorrente = null;
		if (this.viewer.pluginClearHighlighted(bRedraw)) this.viewer.pluginRefreshMap();
		*/
	},

	/**
	 * Method: encodeToggleGroup
	 *
	 * Parameters:
	 * geoms - {JSGeometryArray o JSGeometry} Oggetto da evidenziare, se passato un JSGeometryArray viene utilizzato il primo.
	 */
	addAutoidentified: function (geoms) {
		if(geoms instanceof JSGeometryArray){
			this.autoIdentifyCorrente = geoms;
		}else if(geoms instanceof JSGeometry){
			this.autoIdentifyCorrente = geoms.add(geoms);
		}else{
			alert(ToloI18n.getMsg("ToloMapAPIExt.addAutoidentified.msg"));
			return;
		}
		this.viewer.pluginAddAutoidentified(this.autoIdentifyCorrente);
	},

	/**
	 * Method: clearAutoidentified
	 * Svuota la evidenziazione corrente, deselezionando gli oggetti dalla mappa ed effettuando il resto delle operazioni necessarie (come l'aggiornamento delle operazioni di editing, interrogazione etc. possibili)
	 *
	 * Parameters:
	 * bRedraw - {boolean} bRedraw
	 */
	clearAutoidentified: function (bRedraw) {
	
		this.autoIdentifyCorrente = null;
		if (this.viewer.pluginClearAutoidentified(bRedraw)) this.viewer.pluginRefreshMap();
	},

	/**
	 * Method: addSelected
	 * Sulla mappa e' possibile selezionare degli oggetti per sottoporli ad editing, interrogazione o altro. Le operazioni possibili sono controllate da configurazione.
	 * Questa funzione consente di selezionare l'oggetto, di aggiornare di conseguenza la mappa e di fare le altre azioni necessarie.
	 * L'attuale implementazione prevede che un solo oggetto possa essere evidenziato, quindi ogni nuovo va a sostituirsi a quello eventualmente presente.
	 *
	 * Parameters:
	 * geoms - {JSGeometryArray o JSGeometry} Oggetto da evidenziare, se passato un JSGeometryArray viene attivata una combobox sull'interfaccia grafica per permettere di scegliere.
	 *  bClearUrl - Indica se resettare l'eventuale pagina di identify 
	 * Quando la scelta è stata fatta viene chiamata la funzione onSelectedFromChoice()
	 */
	addSelected: function (geoms, addToSelected, visualize, bClearUrl) {
		
		var jsGeoArr;
		
		if(geoms instanceof JSGeometry){
			jsGeoArr = new JSGeometryArray();
			jsGeoArr.add(geoms); 
		}else{
			jsGeoArr = geoms;
		}
						
		if (jsGeoArr.geometries.length==1) {
			
			if(!addToSelected){
				this.clearSelected(false, undefined, bClearUrl);
			}else if (this.selezioneCorrente.ContainsCodTPN(jsGeoArr.geometries[0].codTPN)) {			
			// In ogni momento e' possibile che ci sia un solo oggetto selezionato per ogni codTPN
			// Questo vincolo e' stato messo per semplificare l'utilizzo
			// Verifica se già presente un oggetto dello stesso codTPN
				// lo cancella
				this.clearSelected(false, jsGeoArr.geometries[0].codTPN, bClearUrl);
			}
			
			//ALE1 selezioneCorrente = jsGeoArr.geometries[0];
			this.selezioneCorrente.add(jsGeoArr.geometries[0]);
			this.viewer.pluginAddSelected(jsGeoArr.geometries[0]);
			
			this.applyCustomQuery();
								
			if(this.paramsJS.isSelectable(jsGeoArr.geometries[0].codTPN)){
				this.setCurrentSelectLayer(jsGeoArr.geometries[0].codTPN);
			}
			this.fireEvent('onObjectSelect', geoms);
			
			var selLayer = this.getCurrentSelectLayer();
			if (selLayer && selLayer.azioniEventiVis && (selLayer.azioniEventiVis.autoVisOnSelect || visualize)) {
				this.onIdentify();
			}
			
		} else if (jsGeoArr.geometries.length>1) {
			
			var dataArray = [];
			
			var descrizioneLayer = null;
			var currentCodTPN = null;
			var z_index = 1000;
			for(var i=0; i<jsGeoArr.geometries.length;i++){
				var jsG = jsGeoArr.geometries[i];
				if(!currentCodTPN || currentCodTPN!=jsG.codTPN){
					descrizioneLayer = this.paramsJS.getParamJSLayer(jsG.codTPN).descrizioneLayer;
					z_index++;
				}
				dataArray.push([z_index+descrizioneLayer,jsG.codTPN,jsG.description,jsG]);
				currentCodTPN = jsG.codTPN;
			}
			
		    var store = Ext.create('Ext.data.ArrayStore',{
		    		fields: [
				       {name: 'z_index'},
				       {name: 'codTPN'},
				       {name: 'objectDescription'},
				       {name: 'jsGeometry'}
				    ], 
		            data: dataArray,
		            sorters:[{property: 'objectDescription', direction: "ASC"}],
		            groupField:'z_index'
		        });
		    
		    var listView = Ext.create('Ext.grid.Panel',{ 
		        store: store, 
		        features: [{ftype:'grouping', groupHeaderTpl: ['{name:this.formatName}',
														        {
														            formatName: function(name) {
														                return name.substr(4);
														            }
														        }]}],
		        emptyText: ToloI18n.getMsg("ToloMapAPIExt.addSelected.emptyText"),
		        startCollapsed : false, 
		      
		        columns: [		        	
		        	{text: ToloI18n.getMsg("ToloMapAPIExt.addSelected.desc"),dataIndex:'objectDescription',flex: 1, menuDisabled: true, sortable: false},
		        	{text: ToloI18n.getMsg("ToloMapAPIExt.addSelected.layer"),dataIndex:'layerDescription', hidden: true, menuDisabled: true, width:0}
		        	
		        ],
		        listeners: {
					itemmouseenter: {
						fn: function (dv, index, node, e) {
        					var record = dv.getRecord( node ).data;
        					this.onChangeFromChoice(record.jsGeometry);
        					return true;
        				},
        			  	scope: this
					},
					itemmouseleave: {
						fn: function (dv, index, node, e) {
							var record = dv.getRecord( node ).data;
							this.clearHighLigthed();
							return true;
						},
						scope: this
					},
					itemclick: {
						fn: function (dv, index, node, e) {
							var record = dv.getRecord( node ).data;
							var selected = record.jsGeometry;
							
							selected.relatedGeoms = jsGeoArr.getByCodTPN(selected.codTPN);
							this.onSelectedFromChoice(selected,addToSelected,visualize);
							return true;
						},
					  	scope: this
					}
				}
			});	    	
		    		    
			if(this.selectedChoiceWindow){
				this.selectedChoiceWindow.close();
			} 
			
			this.selectedChoiceWindow = Ext.create('Ext.Window', {
				title: ToloI18n.getMsg("ToloMapAPIExt.addSelected.win"),
				x: 50,
				width: 350,
				height: 300,
				layout:'fit',
				autoScroll: true,
				constrain: true,
				bodyStyle: 'background-color:white',
				items: [listView]
			}).show();
		}
	},
	
	/**
	 * Method: queryingById
	 * Fa un interrogazione ajax al server per recuperare la geometria dell'oggetto.
	 * Come parametri per l'interrogazione utilizza il codTPN e l'IDTPN ed accetta 
	 * come, parametri ulteriori, le funzioni da richiamare in caso di successo o di
	 * fallimento. Se IDTPN è un array verranno cercati tutti gli ID contenuti
	 *
	 * Parameters:
	 * codTPN - {String} codTPN
	 * IDTPN - {String or Array} IDTPN
	 * onSuccess - {Function} onSuccess
	 * onFailure - {Function} onFailure
	 * scope - {Object} contesto delle chiamate di callback
	 */
	queryingById: function(codTPN, IDTPN, onSuccess, onFailure, scope){
		/** {String} */
		var ids = "";
		if (IDTPN instanceof Array) {
			for (var i=0; i<IDTPN.length; i++) {
				ids += ((ids=="") ? "" : "||")  + IDTPN[i] ;
			}
		} else {
			ids=IDTPN;
		}
		
		var ajaxOptions = {
			url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxQueryByIDServlet',
			method: 'post',
			params: {
				codTPN: codTPN, 
				IDTPN: ids, 
				SRID: this.paramsJS.mappe.SRID, 
				format: 'ext',
				paramPreset: this.paramsJS.nomePreset
				},
			success: onSuccess,
			failure: onFailure || this.showAjaxError,
			scope: scope || this
		}

		// Aggiunta parametri WMS da url
		Ext.apply(ajaxOptions.params, this.paramsJS.urlAdditionalParams);
		
		new TolomeoExt.ToloCrossAjax().request(ajaxOptions);
	},
	
	/**
	 * Method: addSelectedByID
	 * Aggiunge a selected interrogando via ajax il layer per codTPN ed IDTPN 
	 *
	 * Parameters:
	 * codTPN - {String} codTPN
	 * IDTPN - {String or Array} IDTPN
	 * bClearUrl - Indica se resettare l'eventuale pagina di identify 
	 *
	 * Returns:
	 * {<JSGeometryArray>}
	 */
	addSelectedByID: function (codTPN, IDTPN, bClearUrl) {		
		this.queryingById(codTPN, IDTPN, function(res) { this.doAddSelectedByIDAjaxCallback(res, bClearUrl); }, this.doAddSelectedByIDAjaxFailure, this);
	},
	
	/**
	 * Method: zoomToSelected
	 * Esegue lo zoom ad una zona selezionata.
	 *
	 * Parameters:
	 * scale - {Number} zoom, se valorizzato viene fatto lo zoom alla data scala
	 * buffer - {Number} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	 */
	zoomToSelected: function(zoom, buffer) {
		this.viewer.pluginZoomToSelected(zoom, buffer);
	},
	
	/**
	 * Method: centerOnSelected
	 * Riposiziona la mappa mettendo al centro la feature selezionata e mantenendo la scala attuale.
	 */
	centerOnSelected: function(){
		var zoom = this.viewer.pluginGetCurrentZoom();
		this.zoomToSelected(zoom);
	},
	
	/**
	 * Method: zoomToHighlighted
	 * Esegue lo zoom ad una zona evidenziata.
	 * 
	 * Parameters:
	 * zoom - {Number} il valore di zoom.
	 * buffer - {Integer} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	 */
	zoomToHighlighted: function(zoom, buffer) {
		this.viewer.pluginZoomToHighlighted(zoom, buffer);
	},
	
	/**
	 * Method: centerOnHighlighted
	 * Riposiziona la mappa mettendo al centro la feature evidenziata e mantenendo la scala attuale.
	 */
	centerOnHighlighted: function(){
		var zoom = this.viewer.pluginGetCurrentZoom();
		this.zoomToHighlighted(zoom);
	},
	
	/**
	 * Method: zoomToAutoidentified
	 *
	 * Parameters:
	 * zoom - {Number} il valore di zoom.
	 */
	zoomToAutoidentified: function(zoom) {
		this.viewer.pluginZoomToAutoidentified(zoom);
	},	
	
	/**
	 * Method: zoomToExtent
	 * Esegue lo zoom alla massima estenzione della mappa.
	 * 
	 * Parameters:
	 * geometry - {Mixed} Stringa wkt della geometria o oggetto di tipo BBox
	 * buffer - {Number} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	 */
	zoomToExtent: function(geometry, buffer) {
		this.viewer.pluginZoomToExtent(geometry, buffer);
	},	
	
	/**
	 * Method: zoomToScale
	 * esegue lo zoom ad una specifica scala.
	 * 
	 * Parameters:
	 * scale - {Number} il valore di scala.
	 */
	zoomToScale: function (scale){
		this.viewer.pluginZoomToScale(scale);
	},
	
	/**
	 * Method: zoomToObj
	 * Esegue uno zoom all'oggetto dopo aver interrogato il server per recuperare la geometria
	 * per mezzo di codTPN e IDTPN.
	 * Se il parametro selectIt è impostato a true l'oggetto viene selezionato, se = false viene evidenziato 
	 * Se bMulti == true e selectIt==false viene abilitata l'evidenziazione multipla (l'oggetto viene evidenziato insieme agli altri eventualmente già evidenziati) 
	 *
	 * Parameters:
	 * codTPN - {String} codice del layer
	 * IDTPN - {String o Array} id della feature
	 * selectIt - {Boolean} se true viene selezionato
	 * scale - {Integer} scala, se valorizzato viene fatto lo zoom alla data scala
	 * buffer - {Integer} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	 */
	zoomToObj: function(codTPN, IDTPN, selectIt, scale, buffer) {
		var onSuccess = function(res) { this.doZoomToObjAjaxCallback(res, selectIt, scale, buffer); };
		this.queryingById(codTPN, IDTPN, onSuccess, this.showAjaxError);	
	},

	/**
	 * Method: doZoomToObjAjaxCallback
	 * Funzione di callback della chiamata Ajax che effettua la zoomToObj
	 *
	 * Parameters:
	 * res - {} risposta della chiamata ajax
	 * selectIt - {Boolean} se true viene selezionato
	 * scale - {Integer} scala, se valorizzato viene fatto lo zoom alla data scala
	 * buffer - {Integer} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	 */
	doZoomToObjAjaxCallback: function(res, selectIt, scale, buffer) {
		var geoms = new JSGeometryArray();
		geoms.FromUntypedArray(res[0].data);
		
		if (geoms.geometries.length>0) {
			if(selectIt && this.paramsJS.isSelectable(geoms.geometries[0].codTPN)) {
				this.clearSelected();			
				this.addSelected(geoms);
				this.zoomToSelected(scale, buffer);
			} else {
				this.clearHighLigthed();
				this.addHighlighted(geoms);
				this.zoomToHighlighted(scale, buffer);
			}
		}
	},
	
	/**
	 * Method: doAddSelectedByIDAjaxCallback
	 * Funzione di callback della chiamata Ajax che effettua la addSelectedByID 
	 *
	 * Parameters:
	 * res - {} risposta della chiamata ajax
	 *  bClearUrl - Indica se resettare l'eventuale pagina di identify 
	 */
	doAddSelectedByIDAjaxCallback: function (res, bClearUrl) {
		//var geoms = new JSGeometryArray(transport.responseText);
		var geoms = new JSGeometryArray();
		geoms.FromUntypedArray(res[0].data);
		 
		this.addSelected(geoms, undefined, undefined, bClearUrl);
	},

	/**
	 * Method: doAddSelectedByIDAjaxFailure
	 * Funzione di gestione errore avvenuto nella chiamata Ajax che effettua la ricerca
	 *
	 * Parameters:
	 * transport - {} risposta della chiamata ajax
	 */
	doAddSelectedByIDAjaxFailure: function (transport) {
		//TODO CrossAjax riadattare
		showAjaxError(transport);
	},

	/**
	 * Method: clearSelected
	 * Svuota la selezione corrente, deselezionando gli oggetti dalla mappa ed effettuando il resto delle operazioni necessarie (come l'aggiornamento delle operazioni di editing, interrogazione etc. possibili)
	 * Nel caso che sia definito codTPN l'operazione viene fatta solo sugli oggetti del layer corrispondente
	 *
	 * Parameters:
	 * bRedraw - {Boolean} bRedraw
	 * codTPN - {Integer} codTPN
	 *  bClearUrl - Indica se resettare l'eventuale pagina di identify 
	 */
	clearSelected: function (bRedraw, codTPN, bClearUrl) {
		var doit = this.fireEvent('onBeforeClearSelected', bRedraw, codTPN, bClearUrl);
		if (doit) {
			var removed = this.selezioneCorrente.clear(codTPN);
			if (bClearUrl==undefined || bClearUrl==null) bClearUrl = true; 

			// Elimina eventuale pagina di identify
			if (window.pannello && bClearUrl) this.clearURL ("pannello", "GET");		
			
			if (bRedraw) this.applyCustomQuery();
			if (this.viewer.pluginClearSelected(bRedraw, codTPN)) this.viewer.pluginRefreshMap();
			// Modifica di conseguenza le operazioni possibili (icone)
			//TODO
			this.togglePermittedOperations();
			
			this.fireEvent('onAfterClearSelected', bRedraw, codTPN, bClearUrl);
			
			return removed;	
		} else {
			return [];
		}
		
	},

	/**
	 * Method: refreshSelected
	 * Aggiorna la selezione rileggendo dal layer la geometria attualmente selezionata
	 */
	refreshSelected: function () {
		if (this.selezioneCorrente.size()!=0) {
			
			var buff = new Array();
			for (var i=0;i<this.selezioneCorrente.geometries.length;i++) {
				var elem = new Object();
				elem.codTPN = this.selezioneCorrente.geometries[i].codTPN;
				elem.key = this.selezioneCorrente.geometries[i].key;
				buff.push(elem);
			}
			
			this.clearSelected(undefined, undefined, false);
			for (var i=0;i<buff.length;i++) {
				this.addSelectedByID(buff[i].codTPN, buff[i].key, false);
			}
		}
		
	},
	
//	/**
//	 * Method: zoomToExtent
//	 * Visualizza sulla mappa l'intero extent passato 
//	 *
//	 * Parameters:
//	 * geometryWKT - {} rappresentazione WKT della geometria da inquadrare
//	 */
//	zoomToExtent: function (geometryWKT){
//		this.viewer.pluginZoomToExtent(geometryWKT);
//	},
		
	/**
	 * Method: addMarker 
	 * Aggiunge un marker alla  mappa.
	 * 
	 * Parameters:
	 * x - {Integer} x
	 * y - {Integer} y
	 */
	addMarker: function(x,y) {
		this.viewer.pluginAddMarker(x,y);
	},
	
	/**
	 * Method: setMarker 
	 * Imposta un marker sulla  mappa, cancellando markers esistenti
	 * 
	 * Parameters:
	 * x - {Integer} x
	 * y - {Integer} y
	 */
	setMarker: function(x,y) {
		this.viewer.pluginAddMarker(x,y,null,true);
	},
	
	/**
	 * Method: clearMarkers
	 * Rimuove un marker dalla mappa.
	 *
	 * Parameters:
	 * x - {Integer} x
	 * y - {Integer} y
	 */
	clearMarkers: function(x,y) {
		this.viewer.pluginClearMarkers();
	},
	
	/**
	 * Method: addPopup 
	 * Aggiunge un popup alla  mappa.
	 * 
	 * Parameters:
	 * x - {Integer} x
	 * y - {Integer} y
	 * htmlText - {String} testo html
	 * isUnique - {boolean} se deve essere l'unico popup presente
	 */
	addPopup: function(x,y,htmlText,isUnique,editable) {		
		this.viewer.pluginAddPopup(x,y,htmlText,isUnique,editable);
	},	

	/**
	 * Method: updateImplicitCustomQuery
	 *  
	 */
	updateImplicitCustomQuery: function (){
	
		this.implicitCustomQuery = new Object();
		
		// Aggiunta SelectCustomQuery
		for(var index=0; index<this.paramsJS.azioniEventi.eventiLayerList.length; index++) {
			var paramJSLayer = this.paramsJS.azioniEventi.eventiLayerList[index];
		
			if (((paramJSLayer.customQueryOnSelect) && (paramJSLayer.customQueryOnSelect!='')) || 
				((paramJSLayer.customQueryOnNOSelect) && (paramJSLayer.customQueryOnNOSelect!=''))){
				var sel = this.selezioneCorrente.getByCodTPN(paramJSLayer.codTPN)
				var codTPN = paramJSLayer.codTPN;
				var buffCodTPN = codTPN + '';
				buffCodTPN = buffCodTPN.replace("-","M");
				var nome = "CQSELCOD" + buffCodTPN;	
				// ReplaceAll %SELCOD%
				 
				var valore = null;			
				
				if (sel) {
					// ci sono oggetti selezionati per questo layer
					valore = paramJSLayer.customQueryOnSelect;	
					var RE = new RegExp("%SELCOD%", "ig");
					valore = valore.replace(RE, codTPN);
					
					// ReplaceAll 
					var RE = new RegExp("%SELID%", "ig");
					valore = valore.replace(RE, sel.geometries[0].key);
					
				} else {
					// Non ci sono oggetti selezionati per questo layer
					valore = paramJSLayer.customQueryOnNOSelect;
				}
				
				eval ("this.implicitCustomQuery."+nome+"=valore");
			}            	
		}
		
		this.implicitCustomQuery.CQTEMPORALFILTERDTINIZIO = this.temporalFilterDtInizio; 
		this.implicitCustomQuery.CQTEMPORALFILTERDTFINE = this.temporalFilterDtFine;
		// WMS-TIME
		var dInizioBuff = Ext.Date.parse(this.temporalFilterDtInizio,"d/m/Y");
		var dFineBuff = Ext.Date.parse(this.temporalFilterDtFine,"d/m/Y");
		var format = "Y-m-d\\TH:i:s\\Z";
		this.implicitCustomQuery.TIME = Ext.Date.format(dInizioBuff, format) + "/" + Ext.Date.format(dFineBuff, format) ; 
	},
	
	/**
	 * Method: applyCustomQuery
	 * Consente di utilizzare una query personale.
	 * 
	 */
	applyCustomQuery: function (){
	
		var params = this.getCustomQueryParams();
	
		if ( !this.paramsPrev || (Ext.JSON.encode(params)!= Ext.JSON.encode(this.paramsPrev))) {
			this.viewer.pluginUpdateCustomQuery(params);
			if (this.TOCPanel!=null){
				this.TOCPanel.updateCustomQuery(params);
			}
		}
		
		this.paramsPrev=params;
		
	},
		
	/**
	 * Method: getCustomQueryParams
	 * 
	 */
	getCustomQueryParams: function () {
	
		var params = new Array();
	
		this.updateImplicitCustomQuery();
		
		for (var i = 0; i<this.paramsJS.mappe.mappaList.length; i++) {
			
			var mappa = this.paramsJS.mappe.mappaList[i];
		
			var customQueryParams = new Object();
		    for (var j=0;j<mappa.customQueryList.length ;j++) {
		    	eval ("customQueryParams."+mappa.customQueryList[j].nome+"='"+mappa.customQueryList[j].query+"';");
		   	}
		   	
		    customQueryParams = Ext.apply(customQueryParams,this.implicitCustomQuery );
		    customQueryParams = Ext.apply(customQueryParams,this.explicitCustomQueryParams );
		   	params.push(customQueryParams);        
		
		}
		
		return params;
		
	},
	
	
	
	addExplicitCustomQueryParam: function(customQueryobj) {
		Ext.apply(this.explicitCustomQueryParams, customQueryobj );
		this.applyCustomQuery();
	},
	
	removeExplicitCustomQueryParam: function(name) {
		this.explicitCustomQueryParams[name] = undefined;
		this.applyCustomQuery();
	}, 
	
	getExplicitCustomQueryParam: function(name) {
		return this.explicitCustomQueryParams[name];
	},

	/**
	 * Method: gotoPosition 
	 *
	 * Parameters:
	 * coordX - {Integer} coordinata X.
	 * coordY -{Integer} coordinata Y.
	 * zoomFactor - {} livello di zoom.
	 * withMarker - {} withMarker.
	 * crsCode - {String} codice EPSG del sitema di riferimento in cui sono le coordinate passate.
	 */
	gotoPosition: function (coordX, coordY, zoomFactor, withMarker, crsCode) {
		
		zoomFactor = zoomFactor || this.viewer.pluginGetCurrentZoom();
		var currPoint = new Point(coordX,coordY);
		
		// Se è stato passato un sistema di riferimento fra qelli gestiti e diverso da quello attuale
		// riproietto le coordinate
		if(crsCode && this.projectionCrs[crsCode]){
			var currSrsCode = this.getProjectionCode();
			if(currSrsCode != crsCode){
				var currProj = new TolomeoExt.Projection(currSrsCode);
				var sourceProj = new TolomeoExt.Projection(crsCode);		
				currPoint = TolomeoExt.Projection.transform(currPoint,sourceProj,currProj);
			}		
		}				
		this.viewer.pluginGotoPosition(currPoint.x, currPoint.y, zoomFactor, withMarker, this.TOLOMEOServer + this.TOLOMEOStaticRoot + '/img/markers/arrow_Marker.png'); 
	},
	
	/**
	 * Method: doOpenActionsJS
	 * 
	 */
	doOpenActionsJS: function() {
		if (this.openActionsJS!=null) {
			this.openActionsJS();
		} 
		
		var bOnOpenDrawMap = false;
		var bUnComando = false;
		var buff;
		
		var queryString = window.location.toString();
		var pos = queryString.indexOf('?')
		if (pos!=-1) {
			queryString = queryString.substr(pos);
			var qsObj = Ext.Object.fromQueryString(queryString);
			if (qsObj[this._getPermalinkParameterName("cmdUrl")] || qsObj[this._getPermalinkParameterName("cmdUrlComp")]) {
				bUnComando = true;
				pro = Ext.create('TolomeoExt.ToloProcedure', {
			    			cmdUrl: qsObj[this._getPermalinkParameterName("cmdUrl")],
			    			cmdUrlComp: qsObj[this._getPermalinkParameterName("cmdUrlComp")]
			    		});
			    		
			    buff = 	pro.run(this);	
			    bOnOpenDrawMap = bOnOpenDrawMap || buff;
			} 
			
			// Popups.
			// Fare prima di posizionamento perchè openalyers sembra spostare la mappa quando si aggiugono popup 
			var popupPrm = qsObj[this._getPermalinkParameterName("popup")];
			if (popupPrm) {
				bUnComando = true;
				var conf = [];
				if (popupPrm instanceof Array) {
					for (var i=0; i<popupPrm.length ; i++) {
						var arg = popupPrm[i].split("|"); 
						conf.push({x: arg[0], y: arg[1], t: arg[2], e: arg[3]});
					}
				} else {
					var arg = popupPrm.split("|"); 
					conf.push({x: arg[0], y: arg[1], t: arg[2], e: arg[3]});
				}
				
				var popUpCommand = Ext.create('TolomeoExt.ToloCommand.addPopups', { conf: conf });
				buff = popUpCommand.run(this);
				bOnOpenDrawMap = bOnOpenDrawMap || buff; 
			}
			
			// Posizonamento per extent
			var ztePrmLeft   = qsObj[this._getPermalinkParameterName("left")];
			var ztePrmBottom = qsObj[this._getPermalinkParameterName("bottom")];
			var ztePrmRight  = qsObj[this._getPermalinkParameterName("right")];
			var ztePrmTop    = qsObj[this._getPermalinkParameterName("top")];
				
			if (ztePrmLeft && ztePrmBottom && ztePrmRight && ztePrmTop) {
				bUnComando = true;
				var zoomToCommand = Ext.create('TolomeoExt.ToloCommand.zoomToExtent', { left: ztePrmLeft, bottom: ztePrmBottom, right: ztePrmRight, top: ztePrmTop });
				buff = zoomToCommand.run(this);
				bOnOpenDrawMap = bOnOpenDrawMap || buff;
			}
			
			// Positionamento per centro e scala
			var ztsPrmX     = qsObj[this._getPermalinkParameterName("x")];
			var ztsPrmY     = qsObj[this._getPermalinkParameterName("y")];
			var ztsPrmScale = qsObj[this._getPermalinkParameterName("scale")];			
			
			if (ztsPrmX && ztsPrmY && ztsPrmScale) {
				bUnComando = true;
				var zoomToCommand = Ext.create('TolomeoExt.ToloCommand.zoomTo', { x: ztsPrmX, y: ztsPrmY, s: ztsPrmScale });
				buff = zoomToCommand.run(this);
				bOnOpenDrawMap = bOnOpenDrawMap || buff;
			}
			
			// Simulazione identify
			var iPrmX    = qsObj[this._getPermalinkParameterName("ix")];
			var iPrmY    = qsObj[this._getPermalinkParameterName("iy")];
			var iPrmSrid = qsObj[this._getPermalinkParameterName("isrid")];
			var iPrmLn   = qsObj[this._getPermalinkParameterName("iln")];
			
			if (iPrmX && iPrmY && iPrmSrid) {
				bUnComando = true;
				var identifyCommand = Ext.create('TolomeoExt.ToloCommand.identify', { x: Number(iPrmX.replace(",",".")), y: Number(iPrmY.replace(",",".")), srid: iPrmSrid, layerName: iPrmLn });
				buff = identifyCommand.run(this);
				bOnOpenDrawMap = bOnOpenDrawMap || buff;
			}
			
			// Note all'avvio
			var startPopup = qsObj[this._getPermalinkParameterName("startPopup")];
			if (startPopup) {
				var spp = Ext.create('TolomeoExt.ToloCommand.startPopup', 
						{ msg: startPopup});
				buff = spp.run(this);
				bOnOpenDrawMap = bOnOpenDrawMap || buff;
			}
			
			this.viewer.bOnOpenDrawMap = this.viewer.bOnOpenDrawMap || bOnOpenDrawMap || !bUnComando;			
		}
		
	},	

	/**
	 * Method: doOpenActions
	 * Esegue le azioni di apertura definite nel file .xml, come il posizionamento etc
	 */
	doOpenActions: function (){

		var action = this.paramsJS.azioniApertura.action;
	   	
	   	if (action == "ZoomTo") {      
	   		this.viewer.pluginGotoPosition (this.paramsJS.azioniApertura.coordX, this.paramsJS.azioniApertura.coordY, this.paramsJS.azioniApertura.zoom, this.paramsJS.azioniApertura.withMarker);      
		} else if (action == "ZoomToOgg")  {
			if(this.paramsJS.azioniApertura.zoomToCodTPN && this.paramsJS.azioniApertura.zoomToIdTPN){				
				this.zoomToObj(this.paramsJS.azioniApertura.zoomToCodTPN, this.paramsJS.azioniApertura.zoomToIdTPN, true);
			}			
				
			/* CODICE CHE NON SEMBRA PIU FUNZIONARE, SE VERIFICATO CHE NON UTILIZZATO, TOGLIERE
			if ((this.paramsJS.azioniApertura.zoomToJSGeometry!=null) & (this.paramsJS.azioniApertura.zoomToJSGeometry!="")) {
				var geoms = new JSGeometryArray();
				
				geoms.FromUntypedArray(this.paramsJS.azioniApertura.zoomToJSGeometry);
				if ((geoms.geometries.length==1) && (geoms.geometries[0].geometry!="")) {
				
					if (this.paramsJS.azioniApertura.modoEditingSingolo != null && 
						this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!=null &&
						this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!="") {
							// Se c'è modoEditingSingolo allora uso highlight perchè selected viene usato da editing singolo
							addHighlighted(geoms);
							if (geoms.geometries[0].isPoint()) {
								this.zoomToHighlighted(this.paramsJS.azioniApertura.zoom);
							} else {
								this.zoomToHighlighted();
							}	
					} else {
						
						// Se non c'è modoEditingSingolo allora uso selected, almeno le azioni sono subito disponibili
						this.addSelected(geoms);
						if (geoms.geometries[0].isPoint()) {
							this.viewer.pluginZoomToSelected(this.paramsJS.azioniApertura.zoom);
						} else {
							this.viewer.pluginZoomToSelected();
						}
					}
					
				}								
			}
			*/
		}
		
		if (this.paramsJS.azioniApertura.urlPannello) {
			var method = (this.paramsJS.azioniApertura.method || 'POST').toUpperCase();
			var m = this;
			setTimeout(function(){m.openURL(m.paramsJS.azioniApertura.urlPannello, "pannello", method)},500);
			/*			
			var method = (this.paramsJS.azioniApertura.method || 'POST').toUpperCase();
			this.openURL(this.paramsJS.azioniApertura.urlPannello, "pannello", method);
			*/
		}
		
		// Modo editing singolo. 
		// Se già presente seleziona e zoomToSelected all'oggetto da modificare,
		// altrimenti nulla 
		if (this.paramsJS.azioniApertura.modoEditingSingolo != null && 
			this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!=null &&
			this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!="") {
			//if (this.paramsJS.azioniApertura.modoEditingSingolo.conInsert==false) {
				//params.getAzioniApertura().getModoEditingSingolo().setEditingJSGeometry
				var geoms = new JSGeometryArray();
			
				geoms.FromUntypedArray(this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry);
				if ((geoms.geometries.length==1) && (geoms.geometries[0].geometry!="")) {
					this.addSelected(geoms);
					if (geoms.geometries[0].isPoint()) {
						this.zoomToSelected(this.paramsJS.azioniApertura.zoom);
					} else {
						this.zoomToSelected();
					}
				}
				
			//}
		}
	},
	
	cleanJSONQuotesOnKeys: function (json) {
    	return json.replace(/"(\w+)"\s*:/g, '$1:');
	},
	
	generatePermalink: function(startPopupText) {
		
		var proc = Ext.create('TolomeoExt.ToloProcedure', {});
		
		if(this.TOCPanel){
			var pl = this.TOCPanel.saveToCommand();
			proc.addCommand(pl);
		}

		// Popups
		// Fare prima di posizionamento
		var conf = [];
		var popups = this.viewer.pluginGetOpenedPopups();
		for (var i=0; i < popups.length; i++) {
			var p = popups[i];
			conf.push({x: p.lonlat.lon , y: p.lonlat.lat, t: p.contentHTML, e: p.editable});
		}
		var zt = Ext.create('TolomeoExt.ToloCommand.addPopups', 
							{ conf:conf	});
		proc.addCommand(zt);
		
		if (startPopupText) {
			var spp = Ext.create('TolomeoExt.ToloCommand.startPopup', 
					{ msg: startPopupText});
			proc.addCommand(spp);	
		}
		
		/* La posizione viene aggiunta in chiaro su richiesta di RT
		var zt = Ext.create('TolomeoExt.ToloCommand.zoomTo', {
								x:this.viewer.pluginGetCurrentX(),
								y:this.viewer.pluginGetCurrentY(),
								s: this.viewer.pluginGetCurrentZoom()});
		proc.addCommand(zt);
		*/

		var cmdParamNoQuotes=Ext.JSON.encode(proc); //this.cleanJSONQuotesOnKeys(Ext.JSON.encode(proc));
		//var cmdParamNoQuotesComp = LZString.compressToBase64(cmdParamNoQuotes); 
		
		//var msg =  "Lunghezza originale: " + encodeURIComponent(cmdParamNoQuotes).length;
		//msg += "<br/>" + "Lunghezza compressa:" + cmdParamNoQuotesComp.length;
		//msg += "<br/>";
		
		var pos = window.location.href.indexOf("?");
		var qs = {};
		if (pos!=-1) {
			qs = Ext.Object.fromQueryString(window.location.href.substring(pos));
		}
		qs[this._getPermalinkParameterName("cmdUrlComp")] = LZString.compressToBase64(cmdParamNoQuotes);
		
		qs[this._getPermalinkParameterName("x")] = this.viewer.pluginGetCurrentX();
		qs[this._getPermalinkParameterName("y")] = this.viewer.pluginGetCurrentY();
		qs[this._getPermalinkParameterName("scale")] = this.viewer.pluginGetCurrentZoom();

		//var proc1 = Ext.create('TolomeoExt.ToloProcedure', {});
		//proc1.addCommand(zt);
		//qs.cmdUrl=this.cleanJSONQuotesOnKeys(Ext.JSON.encode(proc1));
		
		var qs1 = window.location.protocol + "//" + window.location.host + window.location.pathname;
		
		qs1=Ext.String.urlAppend(qs1, Ext.Object.toQueryString(qs));
		
		return qs1;
		
	},
	
	
	showPermalink: function() {
		
		var permalink = this.generatePermalink();
		
		//window.location.href + ((window.location.href.indexOf("?")==-1) ? "?" : "&")  + "cmdUrlComp=" + LZString.compressToBase64(cmdParamNoQuotes) + '
		//var msg = '<a target="_blank" href="' + permalink +'">Permalink</a>';

		//Ext.MessageBox.show({title: "Permalink",  msg: msg });
		var thisid = this.apiid;
		var me = this;
		
		Ext.create('TolomeoExt.Window', {
		    title: ToloI18n.getMsg("ToloMapAPIExt.showPermalink.title"),
		    iconCls: 'iconPermalink',
		    modal: true,
		    height: 200,
		    width: 400,
		    layout: 'fit',
		    //cls: 'clearCSS',
		    items: { 
		        xtype     : 'textareafield',
		        grow      : false,
		        name      : 'permalink',
		        fieldLabel: '',
		        id : thisid + '-permalinkTextArea',
		        anchor    : '100%',
		        value : permalink,
		        selectOnFocus: true
		    },
		    dockedItems: [{
		        xtype: 'toolbar',
		        dock: 'bottom',
		        items: ['->','-',{
		        	iconCls: 'iconOpenInNewWin',
		            text: ToloI18n.getMsg("ToloMapAPIExt.showPermalink.nuovafin"),
		            listeners : {
			        	click : {
			        		fn :  function(){
			        					var permalinkTextAreaCmp = Ext.getCmp(thisid + '-permalinkTextArea');
					        			window.open(permalinkTextAreaCmp.getValue(),"_blank");
			        		}
			        	}
			        }
		        },{
		        	iconCls: 'iconOpenInNewWin',
		            text: ToloI18n.getMsg("ToloMapAPIExt.showPermalink.nota"),
		            listeners : {
			        	click : {
			        		fn :  function(){
			        			
			        			var permalinkTextAreaCmp = Ext.getCmp(thisid + '-permalinkTextArea');
			        			
			        			Ext.create('Ext.Window', {
			        				cls: 'clearCSS',
			        				modal:true,
			        				layout: 'fit',
			        				items:[{
			        			    	xtype: 'htmleditor',
			        			    	id: thisid + '-htmleditor',
			        			    	cls: 'clearCSS',
			        			    	enableAlignments: false,
			        			    	enableFont: false,
			        			    	enableLinks: true,
			        			    	enableSourceEdit: false,
			        			    	value: !permalinkTextAreaCmp.startPopup ? '' : permalinkTextAreaCmp.startPopup,
			        			    	listeners: {
			        			    		change: function(htlmcontrol, newval, oldval, eopts) {
			        		    				Ext.getCmp(thisid + '-btnOkInsNotaApertura').setDisabled(newval=="");
			        			    		}
			        			    	}
			        				}],
			        				buttons: [
			        			          { text: ToloI18n.getMsg("ToloMapAPIExt.showPermalink.btnOK"),
			        			        	disabled: true,
			        			        	id: thisid + '-btnOkInsNotaApertura',
			        			        	listeners: {
			        			        		click: function() {
			        			        			// setto nuovo valore
			        			        			var htmlEditorCmp = Ext.getCmp(thisid + '-htmleditor');
			        			        			var text = htmlEditorCmp.getValue();
			        			        			var pml = me.generatePermalink(text);
			        			        			
			        			        			// setto nuovo valore
			        			        			permalinkTextAreaCmp.setValue(pml);
			        			        			permalinkTextAreaCmp.startPopup = text;
			        			        			Ext.defer(function(){
			        			        				permalinkTextAreaCmp.focus();
			        			        			},200);
			        			        			this.up('.window').close();
			        				        	}
			        				         }
			        			          }
			        			    ],
			        			    listeners : {
			        			    	show : {
			        		        		fn :  function(){
				        		        			Ext.defer(function(){
				        		        				this.items.getByKey(thisid + '-htmleditor').focus();
			        			        			},200, this);
			        		        			
			        		        		}
			        		        	}
			        		        }
			        			}).show();
			        			
			        			/////////////////////////
			        			//window.open(permalink,"_blank");
			        		}
			        	}
			        }
		        }]
		    }],
		    listeners : {
	        	show : {
	        		fn :  function(){		        			
	        			this.items.getByKey(thisid + '-permalinkTextArea').focus();
	        		}
	        	}
	        }
		}).show();
						
	},
	
	exportForQgis : function(catIdx, layIdx, version){
		
		if(!this.TOCPanel) return;				
		
		this.exportForm.getForm().setValues({
			tocInfo: this.TOCPanel.tocInfo.JSONEncodeInfo(catIdx, layIdx),
			paramsJS: Ext.JSON.encode(this.paramsJS),
			nMappa: '0',
			idxCategoriaBase: catIdx,
			idxLayerBase: layIdx,
			version: version
		});
		
		this.exportForm.submit({
			url: this.TOLOMEOServer + this.TOLOMEOContext + '/ExportToQgisServlet',
			method: 'POST',
			target: '_blank'
		});
	},
	
	/**
	 * Method: onDigitizeEndGeometry
	 * Funzione chiamata alla fine del processo di digitalizzazione. 
	 * Viene invocata dalle funzioni piu' specifiche onDigitizeEndPolygon, onDigitizeEndPoint etc.
	 *
	 * Parameters:
	 * geometry - {JSGeometry} la geometria.
	 */
	onDigitizeEndGeometry: function (geometry){

		var	layer = this.getCurrentSelectLayer();
		
		// Se editing singolo ed è definita una geometria editing singolo 
		// (come nel caso di geometrie esistenti da modificare o delle quali inserire la geometria)
		// allora utilizzo la geometria esistente e modifico il solo campo geometria
		if ((this.paramsJS.azioniApertura.modoEditingSingolo!=null) && (this.paramsJS.azioniApertura.modoEditingSingolo!="") &&
			(this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!= null) && (this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!="")) {
			var geoms = new JSGeometryArray();
			geoms.FromUntypedArray(this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry);
			var geom1 = geoms.geometries[0];
			geom1.geometry = geometry.geometry;
			geometry = geom1;
			geometry.SRID = this.paramsJS.mappe.SRID;
			if (this.currentDigitizeOperation==this.digitizeOperationInsert) {
				this.geoOpToPostVar(this.operationGeometryModify);	
			} else {
				this.geoOpToPostVar(this.currentDigitizeOperation);
			}
		} else {
			geometry.codTPN = layer.codTPN;
			geometry.SRID = this.paramsJS.mappe.SRID;
			this.geoOpToPostVar(this.currentDigitizeOperation);
		}
		this.geometryToPostVar (geometry);
		switch (this.currentDigitizeOperation) {
	   		case this.digitizeOperationInsert:
	   			this.onDigitizeEndInsert(geometry);
	   			if ((this.paramsJS.azioniApertura.modoEditingSingolo!=null) && (this.paramsJS.azioniApertura.modoEditingSingolo!="") &&
					(this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!= null) && (this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!="")) {
	   				this.addSelectedByID(this.paramsJS.azioniApertura.modoEditingSingolo.layerCODTPN, this.paramsJS.azioniApertura.modoEditingSingolo.valoreChiave);
				} 
	   			break;
	   		case this.digitizeOperationSubtract:
	   			this.onDigitizeEndSubtract(geometry);
	   			//refreshSelected();
	   			break;
	   		case this.digitizeOperationAdd:
	   			this.onDigitizeEndAdd(geometry);
	   			//refreshSelected();
	   			break;
	   		case this.digitizeOperationAddSub:
	   			this.onDigitizeEndAddSub(geometry);
	   			//refreshSelected();
	   			break;
	   		case this.digitizeOperationVertexEdit:
	   			this.digitizeEndVertexEdit(geometry);
	   			//refreshSelected();
	   			break;
	   		case this.digitizeOperationDragDrop:
	   			this.digitizeEndDragDrop(geometry);
	   			//refreshSelected();
	   			break;
	   		default: alert (ToloI18n.getMsg("ToloButtonPanelExt.mnuGuida.text", {currentDigitizeOperation: currentDigitizeOperation}));

	   	}

		this.currentDigitizeOperation = '';
		
		var me = this;
		
		// ritardata perche' sennò avviene il cambio quando l'evento click non e' ancora stato consumato
		// e lo strumento di selezione esegue una selezione non voluta
		setTimeout(function(){
			me.fireEvent('onOperationPressDefault', 2);				                 
        }, 500);
	    
		this.fireEvent("digitizeEnd", geometry);
	},	

	/**
	 * Method: onDigitizeEndInsert
	 * Funzione chiamata alla fine del processo di digitalizzazione nel caso che l'operazione sia un inserimento. Viene invocata dalla funzione più generale onDigitizeEndGeometry etc.
	 * Si occupa di chiedere conferma ed eventualmente eseguire le azioni richieste invocando la doEventActions
	 *
	 * Parameters:
	 * geometry - {JSGeometry} la geometria.
	 */
	onDigitizeEndInsert: function (geometry){

		
		var layer = this.getCurrentSelectLayer();	
		var messint = ToloI18n.getMsg("ToloMapAPIExt.onDigitizeEndInsert.msg", {descrizioneLayer:layer.descrizioneLayer});
		
		if (geometry.geometryIsValid){
			if (confirm(messint)) {
				this.validateDigitizeOperation(layer, function() {
					if ((this.paramsJS.azioniApertura.modoEditingSingolo != null ) && (this.paramsJS.azioniApertura.modoEditingSingolo != "" )) {
						var edGeom = this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry;
						if ((edGeom!=null) && (edGeom!="")) {
							// Se l'oggetto esiste vuol dire che l'inserimento si riferiva alla sola geometria (quindi e' un update)
							this.doEventActions(layer, this.eventUpdateGeom, this.paramsJS.azioniApertura.modoEditingSingolo.valoreChiave);
							this.digitizeLayerConditionalClear(layer, this.eventUpdateGeom);
						} else {
							// Se l'oggetto non esiste vuol dire che deve essere vero inserimento
							this.doEventActions(layer, this.eventIns, this.paramsJS.azioniApertura.modoEditingSingolo.valoreChiave);
							this.digitizeLayerConditionalClear(layer, this.eventIns);
						}
					} else {
						this.doEventActions(layer, this.eventIns, null);
						this.digitizeLayerConditionalClear(layer, this.eventIns);
					}
				},
				function (errMsg,isAjaxError) {
					if(isAjaxError){
						this.showAjaxError(errMsg);
					}else{
						alert (errMsg);					
					} 					
					this.digitizeLayerClear();	 		 
				})	 
			} else {
				this.digitizeLayerClear();	
			}
		} else {
			alert(ToloI18n.getMsg("ToloMapAPIExt.onDigitizeEndInsert.msgInvalidGeom"));
			this.digitizeLayerClear();
		}
		
		if ((this.paramsJS.azioniApertura.modoEditingSingolo != null ) && (this.paramsJS.azioniApertura.modoEditingSingolo != "" )) {
			this.bEditingSingoloInsertDone=true;
		}
		
		this.fireEvent("digitizeEndInsert", geometry);
	},

	/**
	 * Cancella tutto il contenuto del layer di digitalizzazione se è previsto da configurazione (file preset) questo comportamento a fine digitalizzazione.
	 * Nel caso che non sia prevista la cancellazione attiva l'editing se previsto nel file di preset 
	 * 
	 * @param {} eventoLayer
	 * @param {} tipoEvento
	 */
	digitizeLayerConditionalClear: function(eventoLayer, tipoEvento) {
		
		var azioniEventi = this.getAzioniEventi(eventoLayer, tipoEvento);
		if (azioniEventi.digitizedFeatureClear) {
			this.digitizeLayerClear();
		} else {
			this.viewer.pluginStartDigitizedFeatureModify(azioniEventi.digitizedFeatureModifyMode);
			this.viewer.on('onDigitizedFeatureDragDropEnd', this.digitizedFeatureModifyEnd,this);
			this.viewer.on('onDigitizedFeatureVertexEditingEnd', this.digitizedFeatureModifyEnd,this);
			
		}
			
		
	},
	
	/**
	 * Cancella tutto il contenuto del layer di digitalizzazione e disabilita l'editing eventualmente attivato
	 */
	digitizeLayerClear: function() {
		this.viewer.pluginDigitizeLayerClear();
		this.viewer.pluginStopDigitizedFeatureModify();
		this.viewer.un('onDigitizedFeatureDragDropEnd',this.digitizedFeatureModifyEnd, this);
		this.viewer.un('onDigitizedFeatureVertexEditingEnd', this.digitizedFeatureModifyEnd, this);
	},
	
	/**
	 * Method: onDigitizeEndAdd
	 * Funzione chiamata alla fine del processo di digitalizzazione nel caso che l'operazione sia una aggiunta di poligono. Viene invocata dalla funzione più generale onDigitizeEndGeometry etc.
	 * Si occupa di chiedere conferma ed eventualmente eseguire le azioni richieste invocando la doEventActions
	 *
	 * Parameters:
	 * geometry - {JSGeometry} la geometria.
	 */
	onDigitizeEndAdd: function (geometry){

	 	var layer = this.getCurrentSelectLayer();
	 	//ALE1
	 	// Assumo che esista e che sia uno solo perchè altrimenti non dovrebbe essere abilitato il pulsante che 
	 	// ha dato origine a questa azione
	 	var selCorrente = this.selezioneCorrente.getByCodTPN(layer.codTPN).geometries[0];
	 	var chiaveSelCorrente =  selCorrente.key;
		var messint = ToloI18n.getMsg("ToloMapAPIExt.onDigitizeEndAdd.msg", {descrizioneLayer: layer.descrizioneLayer});
		if (confirm(messint)) {
			this.validateDigitizeOperation(layer, function() {
											this.doEventActions(layer, this.eventUpdateGeom,  chiaveSelCorrente, undefined, undefined, undefined, selCorrente);
											this.digitizeLayerConditionalClear(layer, this.eventUpdateGeom);
												  }, 
											function (errMsg,isAjaxError) {
												if(isAjaxError){
													this.showAjaxError(errMsg);
												}else{
													alert (errMsg);					
												} 					
												this.digitizeLayerClear();			 
											}	 
									) 	
		} else { 
			this.digitizeLayerClear();		
		}
		this.fireEvent("digitizeEndAdd", geometry);
	},

	/**
	 * Method: validateDigitizeOperation
	 * Valida la digitazione.
	 * 
	 * Parameters:
	 * layer - {} il layer.
	 * funcSuccess - {Function} funcSuccess
	 * funcError - {Function} funcError
	 */
	validateDigitizeOperation: function (layer, funcSuccess, funcError) {
			
		if (layer.validazioneGeometria) {
			//Chiamata ajax
			var ajaxOptions = { method: 'post',
								url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxGeometryValidationServlet',
								scope: this,
								params: {geoOp: this.geoOpField.getValue(), 
											 geoCoord: this.geoCoordField.getValue(),
											 codTPN:   layer.codTPN,
											 format: 'ext',
											 urlAdditionalParams: this.paramsJS.urlAdditionalParams,
											 paramPreset: this.paramsJS.nomePreset
											 },
								success: function(results, store) {
												 
												//var resp = eval('(' + transport.responseText + ')');
												//var errori = resp.errori;
												 
												if (results.length==0) {
													funcSuccess.call(this);
												} else {
													//Errore di validazione
													var errMsg = results[0].data.errorMessage;
													for (i=1;  i<results.length; i++) {
														errMsg += '\n' + results[i].data.errorMessage;
													}
													if(funcError){ 
														// Passa il messaggio di errore e dice che l'errore NON è Ajax
														funcError.call(this,errMsg,false); 
													}else{
														alert (errMsg);
													}												
												}				
										  },
								failure: function (store) { 
											//validateDigitizeOperation.showAjaxError(transport);
											if(funcError){ 
												// Passa il messaggio di errore e dice che l'errore è Ajax
												funcError.call(this,ToloI18n.getMsg("ToloMapAPIExt.validateDigitizeOperation.httperr"),true);
											}else{
												this.showAjaxError(transport);
											} 
										 } 
							  }
	
			// Aggiunta parametri WMS da url
			Ext.apply(ajaxOptions.params, this.paramsJS.urlAdditionalParams);
			
			new TolomeoExt.ToloCrossAjax().request(ajaxOptions);
			
		} else {
			funcSuccess.call(this);
		}
	},
	

	/**
	 * Method: digitizeEndVertexEdit
	 * Funzione chiamata alla fine del processo di digitalizzazione nel caso che l'operazione sia una modifica dei vertici. Viene invocata dalla funzione più generale onDigitizeEndGeometry etc.
	 * Si occupa di chiedere conferma ed eventualmente eseguire le azioni richieste invocando la doEventActions
	 *
	 * Parameters:
	 * geometry - {JSGeometry} la geometria.
	 */
	digitizeEndVertexEdit: function (geometry){

	 	var layer = this.getCurrentSelectLayer();
	 	 // Assumo che esista e che sia uno solo perchè altrimenti non dovrebbe essere abilitato il pulsante che 
	 	// ha dato origine a questa azione
	 	var selCorrente = this.selezioneCorrente.getByCodTPN(layer.codTPN).geometries[0];
	 	var chiaveSelezione =  selCorrente.key;
		//var chiaveSelezione = selezioneCorrente.key;
		var messint = ToloI18n.getMsg("ToloMapAPIExt.digitizeEndVertexEdit.msg", {descrizioneLayer: layer.descrizioneLayer});
		if (confirm(messint)) {
			this.validateDigitizeOperation(	layer, 
									  	function() {
											this.doEventActions(layer, this.eventUpdateGeom,  chiaveSelezione, undefined, undefined, undefined, selCorrente);},
										function (errMsg,isAjaxError) { 
											if(isAjaxError){
												this.showAjaxError(errMsg);
											}else{
												alert (errMsg);		
												this.refreshSelected();			
											} 					 
										}) 
			
			}else{
				this.refreshSelected();
			}
		this.fireEvent("digitizeEndVertexEditing",  geometry);
		
	},

	/**
	 * Method: digitizeEndDragDrop
	 * Funzione chiamata alla fine del processo di digitalizzazione nel caso che l'operazione sia un drag drop. Viene invocata dalla funzione più generale onDigitizeEndGeometry etc.
	 * Si occupa di chiedere conferma ed eventualmente eseguire le azioni richieste invocando la doEventActions
	 *
	 * Parameters:
	 * geometry - {JSGeometry} geometry
	 */
	digitizeEndDragDrop: function (geometry){

	 	var layer = this.getCurrentSelectLayer();
	 	//var chiaveSelCorrente =  selezioneCorrente.key;
	 	// Assumo che esista e che sia uno solo perchè altrimenti non dovrebbe essere abilitato il pulsante che 
	 	// ha dato origine a questa azione
	 	var selCorrente = this.selezioneCorrente.getByCodTPN(layer.codTPN).geometries[0]
	 	var chiaveSelCorrente =  selCorrente.key;
		
		var messint = ToloI18n.getMsg("ToloMapAPIExt.digitizeEndDragDrop.msg", {descrizioneLayer: layer.descrizioneLayer});
		if (confirm(messint)) {
			this.validateDigitizeOperation(layer, 
										function() {
												this.doEventActions(layer, this.eventUpdateGeom,  chiaveSelCorrente, undefined, undefined, undefined, selCorrente);  },
										function (errMsg,isAjaxError) { 
											if(isAjaxError){
												this.showAjaxError(errMsg);
											}else{
												alert (errMsg);		
												this.refreshSelected();			
											} 					 
										}) 
			
		}else{
			this.refreshSelected();
		}
		this.fireEvent('digitizeEndDragDrop', geometry);
		
	},

	/**
	 * Method: onDigitizeEndSubtract
	 * Funzione chiamata alla fine del processo di digitalizzazione nel caso che l'operazione sia una sottrazione di poligono. 
	 * Viene invocata dalla funzione più generale onDigitizeEndGeometry etc.
	 * Si occupa di chiedere conferma ed eventualmente eseguire le azioni richieste invocando la doEventActions.
	 * 
	 */
	onDigitizeEndSubtract: function (geometry){

		var layer = this.getCurrentSelectLayer();
		//var chiaveSelCorrente =  selezioneCorrente.key;
		// Assumo che esista e che sia uno solo perchè altrimenti non dovrebbe essere abilitato il pulsante che 
	 	// ha dato origine a questa azione
		var selCorrente = this.selezioneCorrente.getByCodTPN(layer.codTPN).geometries[0];
		var chiaveSelCorrente =  selCorrente.key;
		var messint = ToloI18n.getMsg("ToloMapAPIExt.onDigitizeEndSubtract.msg", {descrizioneLayer: layer.descrizioneLayer});
		if (confirm(messint)) {
			this.validateDigitizeOperation(layer, 
								function() {
									this.doEventActions(layer, this.eventUpdateGeom,  chiaveSelCorrente, undefined, undefined, undefined, selCorrente);  
									this.digitizeLayerConditionalClear(layer, this.eventUpdateGeom);
									},
									function (errMsg,isAjaxError) {
										if(isAjaxError){
											this.showAjaxError(errMsg);
										}else{
											alert (errMsg);					
										} 					
										this.digitizeLayerClear();			 
									}) 
			
			
		} else { 
			this.digitizeLayerClear();		
		}
		this.fireEvent("digitizeEndSubtract", geometry);

	},

	/**
	 * Method: onDigitizeEndAddSub
	 * Funzione chiamata alla fine del processo di digitalizzazione nel caso che l'operazione sia una aggiunta e sottrazione (operazione su coperture) di poligono. 
	 * Viene invocata dalla funzione più generale onDigitizeEndGeometry etc.
	 * Si occupa di chiedere conferma ed eventualmente eseguire le azioni richieste invocando la doEventActions.
	 * 
	 * See Also:
	 * <doEventActions>
	 */
	onDigitizeEndAddSub: function (geometry){

		var layer = this.getCurrentSelectLayer();
		//var chiaveSelCorrente =  selezioneCorrente.key;
		// Assumo che esista e che sia uno solo perchè altrimenti non dovrebbe essere abilitato il pulsante che 
	 	// ha dato origine a questa azione
		var selCorrente = this.selezioneCorrente.getByCodTPN(layer.codTPN).geometries[0];
	 	var chiaveSelCorrente =  selCorrente.key;
	 	var messint = ToloI18n.getMsg("ToloMapAPIExt.onDigitizeEndAddSub.msg", {descrizioneLayer: layer.descrizioneLayer});
		
		if (confirm(messint)) {
			this.validateDigitizeOperation(layer, 
						function() {
				
											        
								this.doEventActions(layer, this.eventUpdateGeom,  chiaveSelCorrente, undefined, undefined, undefined, selCorrente); 
								this.digitizeLayerConditionalClear(layer, this.eventUpdateGeom);
									},
									function (errMsg,isAjaxError) {
										if(isAjaxError){
											this.showAjaxError(errMsg);
										}else{
											alert (errMsg);					
										} 					
										this.digitizeLayerClear();			 
									})
		} else { 
			this.digitizeLayerClear();		
		}
		this.fireEvent("digitizeEndAddSub", geometry);
	},

	/**
	 * Method: onDigitizeStartInsert
	 * Funzione invocata per iniziare la digitalizzazione relativa ad una operazione di inserimento.
	 * 
	 */
	onDigitizeStartInsert:function () {
		this.digitizeStart(this.getCurrentSelectLayer().tipoGeometria);
	},

	/**
	 * Method: onDigitizeStopInsert
	 * Funzione invocata per interrompere (annullare) la digitalizzazione relativa ad una operazione di inserimento.
	 * Non confondere con onDigitizeEndInsert che è la fine regolare della digitalizzazione.
	 * 
	 */
	onDigitizeStopInsert: function () {
		this.digitizeStop(this.getCurrentSelectLayer().tipoGeometria);
	},
	
	/**
	 * Method: onDigitizeByCADStartInsert
	 * Funzione invocata per iniziare la digitalizzazione per mezzo di ACD relativa ad una operazione di inserimento.
	 * 
	 */
	onDigitizeByCADStartInsert:function () {
		this.digitizeByCADStart(this.getCurrentSelectLayer().tipoGeometria);
	},

	/**
	 * Method: onDigitizeByCADStopInsert
	 * Funzione invocata per interrompere (annullare) la digitalizzazione per mezzo di CAD relativa ad una operazione di inserimento.
	 * Non confondere con onDigitizeEndInsert che è la fine regolare della digitalizzazione.
	 * 
	 */
	onDigitizeByCADStopInsert: function () {
		this.digitizeByCADStop(this.getCurrentSelectLayer().tipoGeometria);
	},

	/**
	 * Method: onDigitizeStartSubtract
	 * Funzione invocata per iniziare la digitalizzazione relativa ad una operazione di sottrazione.
	 * 
	 */
	onDigitizeStartSubtract: function () {
		var modLayer = this.getCurrentSelectLayer();
		if (modLayer.tipoGeometria==geomTypePoint) {
			this.digitizeStart(geomTypePolygon);
		} else {
			this.digitizeStart(modLayer.tipoGeometria);
		}
	},

	/**
	 * Method: onDigitizeStopSubtract
	 * Funzione invocata per fermare (annullare) la digitalizzazione relativa ad una operazione di sottrazione.
	 * 
	 */
	onDigitizeStopSubtract: function () {
		var modLayer = this.getCurrentSelectLayer();
		if (modLayer.tipoGeometria==geomTypePoint) {
			this.digitizeStop(geomTypePolygon);
		} else {
			this.digitizeStop(modLayer.tipoGeometria);
		}

	},

	/**
	 * Method: onDigitizeStartAdd
	 * Funzione invocata per iniziare la digitalizzazione relativa ad una operazione di aggiunta.
	 * 
	 */
	onDigitizeStartAdd: function () {
	    var modLayer = this.getCurrentSelectLayer();
	    this.digitizeStart(modLayer.tipoGeometria);
	},

	/**
	 * Method: onDigitizeStopAdd
	 * Funzione invocata per fermare la digitalizzazione relativa ad una operazione di aggiunta.
	 * 
	 */
	onDigitizeStopAdd: function () {
	    var modLayer = this.getCurrentSelectLayer();
	    this.digitizeStop(modLayer.tipoGeometria);
	},

	/**
	 * Method: onDigitizeStartAddSub
	 * Funzione invocata per iniziare la digitalizzazione relativa ad una operazione di aggiunta/sottrazione (operazione su copertura).
	 * 
	 */
	onDigitizeStartAddSub: function () {
	    var modLayer = this.getCurrentSelectLayer();
	    this.digitizeStart(modLayer.tipoGeometria);
	},

	/**
	 * Method: onDigitizeStopAddSub
	 * Funzione invocata per fermare (annullare) la digitalizzazione relativa ad una operazione di aggiunta/sottrazione (operazione su copertura).
	 * 
	 */
	onDigitizeStopAddSub: function () {
	    var modLayer = this.getCurrentSelectLayer();
	    this.digitizeStop(modLayer.tipoGeometria);
	},

	/**
	 * Method: onDigitizeStartVertexEdit
	 * Funzione invocata per iniziare la modifica dei vertici.
	 */
	onDigitizeStartVertexEdit: function () {
	    var modLayer = this.getCurrentSelectLayer();
	    this.digitizeStartVertexEditing(modLayer.tipoGeometria);

	},
	
	/**
	 * Method: onDigitizeStopVertexEdit
	 * Funzione invocata per fermare (annullare) la modifica dei vertici.
	 * 
	 */
	onDigitizeStopVertexEdit: function () {
	    var modLayer = this.getCurrentSelectLayer();
	    this.digitizeStopVertexEditing(modLayer.tipoGeometria);
	},

	/**
	 * Method: onDigitizeStartDragDrop
	 * Funzione invocata per lo spostamento di un oggetto.
	 * 
	 */
	onDigitizeStartDragDrop: function () {
	    var modLayer = this.getCurrentSelectLayer();
	    this.digitizeStartDragDrop(modLayer.tipoGeometria);
	},

	/**
	 * Method: onDigitizeStopDragDrop
	 * Funzione invocata per fermare (annullare) lo spostamento di un oggetto.
	 * 
	 */
	onDigitizeStopDragDrop:function () {
	    var modLayer = this.getCurrentSelectLayer();
	    this.digitizeStopDragDrop(modLayer.tipoGeometria);
	},

	/**
	 * Method: digitizeStart
	 * Inizia la digitalizzazione (invocata dalle onDigitizeStartAddSub etc.)
	 *
	 * Parameters:
	 * geomType - {} il tipo di geometria definito.
	 */
	digitizeStart: function (geomType){

		switch (geomType) {
	   		case geomTypePoint:
	   			this.viewer.pluginStartDigitizePoint(geomType);
	   		break;
	   		case geomTypeLine:
	   			this.viewer.pluginStartDigitizeLine(geomType);
	   		break;
	   		case geomTypePolygon:
	   			this.viewer.pluginStartDigitizePolygon(geomType);
	   		break;
	   		case geomTypeCircle:
	   			this.viewer.pluginStartDigitizeCircle(geomType);
	   		break;

	   	}
	},		

	/**
	 * Method: digitizeStop
	 * Interrompe (annulla) la digitalizzazione (invocata dalle onDigitizeStartAddSub etc.)
	 *
	 * Parameters:
	 * geomType - {} il tipo di geometria definito.
	 */
	digitizeStop: function (geomType){

		switch (geomType) {
	   		case geomTypePoint:
	   			this.viewer.pluginStopDigitizePoint(geomType);
	   		break;
	   		case geomTypeLine:
	   			this.viewer.pluginStopDigitizeLine(geomType);
	   		break;
	   		case geomTypePolygon:
	   			this.viewer.pluginStopDigitizePolygon(geomType);
	   		break;
	   		case geomTypeCircle:
	   			this.viewer.pluginStopDigitizeCircle(geomType);
	   		break;

	   	}
	},
				
	/**
	 * Method: digitizeByCADStart
	 * Inizia la digitalizzazione (invocata dalle onDigitizeByCADStart etc.)
	 *
	 * Parameters:
	 * geomType - {} il tipo di geometria definito.
	 */
	digitizeByCADStart: function (geomType){

		switch (geomType) {
	   		case geomTypePoint:
	   			//this.viewer.pluginStartDigitizePointFromRef(geomType);
	   			this.viewer.pluginStartDigitizePointByCAD(geomType);
	   		break;
	   		case geomTypeLine:
	   			this.viewer.pluginStartDigitizeLineByCAD(geomType);
	   		break;
	   		case geomTypePolygon:
	   			this.viewer.pluginStartDigitizePolygonByCAD(geomType);
	   		break;
	   		default:
	   			Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.digitizeByCADStart.title"), ToloI18n.getMsg("ToloMapAPIExt.digitizeByCADStart.msg"));
	   		break;
	   	}
	},
	
	/**
	 * Method: digitizeByCADStop
	 * Interrompe (annulla) la digitalizzazione per mezo di CAD
	 *
	 * Parameters:
	 * geomType - {} il tipo di geometria definito.
	 */
	digitizeByCADStop: function (geomType){

		switch (geomType) {
	   		case geomTypePoint:
	   			//this.viewer.pluginStopDigitizePointFromRef(geomType);
	   			this.viewer.pluginStopDigitizePointByCAD(geomType);
	   		break;
	   		case geomTypeLine:
	   			this.viewer.pluginStopDigitizeLineByCAD(geomType);
	   		break;
	   		case geomTypePolygon:
	   			this.viewer.pluginStopDigitizePolygonByCAD(geomType);
	   		break;
	   		case geomTypeCircle:
	   			Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.digitizeByCADStop.title"), ToloI18n.getMsg("ToloMapAPIExt.digitizeByCADStop.msg"));
	   		break;

	   	}
	},

	/**
	 * Method: digitizeStartVertexEditing
	 * Inizia la modifica dei vertici.
	 *
	 * Parameters:
	 * geomType - {} il tipo di geometria definito.
	 */
	digitizeStartVertexEditing: function (geomType){

		this.viewer.pluginStartVertexEditing(geomType, this.getCurrentSelectLayer().codTPN);

	},

	/**
	 * Method: digitizeStopVertexEditing
	 * Ferma (annulla) la modifica dei vertici.
	 *
	 * Parameters:
	 * geomType - {} il tipo di geometria definito.
	 */
	digitizeStopVertexEditing: function (geomType){

		this.viewer.pluginStopVertexEditing(geomType);

	},

	/**
	 * Method: digitizeStartDragDrop
	 * Inizia lo spostamento di un oggetto.
	 *
	 * Parameters:
	 * geomType - {} il tipo di geometria definito.
	 */
	digitizeStartDragDrop: function (geomType){

		this.viewer.pluginStartDragDrop(geomType);
	},

	/**
	 * Method: digitizeStopDragDrop
	 * Ferma (annulla) lo spostamento di un oggetto.
	 *
	 * Parameters:
	 * geomType - {} il tipo di geometria definito.
	 */
	digitizeStopDragDrop: function (geomType){

		this.viewer.pluginStopDragDrop(geomType);
	},

	/**
	 * Method: digitizeStopVertexEditing
	 * Ferma (annulla) l'editing dei vertici di un oggetto.
	 *
	 * Parameters:
	 * geomType - {} il tipo di geometria definito.
	 */
	digitizeStopVertexEditing: function (geomType){

		this.viewer.pluginStopVertexEditing(geomType);
	},

	/**
	 * Method: getAzioniEventi
	 * Ritorna la sezione AzioniEventi specifica di eventoLayer e del tipoLayer passati come parametro
	 *
	 * Parameters:
	 * eventoLayer - {} eventoLayer
	 * tipoEvento - {} tipoEvento
	 * idBtn - {} idBtn
	 *
	 * Returns:
	 *  {<AzioniEventi>} la sezione AzioniEventi specifica.
	 */
	getAzioniEventi: function (eventoLayer, tipoEvento, idBtn) {
		var azioniEventi = null;
		
		if (eventoLayer) {
			switch (tipoEvento) {
			  case (this.eventVis):
			  		azioniEventi = eventoLayer.azioniEventiVis;
			  		break;
			  case (this.eventCanc):
			  		azioniEventi = eventoLayer.azioniEventiCanc;
			  		break;
			  case (this.eventUpdateGeom):
			  		azioniEventi = eventoLayer.azioniEventiUpdateGeom;
			  		break;
			  case (this.eventUpdateAlpha):
			 		azioniEventi = eventoLayer.azioniEventiUpdateAlpha;
			  		break;
			  case (this.eventIns):
			  		azioniEventi = eventoLayer.azioniEventiIns;
			  		break;
			  case (this.eventInsFromLayer):
			  		azioniEventi = eventoLayer.azioniEventiInsFromLayer;
			  		break;
			  case (this.eventInsFromImport):
			  		azioniEventi = eventoLayer.azioniEventiInsFromImport;
			  		break;
			  case (this.eventRicerca):
			  		azioniEventi = eventoLayer;
			  		break;
		      case (this.eventCustomButton):		      		
			      	var azCbList = eventoLayer.azioniEventiCustomButtonList.customButtonList;
					for (var i=0; i<azCbList.length; i++) {
						if (azCbList[i].idCustomButton == idBtn) {
							azioniEventi = azCbList[i];
							// parametro che mi dice se l'azione è sul layer o globale
							if(azioniEventi)
								azioniEventi.isOfTheLayer = true;						
							break;
						}
					}
					if (!azioniEventi) {
						// se non definito a livello di layer/evento cerca su definizione globale bottoni
						var azCbList = this.paramsJS.layOut.customButtonList;
						for (var i=0; i<azCbList.length; i++) {
							if (azCbList[i].idCustomButton == idBtn) {
								azioniEventi = azCbList[i].azioniEventiCustomButton;
								// parametro che mi dice se l'azione è sul layer o globale
								if(azioniEventi)
									azioniEventi.isOfTheLayer = false;
								break;
							}
						}
					}
					
			  		break;
			}
		}
		
		return azioniEventi;
	},

	// TODO
	// - gestire in doEventActions chiudiSuDblClick e togliere da onIdentify
	// - gestire pluginRefreshMappa quando necessario (adesso lo fa sempre dopo l'ultimo passo)
	// - gestione parametri da uno step all'altro?
	// - gestione errori ajax
	/**
	 * Method: doEventActions
	 * Si occupa di eseguire le azioni previste per l'evento ed il layer specifici
	 * le azioni ajax sono eseguite in maniera sincrona (attesa della fine prima di passare alla successiva) 
	 * mentre le altre sono eseguite in maniera asincrona (senza attesa della fine) fino alla successiva ajax.
	 * N.B. Ricordarsi in caso di aggiunta di parametro di gestire anche all'interno dove vengono settati sulla funzione
	 * di callback e nella funzione di callback stessa e doEventActionsAjaxFailure
	 *
	 * Parameters:
	 * eventoLayer - {} corrispondente JS della classe parametriEventiLayer
	 * tipoEvento - {} tipo di evento che si e' verificato. Valori possibili sono quelli previsti per le costanti che indicano il tipo di evento
	 * keyValue - {} chiave dell'oggetto al quale si riferisce l'azione
	 * nStep - {} numero del passo. Nelle chiamate ajax la funzione termina per essere richiamata 
	 *            dalla funzione di callback ajax passando in questo parametro il passo al quale si è arrivati e consentendo quindi di andare avanti
	 * nextActionObj - {} oggetto contenente i parametri per l'eventuale azione di redirect. Una azione di redirect è caratterizzata dal fatto di avere
	 *   		          il flag redirect=true. Quando questo si verifica viene fatta redirect (tenendo conto di ajaxCall e traget) all'url definita in redirectUrl 
	 * 			          se definito oppure nel parametro nextActionObj.redirectUrl. Il contenuto del parametro nextActionObj.parameters viene in ogni caso
	 * 			          aggiunto ai parametri di chiamata dell'azione successiva (anche se non è una redirect)
	 * nSubEvento - {} umero di sottoevento, per esempio numero di ricerca, di custombutton etc. Indica la posizione nell'array.
	 * oggetto - {JSGeometry} oggetto al quale si riferisce l'azione
	 */
	doEventActions: function (eventoLayer, tipoEvento, keyValue, nStep, nextActionObj, idBtn, oggetto) {

		if ((nStep == null) || (nStep == undefined)) nStep = -1;
		
		nStep++;
		
		var azioniEventi = this.getAzioniEventi(eventoLayer, tipoEvento, idBtn);
		var bLastAction = (nStep==azioniEventi.azioneList.length);
		var azione = null;
		
		if (azioniEventi) {
			var bExit = false;
			if (azioniEventi.azioneList) {
				while ( (nStep<azioniEventi.azioneList.length) && !bExit ) {
					azione = azioniEventi.azioneList[nStep];
					
					if (azione.codeless) {
						this.fireEvent("codelessaction", eventoLayer, tipoEvento, oggetto);
					} else if (azione.viewCodeless && azione.viewCodeless.rigaList && azione.viewCodeless.rigaList.length > 0) {
						this.fireEvent("viewcodelessaction", eventoLayer, oggetto);
					} else {
						var url;
						var urlcompleta;
					
						 
					
						// definizione url
						/*
						if (azione.redirect) {
							if (nextActionObj.redirectUrl) {
								url = nextActionObj.redirectUrl;
							} else {
								url = azione.redirectUrl;
							}
						} else {
							url = azione.url;
						}
						*/
					
						if (azione.redirect) {
							if (nextActionObj.redirectUrl) {
								url = nextActionObj.redirectUrl;
							} else {
								url = azione.redirectUrl;
							}
						} else {
							// if (azione.useWMSGetFeatureInfo && oggetto.getFeatureInfoLink!=undefined && oggetto.getFeatureInfoLink!=null && oggetto.getFeatureInfoLink!="") {
							if (azione.useWMSGetFeatureInfo && oggetto.getFeatureInfoLink) {
								url = oggetto.getFeatureInfoLink;
								this.clearHighLigthed();
								if (oggetto.relatedGeoms!=undefined && oggetto.relatedGeoms!=null) this.addHighlighted(oggetto.relatedGeoms, false);
							} else { 						
								url = azione.url;
							}
						}
					
						if(!url) {
						
							this.fireEvent('visualize', eventoLayer.codTPN, keyValue);
						
						} else {
					
							/*
							// sostituzione dei valori #JS{} presenti nel tag <url> del file di preset						
							stringEx = /#JS{{1}[A-Z,a-z,0-9,_,., \/]*}/;
						    stringExInit= /^#JS{{1}/;
							stringExEnd= /}/;
							newJSUrl='';
							if (stringEx.test(url)) {
							   initStringIndex= url.search(stringExInit)+ 4;
							   endStringIndex= url.search(stringExEnd);
							   value= url.substring(initStringIndex, endStringIndex);
							   newJSUrl= url.replace(stringEx, eval(value));
							   url= newJSUrl;
							}
							*/
						
							// sostituzione dei valori #JS{} presenti nel tag <url> del file di preset
							var stringEx     = /#JS{{1}[A-Z,a-z,0-9,_,',",.,:,\?,=,!,<,>,\*,\(,\),\[,\],\^, \/]*}/;
						    var stringExInit = /#JS{{1}/;
							var stringExEnd  = /}/;
							var newJSUrl     = url;
							var suburl       = url;
							while (stringEx.test(suburl)) {
							
							   var initStringIndex = suburl.search(stringExInit);
							   var endStringIndex  = suburl.search(stringExEnd);
							   var name			   = suburl.substring(initStringIndex, endStringIndex+1);
							   var value           = suburl.substring(initStringIndex+4, endStringIndex);
							   newJSUrl			   = newJSUrl.replace(name, eval(value));
							   suburl 			   = (endStringIndex+2 <= suburl.length) ? suburl.substring(endStringIndex+2): "";
							   
							}
							url= newJSUrl;
						
							var questionMark = (url.indexOf("?") == -1) ? "?" : "";			
							urlcompleta = url + questionMark + "&" +  "IDTPN=" + escape(keyValue) + '&codTPN=' + escape(eventoLayer.codTPN);
						
							this.condizioneListToPostVar(azioniEventi);
						
							if((azione.forward != null) && (azione.forward != "")){
								urlcompleta += '&forward=' + escape(azione.forward);
							}
						
							if((azione.command != null) && (azione.command != "")){
								urlcompleta += '&command=' + escape(azione.command);
							}
						
							if((nextActionObj) && (nextActionObj.parameters!=null) && (nextActionObj.parameters!="")){
								for (var i=0; i<nextActionObj.parameters.length; i++){
									urlcompleta += '&' + nextActionObj.parameters[i].key + "=" + escape(nextActionObj.parameters[i].value);
								}
								//urlcompleta += '&' + escape(nextActionObj.parameters);
							}
						
							var method = (azione.method || 'POST').toUpperCase(); 
						
							if (!azione.ajaxCall) {
								// azione normale (non ajax)
								if (azione.noTolomeoDefaultParams) 
									this.openURL (url, azione.target, method, azione.noTolomeoDefaultParams);
								else
									this.openURL (urlcompleta, azione.target, method);
							} else {
								// azione ajax
								// è bloccante (si aspetta la fine per proseguire negli step
								// per questo bexit e' posto a true
							
								//salvataggio parametri per successivo passo da callback
								this.doEventActionsAjaxCallback.eventoLayer = eventoLayer;
								this.doEventActionsAjaxCallback.tipoEvento	= tipoEvento;
								this.doEventActionsAjaxCallback.keyValue	= keyValue;
								this.doEventActionsAjaxCallback.nStep		= nStep;
								this.doEventActionsAjaxCallback.idBtn		= idBtn;
								this.doEventActionsAjaxCallback.oggetto		= oggetto;
							
								//Chiamata ajax
								var ajaxOptions = {
									method: method,								
									url: urlcompleta,
									params: {
										geoOp: this.geoOpField.getValue(), 									
										geoCoord: method == 'POST' ? this.geoCoordField.getValue() : null,
										selectedList: this.selectedListField.getValue(),
										SRID: this.paramsJS.mappe.SRID,
										clippingCodTPN: eventoLayer.clippingCodTPN
									},
									scope: this,
									success: this.doEventActionsAjaxCallback,
									failure: this.doEventActionsAjaxFailure
								}
										
								if (azione.crossDomainAjax) {
									// Aggiunta parametro format='ext' in richiesta
									ajaxOptions.params.format = 'ext';
									new TolomeoExt.ToloCrossAjax().request(ajaxOptions);
								} else {
									Ext.Ajax.request(ajaxOptions);
								}
								bExit = true;
							}
						}
					}
					nStep++;
			
				}
				
				if ( bLastAction || 
				     (azione!=null && !azione.ajaxCall && nStep>=azioniEventi.azioneList.length )) {
					// dopo ultima azione...
					if (azioniEventi.refreshAtTheEnd) {
						this.viewer.pluginRefreshMap();
					}
					if (azioniEventi.closeAtTheEnd) {
						window.close();
					}
					
					if (azioniEventi.azioneList.length>0) {
						// se azioniEventi.azioneList.length=0 non sono state eseguite azioni quindi non ha senso fare nulla
						// ed è demandato alla gestione fatta probabilmente sull'evento actionsEnd
						switch (tipoEvento) {
						  case (this.eventVis):
		
						  		break;
						  case (this.eventCanc):
						  		this.clearSelected(false, eventoLayer.codTPN);
					  			this.refreshSelected();
						  		break;
						  case (this.eventUpdateGeom):
							  	this.refreshSelected();
						  		break;
						  case (this.eventUpdateAlpha):
							  	//this.refreshSelected();
						  		break;
						  case (this.eventIns):
							  	//this.refreshSelected();
						  		break;
						  case (this.eventInsFromLayer):
							    this.clearSelected(false);
							  	break;
						  case (this.eventInsFromImport):
							    this.clearSelected(false);
							  	break;
						  case (this.eventRicerca):
						  		break;
						  		
						}	
					}
					
			
				}
			}
		}
		
		if ((azioniEventi.azioneList.length==0) || (nStep>=azioniEventi.azioneList.length)) {
				// dopo ultima azione...anche se non ci sono azioni definite
				this.eventActionsEnd(keyValue, eventoLayer.codTPN);
		}
				
	},
	
	/**
	 * Eseguita a fine azioni.
	 *  - se definiti i campi di una form per ricevere i valori a fine azione provvede a settarli
	 *  - lancia l'evento actionsEnd 
	 * @param {String} keyValue
	 * @param {String} codTPN
	 */
	eventActionsEnd: function(IDTPN, codTPN) {
		
		var values = { geoOp: this.geoOpField.getValue(),
					   geoCoord: this.geoCoordField.getValue(),
					   geoParts: this.geoCoordParts.getValue(),
					   geomPartsAllFlag: this.geomPartsAllFlag.getValue(),
					   selectedList: this.selectedListField.getValue(),
					   IDTPN: IDTPN,
					   codTPN: codTPN};
		
		if (this.actionsEndReturnFields) {
        	for(var fieldName in this.actionsEndReturnFields){
        		if(this.actionsEndReturnFields[fieldName]){
        			var domField = Ext.fly(this.actionsEndReturnFields[fieldName]);
        			if(domField) domField.dom.value =  values[fieldName];
        		}
        	}
        }
        
        this.fireEvent("actionsEnd", values);
		
	},
	
	/**
	 * Eseguita quando una feature digitalizzata viene in un secondo momento modificata
	 * 
	 */
	digitizedFeatureModifyEnd: function(geom) {
		
		//TODOH Attenzione che l'utente potrebbe aver cambiato layer attivo!!!
		var	layer = this.getCurrentSelectLayer();
		
		geom.codTPN = layer.codTPN;
		geom.SRID = this.paramsJS.mappe.SRID;
		
		this.geometryToPostVar(geom);
		var domField = Ext.fly(this.actionsEndReturnFields['geoCoord']);
		if(domField) domField.dom.value =  this.geoCoordField.getValue();
        
		
		/* Se faccio così fa validazione ma se validazione non passa sparisce il punto
		 * this.currentDigitizeOperation = this.digitizeOperationInsert;
		this.onDigitizeEndGeometry(geom);
		
		 */
        this.fireEvent("digitizedFeatureModifyEnd", geom);
		
	},

	/**
	 * Method: doEventActionsAjaxCallback
	 * Funzione di callback per le chiamate ajax effettuate dalla funzione doEventActions. 
	 * Recupera i parametri e richiama doEventActions per effettuare il passo successivo.
	 *
	 * Parameters:
	 * transport - {} transport
	 */
	doEventActionsAjaxCallback: function (records, store, originalOptions) {
		// recupero eventuali parametri di ritorno ajax e gestione (passaggio ai successivi? 
		// TODO ALE var response = eval('(' + transport.responseText + ')');	
		var nextActionObj;
		// TODO ALE if (response) {
		// TODO ALE 	nextActionObj = response.nextAction;
		// TODO ALE }
		
		var tipoEvento = this.doEventActionsAjaxCallback.tipoEvento;
		var eventoLayer = this.doEventActionsAjaxCallback.eventoLayer
		var idBtn = this.doEventActionsAjaxCallback.idBtn;
		var nStep = this.doEventActionsAjaxCallback.nStep;
		var oggetto = this.doEventActionsAjaxCallback.oggetto;
		
		//var azioniEventi = this.getAzioniEventi(eventoLayer, tipoEvento, idBtn);
		//var azione = azioniEventi.azioneList[nStep];
		
		
		// Lancia l'evento. Se l'evento non è crossDomain records contiene trasport della chiamata normale e store non c'e'
		this.fireEvent("onEventActionAjaxSuccess", eventoLayer, tipoEvento, idBtn, nStep, records, store, oggetto);
		
		// Richiama doEventActions per eventuali step successivi
		// ripassandogli i parametri che erano stati salvati 
		this.doEventActions(eventoLayer,
							tipoEvento,
							this.doEventActionsAjaxCallback.keyValue,
							nStep,
							nextActionObj,
							idBtn,
							oggetto);
					   
	},

	/**
	 * Method: doEventActionsAjaxFailure
	 * Comunica l'errore, recupera i parametri e richiama doEventActions per effettuare il passo successivo.
	 *
	 * Parameters:
	 * transport - {} transport
	 */
	doEventActionsAjaxFailure: function (records, store, originalOptions) {
		this.showAjaxError(records);
		var nextActionObj;
		//ALE TODO
		// recupero eventuali parametri di ritorno ajax e gestione (passaggio ai successivi? 
		
		var tipoEvento = this.doEventActionsAjaxCallback.tipoEvento;
		var eventoLayer = this.doEventActionsAjaxCallback.eventoLayer
		var idBtn = this.doEventActionsAjaxCallback.idBtn;
		var nStep = this.doEventActionsAjaxCallback.nStep;
		var oggetto = this.doEventActionsAjaxCallback.oggetto;
		
		// Lancia l'evento. Se l'evento non è crossDomain records contiene trasport della chiamata normale e store non c'e'
		this.fireEvent("onEventActionAjaxFailure", eventoLayer, tipoEvento, idBtn, nStep, records, store, oggetto);
		
		// Richiama doEventActions per eventuali step successivi
		// ripassandogli i parametri che erano stati salvati 
		this.doEventActions(this.doEventActionsAjaxCallback.eventoLayer,
				this.doEventActionsAjaxCallback.tipoEvento,
				this.doEventActionsAjaxCallback.keyValue,
				this.doEventActionsAjaxCallback.nStep,
				nextActionObj,
				this.doEventActionsAjaxCallback.idBtn,
				this.oggetto);
					   
	},
	
	/**
	 * Method: openURLRaw
	 *
	 * Parameters:
	 * url - {String} url su cui fare il submit
	 * target - {String} target sul quale aprire la url
	 * noTolomeoDefaultParams - {boolean} indica se includere o meno i parametri di default di tolomeo (attivo solo con method=GET
	 * 
	 */
	openURLRaw: function(url, target, method, noTolomeoDefaultParams) {
		
		var form = this.submitForm.getForm();
		//console.log("apro con metodo " + method + " la url " + completeUrl);
				
		if(method && method.toUpperCase() == 'GET'){
			var valuesObj = form.getValues();
			var completeUrl = url;
			completeUrl += (completeUrl.indexOf("?") == -1) ? "?" : "";
			
			if (noTolomeoDefaultParams==null || noTolomeoDefaultParams==false) {
				// si escludono le coordinate per i limiti del get
				for(var i in valuesObj){
					//&&(i != "selectedList")
					if ((i != "geoCoord")){
						completeUrl += "&" + i + "=" + encodeURIComponent(valuesObj[i]);
					}
				}	
			}
			window.open(completeUrl,target);
			
		} else {

			var submitParams = {
	               			method: 'POST',
	               			target: target,
	            			url: url
	               		}
	               		
	        form.submit(submitParams);
	               		
	        /*
			var formDom = form.getEl().dom;
			formDom.action = url;
			formDom.method = 'POST';
			formDom.target = target;		
			form.submit();*/
		}
	},
	
	/**
	 * Method: openURL
	 * Esegue il submit su una certa url e su un determinato target. Nella form sono presenti i campi geoCoord e geoOp.
	 *
	 * Parameters:
	 * url - {String} url su cui fare il submit
	 * target - {String} target sul quale aprire la url
	 * noTolomeoDefaultParams - {boolean} indica se includere o meno i parametri di default di tolomeo (attivo solo con method=GET
	 * 
	 */
	openURL: function (url, target, method, noTolomeoDefaultParams) {
		if(this.fireEvent("beforeOpenUrl", url, target) === false)
			return;
		/*if (target!="pannello")*/
		this.openURLRaw(url, target, method, noTolomeoDefaultParams);
		this.fireEvent("openUrl", url, target);
		
	},

	/**
	 * Method: clearURL
	 * Esegue il submit su una certa url e su un determinato target. Nella form sono presenti i campi geoCoord e geoOp.
	 *
	 * Parameters:
	 * target - {String} target sul quale aprire la url
	 * method - {String} metodo per la chiamata (GET o POST)
	 */
	clearURL: function (target, method) {
		var url = (this.paramsJS.azioniApertura.urlPannello) ? this.paramsJS.azioniApertura.urlPannello : this.TOLOMEOServer + this.TOLOMEOStaticRoot + "html/blank.html";		
		if(this.fireEvent("beforeClearUrl", url, target) === false)
			return;		
		/*if (target!="pannello")*/
		this.openURLRaw(url, target, method, true);
		this.fireEvent("clearUrl", url, target);
		
	},
	
	/**
	 * Method: geoOpToPostVar
	 * Setta l'operazione nella form per la trasmissione.
	 *
	 * Parameters:
	 * geoOp - {Ext.form.TextField} geoOp operazione (vedi costanti)
	 */
	geoOpToPostVar: function (geoOp){
		
		this.geoOpField.setValue(geoOp);
		
		//submitForm.geoOp.value = geoOp;
	},

	/**
	 * Method: geometryToPostVar
	 * Setta la geometrie nella form per la trasmissione alla servlet.
	 *
	 * Parameters:
	 * jsGeometry - {JSGeometry} jsGeometry Geometria
	 */
	geometryToPostVar : function (jsGeometry) {
		
		 this.geoCoordField.setValue(jsGeometry)
			
	},	
	
	/**
	 * Method: geomPartsToPostVar
	 * Setta geomParts nella form per la trasmissione alla servlet.
	 *
	 * Parameters:
	 * geomParts - {JSGeometryAttay} 
	 */
	geomPartsToPostVar : function (geomParts) {
		
		 this.geoCoordParts.setValue(geomParts)
			
	},
	
	/**
	 * Method: geomPartsAllFlagToPostVar
	 * Setta partsAllFlag (flag che indica se le parti presenti nella geomParts sono tutte) nella form per la trasmissione alla servlet.
	 *
	 * Parameters:
	 * partsAllFlag - {boolean} partsAllFlag
	 */
	geomPartsAllFlagToPostVar : function (partsAllFlag) {
		
		 this.geomPartsAllFlag.setValue(partsAllFlag)
			
	},

	/**
	 * Method: condizioneListToPostVar
	 * Setta nella form i valori delle chiavi relative agli oggetti selezionati e richiesti dalla condizioneList 
	 * dell'azioneEvento. Il formato utilizzato e' il seguente.
	 *
	 * <![CDATA[
	 * <condizioneList>
	 * 		<layer>
	 * 			<codTPN>-900</codTPN>
	 * 			<IDTPN>1000</IDTPN>
	 * 			<IDTPN>1002</IDTPN>
	 * 		</layer> 
	 * 		<layer>
	 * 			<codTPN>-1000</codTPN>
	 * 			<IDTPN>12</IDTPN>
	 * 			<IDTPN>13</IDTPN>
	 * 			<IDTPN>112</IDTPN>
	 * 		</layer>
	 * </condizioneList>
	 * ]]>
	 *
	 * dove 
	 *
	 * -900 e -1000 sono i codTPN di due layer indicati nella codnizioneList
	 * 1000 e 1002 sono gli oggetti selezionati appartenenti al layer -900 
	 * 12, 134 e 112 sono gli oggetti selezionati appartenenti al layer -1000 
	 *
	 * Parameters:
	 * azioniEventi - {} azioniEventi
	 */
	condizioneListToPostVar: function (azioniEventi) {
		
		if (azioniEventi.condizioneList) {
			var condList = azioniEventi.condizioneList;
			var condListParam = "<condizioneList>";
			for (var i=0; i<condList.length; i++) {
				var cond = condList[i];
				var selezionati = this.selezioneCorrente.getByCodTPN(cond.codTPN).geometries;
				if ((selezionati!=null) && (selezionati.length>0)) {
					condListParam += "<layer><codTPN>" + cond.codTPN + "</codTPN>" ;
					for (var j=0; j<selezionati.length; j++) {
						condListParam += "<IDTPN>" + selezionati[j].key + "</IDTPN>" ;
					}
					condListParam += "</layer>" ;
				}
			}
			condListParam += "</condizioneList>" ;
		}
		
		this.selectedListField.setValue(condListParam);
		//submitForm.selectedList.value = condListParam;
		
	},

	/**
	 * Method: verifyEventsSelectedConditions
	 *
	 * Parameters:
	 * eventoLayer - {} eventoLayer 
	 * tipoEvento - {} tipoEvento 
	 * nSubEvento - {} nSubEvento
	 */
	verifyEventsSelectedConditions: function (eventoLayer, tipoEvento, nSubEvento) {
		return this.eventsSelectedConditionsfullCheck(eventoLayer, tipoEvento, nSubEvento).ok;
	}, 

	/**
	 * Method: messageEventsSelectedConditions
	 *
	 * Parameters:
	 * eventoLayer - {} eventoLayer 
	 * tipoEvento - {} tipoEvento 
	 * nSubEvento - {} nSubEvento
	 */
	messageEventsSelectedConditions: function (eventoLayer, tipoEvento, nSubEvento) {
		return this.eventsSelectedConditionsfullCheck(eventoLayer, tipoEvento, nSubEvento).disabledMessage;	
	},

	/**
	 * Method: eventsSelectedConditionsfullCheck
	 * Verifica se sono rispettate le condizioni per abilitare un certo evento 
	 * (limitatamente agli oggetti selezionati) ed il messaggio con le motivazioni se le condizioni non siano rispettate 
	 * contiene il messaggio con le motivazioni. Ogni motivazione viene aggiunta alla precedente.
	 *
	 * Parameters:
	 * eventoLayer - {} eventoLayer 
	 * tipoEvento - {} tipoEvento 
	 * nSubEvento - {} nSubEvento
	 */
	eventsSelectedConditionsfullCheck: function (eventoLayer, tipoEvento, nSubEvento) {
		var ret = true;
		var disabledMessage = "";
		
		var azioniEventi = this.getAzioniEventi(eventoLayer, tipoEvento, nSubEvento);
		
		switch (tipoEvento) {
			 case (this.eventVis):
			 case (this.eventCanc):
			 case (this.eventUpdateGeom):
			 case (this.eventUpdateAlpha):
			
				   // verifica se selezionato un oggetto del corrispondente codTPN
				   // Tolto per consentire corretto funzionamento 
				   if (eventoLayer && !this.selezioneCorrente.ContainsCodTPN(eventoLayer.codTPN)) {
				   		disabledMessage += ToloI18n.getMsg("ToloMapAPIExt.digitizeByCADStop.evSelCondFullChck.msg1");	
				   		ret=false;
				   }
				   break;
			 case (this.eventCustomButton): 
			 /*
			 	if(nSubEvento==-1) {
			 		ret = true;
			 		break;
			 	}
			 */	
			 	// se non ci sono azioni eventi non si fa apparire il pulsante
			 	if(!azioniEventi) {
			 		ret = false;
			 	// se l'azione è del layer e non global si richiede la selezione di almeno un elemento del layer
			 	}else if (azioniEventi.isOfTheLayer && !this.selezioneCorrente.ContainsCodTPN(eventoLayer.codTPN)) {
				   	disabledMessage += ToloI18n.getMsg("ToloMapAPIExt.digitizeByCADStop.evSelCondFullChck.msg2");
				   	ret=false;
				} 
			 break;
			 case (this.eventIns):	break;
			 case (this.eventInsFromLayer):
				 // Verifica che ci sia nella selezione corrente almeno un oggetto dei layer abilitati come sorgente
				 if (azioniEventi && azioniEventi.enabledLayerList) {
						var condList = azioniEventi.enabledLayerList;
						var buff = false;
						for (var i=0; i<condList.length; i++) {
							var cond = condList[i];
							if (this.selezioneCorrente.ContainsCodTPN(cond.codTPN)) {
								buff=true;
								break;
							}
						}
						if (!buff) {
							disabledMessage += ToloI18n.getMsg("ToloMapAPIExt.digitizeByCADStop.evSelCondFullChck.msg3");
							ret=false;
						}
				 }
				 
				 
				 break;
			 case (this.eventInsFromImport): break;
			 case (this.eventRicerca): break;
		}
		
		// In tutti i casi devono essere verificate le condizioni aggiuntive se presenti
		if (azioniEventi && azioniEventi.condizioneList) {
			var condList = azioniEventi.condizioneList;
			for (var i=0; i<condList.length; i++) {
				var cond = condList[i];
				if (!this.selezioneCorrente.ContainsCodTPN(cond.codTPN)) {
					disabledMessage += ToloI18n.getMsg("ToloMapAPIExt.digitizeByCADStop.evSelCondFullChck.msg1",{nomeLayer:cond.nomeLayer});	
					ret=false;
				}
			}
		}
		
		return {ok: ret, disabledMessage: disabledMessage} ;
	},
	
	/**
	 * Method: enablePermittedOp
	 *
	 * Parameters:
	 * opCode - {} opCode 
	 * bPermitted - {} bPermitted 
	 */
	enablePermittedOp: function(opCode, bPermitted) {
		eventName = (bPermitted ? 'onOperationEnable' : 'onOperationDisable' );
		this.fireEvent(eventName, opCode);
	},
	
	/**
	 * Method: togglePermittedOperations
	 * Modifica lo stato delle icone per renderlo coerente con le operazioni possibili in funzione dell'oggetto selezionato e del layer
	 * Tipo di editing singolo attivo. Valori possibili:
	 *  0 - nessun editing singolo 
	 *  1 - inserimento completo   
	 *  2 - inserimento di sola geometria
	 *  3 - modifica geometria
	 *
	 *  Se editingJSGeometry==null  -> inserimento completo di oggetto con layerCODTPN e valorechiave. Per effettuare l'inserimento vengono invocate 
	 *  le stesse azioni previste per l'inserimento normale
	 *  se editingJSGeometry!=null ma con geometria nulla -> viene abilitato l'inserimento della geometria, ma l'azione risultante sarà la stessa di una updateGeom
	 *  se editingJSGeometry!=null e con geometria non nulla -> vengono abilitate le azioni di update sulla geometria
	 */	 
	//TODO
	togglePermittedOperations: function () {

		var selLayer = this.getCurrentSelectLayer();
		var sctpn = this.paramsJS.getSelectableCodTPN();
		var withSnap = false;		
		
		
		// se nessun layer selezionato nascondi bottone di selezione
		this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnSeleziona, sctpn && sctpn.length > 0);

		var tipoEditingSingolo;
		
		// Stabilisco in quale modalità sono
		if ((this.paramsJS.azioniApertura.modoEditingSingolo != null) && (this.paramsJS.azioniApertura.modoEditingSingolo != "")) {
			if ((this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!=null) && 
				(this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry!="")) {
				var geoms = new JSGeometryArray();
				geoms.FromUntypedArray(this.paramsJS.azioniApertura.modoEditingSingolo.editingJSGeometry);
				if ((geoms.geometries.length==1) && (geoms.geometries[0].geometry!="")) {
					tipoEditingSingolo = 3;  // 3 - modifica geometria	
				} else {
					tipoEditingSingolo = 1;  // 2 - inserimento di sola geometria	
				} 	
			} else {
				tipoEditingSingolo = 1;  // 1 - inserimento completo  
			}
		} else {
			tipoEditingSingolo = 0; 	// 0 - nessun editing singolo
		}
		//selLayer.azioniEventiIns.noAction || selLayer.azioniEventiIns.azioneList.length
		if (selLayer && (selLayer.azioniEventiIns.forceEnable || selLayer.azioniEventiIns.azioneList.length!=0) && this.verifyEventsSelectedConditions(selLayer, this.eventIns) &&
			this.bEditingSingoloInsertDone == false &&
			(tipoEditingSingolo==0    ||
		     	tipoEditingSingolo==1 ||
		     	tipoEditingSingolo==2))  {
			//TODO
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnNuovo, true);
			
			withSnap = true;

		} else {
			//TODO
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnNuovo, false);
			// TODO var disMess = messageEventsSelectedConditions(selLayer, eventIns);
			// TODO if (disMess!="") bottoni[btnNuovo].disabledMessage ;
			
		}

		if (this.isPermittedInsFromLayer(selLayer, tipoEditingSingolo)) {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnNuovoDaLayer, true);
		} else {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnNuovoDaLayer, false);
			
		}

		if (this.isPermittedInsFromImport(selLayer, tipoEditingSingolo)) {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnNuovoDaImport, true);
		} else {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnNuovoDaImport, false);
		}
		
		/*
		if (selLayer && 
			(selLayer.azioniEventiInsFromLayer.forceEnable || selLayer.azioniEventiInsFromLayer.azioneList.length!=0) && 
			this.verifyEventsSelectedConditions(selLayer, this.eventInsFromLayer) &&
			this.bEditingSingoloInsertDone == false &&
			(tipoEditingSingolo==0    ||
			tipoEditingSingolo==1 ||
			tipoEditingSingolo==2))  {
			   this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnNuovoDaLayer, true);
		} else {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnNuovoDaLayer, false);
			
		}*/

		//selLayer.azioniEventiCanc.azioneList.length != 0
		if (selLayer && (selLayer.azioniEventiCanc.forceEnable || selLayer.azioniEventiCanc.azioneList.length != 0 ) && this.verifyEventsSelectedConditions(selLayer, this.eventCanc) && (tipoEditingSingolo == 0) ) {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnDelete, true);
			//bottoni[btnDelete].setEnabled(true);
		} else {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnDelete, false);
			//bottoni[btnDelete].setEnabled(false);
			// TODO var disMess = messageEventsSelectedConditions(selLayer, eventCanc);
			//TODO if (disMess!="") bottoni[btnDelete].disabledMessage = disMess;
		}  
		//selLayer.azioniEventiUpdateGeom.azioneList.length != 0
		if (selLayer && (selLayer.azioniEventiUpdateGeom.forceEnable || selLayer.azioniEventiUpdateGeom.azioneList.length != 0 ) && 
			 this.verifyEventsSelectedConditions(selLayer, this.eventUpdateGeom) && 
			 ((tipoEditingSingolo == 0) || 
			  (tipoEditingSingolo == 3  )))  {
			if (selLayer.copertura) {
				this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnAdd, false);
				this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnSubtract, false);
				this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnVertexEdit, false);
				this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnAddSub, true);

				// TODO bottoni[btnAdd].disabledMessage = "Funzione non disponibile su layer di tipo copertura";
				// TODO bottoni[btnSubtract].disabledMessage = "Funzione non disponibile su layer di tipo copertura";
				// TODO bottoni[btnAddSub].disabledMessage = "Funzione non disponibile su layer di tipo copertura";
				// TODO bottoni[btnVertexEdit].disabledMessage = "Funzione non disponibile su layer di tipo copertura";
				
			} else {
				if (selLayer.azioniEventiUpdateGeom.conAdd) this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnAdd, true); //bottoni[btnAdd].setEnabled(true);
					else this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnAdd, false); //bottoni[btnAdd].setEnabled(false);
				if (selLayer.azioniEventiUpdateGeom.conSubtract) this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnSubtract, true);  //bottoni[btnSubtract].setEnabled(true);
					else this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnSubtract, false); //bottoni[btnSubtract].setEnabled(false);
				this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnAddSub, false); //bottoni[btnAddSub].setEnabled(false);
				if (selLayer.azioniEventiUpdateGeom.conVertexEditing) this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnVertexEdit, true); //bottoni[btnVertexEdit].setEnabled(true);
					else this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnVertexEdit, false); // bottoni[btnVertexEdit].setEnabled(false);
				if (selLayer.azioniEventiUpdateGeom.conDragDrop) this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnDragDrop, true); //bottoni[btnDragDrop].setEnabled(true);
					else this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnDragDrop, false); //bottoni[btnDragDrop].setEnabled(false);
			}
			
			withSnap = true;			
			
		} else {
			// TODO var disMess = messageEventsSelectedConditions(selLayer, this.eventUpdateGeom);
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnAdd, false);
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnSubtract, false);
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnAddSub, false);
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnVertexEdit, false);
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnDragDrop, false);
			
			//TODO if (disMess!="") bottoni[btnAdd].disabledMessage = disMess;
			//TODO if (disMess!="") bottoni[btnSubtract].disabledMessage = disMess;
			//TODO if (disMess!="") bottoni[btnAddSub].disabledMessage = disMess;
			//TODO if (disMess!="") bottoni[btnVertexEdit].disabledMessage = disMess;
			//TODO if (disMess!="") bottoni[btnDragDrop].disabledMessage = disMess;
		} 
		//selLayer.azioniEventiUpdateAlpha.azioneList.length != 0
		if (selLayer && ((selLayer.azioniEventiUpdateAlpha.forceEnable || selLayer.azioniEventiUpdateAlpha.azioneList.length != 0 ) && this.verifyEventsSelectedConditions(selLayer, this.eventUpdateAlpha)) && (tipoEditingSingolo == 0)) {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnUpdateAlfa, true);
			//bottoni[btnUpdateAlfa].setEnabled(true);
		} else {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnUpdateAlfa, false);
			//bottoni[btnUpdateAlfa].setEnabled(false);
			//TODO if (disMess!="") bottoni[btnUpdateAlfa].disabledMessage = messageEventsSelectedConditions(selLayer, eventUpdateAlpha);
		}
		
		//if ((selLayer.azioniEventiVis.azioneList.length != 0) && bSelectedObjectLayerOK &&  (tipoEditingSingolo == 0)) {
		//selLayer.azioniEventiVis.azioneList.length != 0
		if (selLayer &&  (selLayer.azioniEventiVis.forceEnable || selLayer.azioniEventiVis.azioneList.length != 0) && this.verifyEventsSelectedConditions(selLayer, this.eventVis) &&  (tipoEditingSingolo == 0)) {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnIdentify, true);
			//bottoni[btnIdentify].setEnabled(true);
		} else {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnIdentify, false);
			// bottoni[btnIdentify].setEnabled(false);
			//TODO if (disMess!="") bottoni[btnIdentify].disabledMessage = messageEventsSelectedConditions(selLayer, eventVis);
		}  
		
		if(selLayer && selLayer.snappable && withSnap){
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnSnap, true);
		} else {
			this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnSnap, false);
		}	
		
		// CustomButtons TODO
		/* TODO TUTTO 
		for (var i=btnCustomBase; i<bottoni.length; i++) {
			var cb = bottoni[i];

				if  (selLayer && verifyEventsSelectedConditions(selLayer, eventCustomButton, cb.idCustomButton)  ) {
					cb.setEnabled(true);
				} else {
					cb.setEnabled(false);
					var disMess = messageEventsSelectedConditions(selLayer, eventCustomButton, cb.idCustomButton);
					if (disMess!="") cb.disabledMessage = disMess;
				}
		}
		*/
		
		for (var i = 0; i < this.paramsJS.layOut.customButtonList.length; i++) {
			var cb = this.paramsJS.layOut.customButtonList[i];
				if  (selLayer && this.verifyEventsSelectedConditions(selLayer, this.eventCustomButton, cb.idCustomButton)  ) {
					this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnCustomBase + i, true);
					//cb.setEnabled(true);
				} else {
					this.enablePermittedOp(TolomeoExt.ToloAPIOpCodes.btnCustomBase + i, false);
					//cb.setEnabled(false);
					//var disMess = messageEventsSelectedConditions(selLayer, eventCustomButton, cb.idCustomButton);
					//if (disMess!="") cb.disabledMessage = disMess;
				}
		}
		  
		
	},

	/**
	 * Method: isPermittedInsFromLayer
	 * Verifica se sul layer passato, nella modalità richiesta, è permesso l'inserimento da layer tenendo conto degli oggetti selezionati e delle impostazioni di preset
	 *
	 * Parameters:
	 * selLayer - {} layer su cui fare la verifica. 
	 * tipoEditingSingolo - {Number}  0 - nessun editing singolo, 1 - inserimento completo, 2 - inserimento di sola geometria, 3 - modifica geometria
	 * 
	 * 
	 * 
	 */
	isPermittedInsFromLayer: function(selLayer, tipoEditingSingolo){
		
		if (selLayer && selLayer.azioniEventiInsFromLayer &&
				(selLayer.azioniEventiInsFromLayer.forceEnable || selLayer.azioniEventiInsFromLayer.azioneList.length!=0) && 
				this.verifyEventsSelectedConditions(selLayer, this.eventInsFromLayer) &&
				this.bEditingSingoloInsertDone == false &&
				(tipoEditingSingolo==0    ||
				tipoEditingSingolo==1 ||
				tipoEditingSingolo==2))  {
				   return true;
		} else {
			return false;
			
		}
		
	},
	
	/**
	 * Method: isPermittedInsFromImport
	 * Verifica se sul layer passato, nella modalità richiesta, è permesso l'inserimento da import tenendo conto degli oggetti selezionati e delle impostazioni di preset
	 *
	 * Parameters:
	 * selLayer - {} layer su cui fare la verifica. 
	 * tipoEditingSingolo - {Number}  0 - nessun editing singolo, 1 - inserimento completo, 2 - inserimento di sola geometria, 3 - modifica geometria
	 * 
	 * 
	 * 
	 */
	isPermittedInsFromImport: function(selLayer, tipoEditingSingolo){
		                         
		if (selLayer && selLayer.azioniEventiInsFromImport &&
				(selLayer.azioniEventiInsFromImport.forceEnable || selLayer.azioniEventiInsFromImport.azioneList.length!=0) && 
				this.verifyEventsSelectedConditions(selLayer, this.eventInsFromImport) &&
				this.bEditingSingoloInsertDone == false &&
				(tipoEditingSingolo==0    ||
				tipoEditingSingolo==1 ||
				tipoEditingSingolo==2))  {
				   return true;
		} else {
			return false;
		}
	},
	
	
	/**
	 * Method: onChangeFromChoice
	 * Quando in fase di select sono presenti più oggetti e viene richiesta la scelta all'utente. Questa funzione viene chiamata quando la scelta cambia.
	 *
	 * Parameters:
	 * geom - {} la geometria. 
	 */
	onChangeFromChoice: function (geom){
		var geoms = new JSGeometryArray();
		geoms.geometries[0] = geom;
		this.addHighlighted(geoms);
	},
	
	/**
	 * Method: onSelectedFromChoice
	 * Quando in fase di select sono presenti più oggetti e viene richiesta la scelta all'utente. Questa funzione viene chiamata quando la scelta è stata effettuata.
	 *
	 * Parameters:
	 * geom - {} la geometria. 
	 */
	//TODO
	onSelectedFromChoice: function (geom,addToSelected,visualize) {
		
		this.clearHighLigthed(false);
		var combobox = document.getElementById("scelta");
		
		if (this.autoCloseChoiceWindow && this.selectedChoiceWindow) this.selectedChoiceWindow.close();
		
		var geoms = new JSGeometryArray();
		geoms.geometries[0] = geom; 
		
		this.addSelected(geoms,addToSelected,visualize);
	},

	/**
	 * Method: updateZoomToScale
	 * Aggiorna il valore della scala nel giusto campo.
	 *
	 * Parameters:
	 * scale - {} il valore di scala. 
	 */
	updateZoomToScale: function (scale){
		// TODO da provare la sostituzione con Ext
		Ext.get('scaleinput').value = Math.round(scale);
		//$('scaleinput').value = Math.round(scale);
	},
	
	// Autoidentify - INIZIO
	
	/**
	 * Method: autoIdentifyLayers
	 *
	 */
	autoIdentifyLayers: function () {
		var codTPNStr = "";
		for(var index=0; index<this.paramsJS.azioniEventi.eventiLayerList.length; index++) {
			var paramJSLayer = this.paramsJS.azioniEventi.eventiLayerList[index];
			if (paramJSLayer.autoIdentifyAllowed==true)  {
				if (this.TOCPanel.layerIsVisible(paramJSLayer.codTPN)==true) {
					codTPNStr += (codTPNStr!="") ? "," : "";
					codTPNStr += paramJSLayer.codTPN;
				}
			}
		}
		return codTPNStr;
	},

	/**
	 * Method: autoIdentifyLayersPresent
	 *
	 */
	autoIdentifyLayersPresent: function (){
		return (this.autoIdentifyLayers()=="") ? false : true;
	},

	/**
	 * Method: autoIdentifyLayersWithHighlight
	 *
	 * Parameters:
	 * codTPN - {} codTPN 
	 */
	autoIdentifyLayersWithHighlight: function (codTPN){
		var retVal = false;
		for(var index=0; index<this.paramsJS.azioniEventi.eventiLayerList.length; index++) {
			var paramJSLayer = this.paramsJS.azioniEventi.eventiLayerList[index];
			if ((paramJSLayer.autoIdentifyAllowed) && (paramJSLayer.autoIdentifyWithHighlight) && (
					(paramJSLayer.codTPN==codTPN) || (codTPN==undefined))) {
				retVal = true;
				break;
			}
		}
		return retVal;
	},

	/**
	 * Method: onAutoIdentify
	 *
	 * Parameters:
	 * point - {} il punto. 
	 * mouseX - {} coordinata x del mouse. 
	 * mouseY - {} coordinata y del mouse.
	 *  
	 */
	onAutoIdentify: function (point, mouseX, mouseY, mapXPixel, mapYPixel) {

		// raggio di tolleranza 6 pixel
		var tolleranceRange = this.viewer.pluginGetResolution() * 6;
			
		// Creazione elenco layer da interrogare
		var codTPNStr = this.autoIdentifyLayers();
		
		// TODO supportata solo mappa 0
		var nmappa = 0;
		var buff = codTPNStr.split(",");
		var stylesArr = new Array();
		
		for (var i=0; i<buff.length; i++) {
			var legendaInfo = this.paramsJS.getLegendaLayerInfoByCodTPN(buff[i], nmappa, (this.TOCPanel ? this.TOCPanel.tocInfo : null))			
			var stylebuff = (legendaInfo.tocInfoLayerInfo) ? legendaInfo.tocInfoLayerInfo.style : (legendaInfo.presetLayerInfo ? legendaInfo.presetLayerInfo.defaultStyle : "");  			
			stylesArr.push(stylebuff);
		}
		
		var styles = stylesArr.join(",");
		
		var mousePos = new Object();
		mousePos.mouseX = mouseX;
		mousePos.mouseY = mouseY;
		
		if (codTPNStr!="") {
			var bounds = this.viewer.pluginGetMapExtent();
			
			// Chiamata Ajax per effettuare l'intersezione e ricevere l'oggetto selezionato
			var ajaxOptions = {
				method: 'post',
				url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxSpatialQueryServlet',
				scope: this,
				mousePos: mousePos,
				params: {
					coordX: point.x, 
					coordY: point.y,
					codTPN: codTPNStr,
					styles: styles,
					range: tolleranceRange,
					SRID: this.paramsJS.mappe.SRID,
					format: 'ext',
					//Parametri aggiunti per GetFeatureInfo
					bbox:  bounds.left+","+bounds.bottom+","+bounds.right+","+bounds.top ,
					mapwidth:  this.viewer.pluginGetMapWidth()  ,
					mapheight: this.viewer.pluginGetMapViewerHeight() ,
					X: mapXPixel,
					Y: mapYPixel,
					paramPreset: this.paramsJS.nomePreset
				},
				success: this.ajaxAutoIdentifyOK,
				failure: this.showAjaxError 
			}

			// Aggiunta parametri WMS da url
			Ext.apply(ajaxOptions.params, this.paramsJS.urlAdditionalParams);
			new TolomeoExt.ToloCrossAjax().request(ajaxOptions);	
		}
	},

	/**
	 * Method: onAutoIdentifyCancel
	 * Provvede a chiudere la finestra di autoIdentify e fa il clear delle evidenziazioni.
	 *
	 */
	onAutoIdentifyCancel: function () {
		if (this.autoIdentifyWindow)
			this.autoIdentifyWindow.close();
		if (this.autoIdentifyLayersWithHighlight())
			this.clearAutoidentified();
	},

	/**
	 * Method: ajaxAutoIdentifyOK
	 * Funzione di callback per la chiamata ajax onMappaSelect {link #onMappaSelect} che identifica gli oggetti presenti in una certa posizione.
	 *
	 * Parameters:
	 * results - {} results 
	 * store - {} store 
	 * originalOptions - {} originalOptions 
	 */
	ajaxAutoIdentifyOK: function(results, store, originalOptions) {
		
		var geoms = new JSGeometryArray();
		geoms.FromStore(results, store);
		
		if (geoms.geometries.length==0) return;
		var szMessaggio = "";
		szMessaggio += '<ul>';
		for(var index=0; index<this.paramsJS.azioniEventi.eventiLayerList.length; index++) {
			var codTPN = this.paramsJS.azioniEventi.eventiLayerList[index].codTPN;
			var layerDesc = this.paramsJS.azioniEventi.eventiLayerList[index].descrizioneLayer;
			
			var g = geoms.getByCodTPN(codTPN);
			if (g!=null) {
				
				szMessaggio += '<li style="font-weight:bold">' + layerDesc + ':</li>';
				for (var i=0; i < g.geometries.length; i++) {
					szMessaggio += '<li style="margin-left:10px">' + g.geometries[i].description.replace(/\\\"/g,"\"") + '</li>';
				}
				if (this.autoIdentifyLayersWithHighlight(codTPN)) this.addAutoidentified(g);
			}
		}
		szMessaggio += "</ul>";

		if (this.autoIdentifyWindow!=null) this.autoIdentifyWindow.close(); 
		
		this.autoIdentifyWindow = Ext.create('Ext.Window', {
			html: szMessaggio,
			cls: 'clearCSS',
			bodyStyle: 'background-color:white;padding:2px',
			closable: false,
			draggable: false,
			maximizable: false,
			minimizable: false,
			resizable: false, 
			constrain: true,
			border: false
		}).show();
		
		if (Ext.isIE) {
			this.autoIdentifyWindow.setWidth(350);
		}
		
		var dialogWidth  = this.autoIdentifyWindow.getWidth() ;
		var dialogHeight = this.autoIdentifyWindow.getHeight();
		
		var viewerPos = this.viewer.getPosition();
		var mouseX = originalOptions.mousePos.mouseX - viewerPos[0];
		var mouseY = originalOptions.mousePos.mouseY - viewerPos[1];
		
		var dialogPosX = (mouseX+15+dialogWidth<this.viewer.getWidth()) ? originalOptions.mousePos.mouseX+15 : originalOptions.mousePos.mouseX-15-dialogWidth;
		var dialogPosY = (mouseY+dialogHeight<this.viewer.getHeight())  ? originalOptions.mousePos.mouseY    : originalOptions.mousePos.mouseY-dialogHeight;

		this.autoIdentifyWindow.setPosition(dialogPosX, dialogPosY);
	}, 

	/**
	 * Method: autoIdentifyEnable
	 *
	 * Parameters:
	 * bEnable - {} bEnable 
	 */
	autoIdentifyEnable: function (bEnable) {
		this.viewer.pluginAutoIdentifyEnable(bEnable);
	},
			
	/**
	 * Method: gotoLocationEnable
	 * Inizializza il sistema per l'inserimento delle coordinate da ricercare
	 */
	gotoLocationEnable: function(){
		if(!this.gotoLocWin){
			this.gotoLocWin = new TolomeoExt.ToloGotoLocationWindowExt({projectionCrs: this.projectionCrs, projectionCode: this.getProjectionCode(), id:'gotoLoc',closable: true,closeAction: 'hide',iconCls: 'iconGotoPosition'});
			this.gotoLocWin.on('afterlayout',
				function(){
					this.gotoLocWin.centerTo(this.viewer.getEl());
				},this,{single: true});			
			this.gotoLocWin.on('gotoLocation',
				function(x,y,crs){
					// Nel caso di trasformazione ho bisogno delle giuste librerie
					if(crs && crs!= this.getProjectionCode()){
						this.lazyLoadScript(
							'proj4js',
							function(){ this.gotoPosition(x, y, null, true, crs); },
							function(){Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.gotoLocationEnable.title"), ToloI18n.getMsg("ToloMapAPIExt.gotoLocationEnable.msg", {ProjectionCode:this.getProjectionCode()}), function(){this.gotoLocWin.show();},this);},
							this);			
					} else {
						this.gotoPosition(x, y, null, true, crs);
					}
				}, this);
		}
		this.gotoLocWin.show();
			
	},
	
	/**
	 * Method: updateNoteOnMap
	 * Aggiornamento delle note
	 * @param {} id
	 * @param {} htmlText
	 */
	updateNoteOnMap: function(id,htmlText,lon,lat){
		var noteW = this.createNoteWidget('update',{
			title: ToloI18n.getMsg("ToloMapAPIExt.updateNoteOnMap.title"),
			htmlValue : htmlText,
			popupId: id,
			lon: lon,
			lat: lat
		});
		noteW.show();
	},
	
	/**
	 * Method: insertNoteOnMap
	 * Aggiornamento delle note
	 * @param {} posX
	 * @param {} posY
	 */
	insertNoteOnMap: function(posX,posY){
		var noteW = this.createNoteWidget('insert',{
			title: ToloI18n.getMsg("ToloMapAPIExt.insertNoteOnMap.title"),
			x: posX,
			y: posY
		});
		noteW.show();		
	},
	
	createNoteWidget: function(mode,config){
		var coordinate;
		
		if(mode=='update'){
			coordinate = {x: config.lon, y: config.lat};
		} else {
			var viewPos = this.getViewerPosition();
			coordinate = this.viewer.pluginGetCoordinateFromPixel({x:(config.x-viewPos.x),y:(config.y-viewPos.y)});
		}
		
		var formattedCoords = this.formatCoords(coordinate);
		var thisid = this.apiid;
		
		var cnw = Ext.create('Ext.window.Window', {
			layout: 'fit',
			width: 500,
			title: !config.title ? ToloI18n.getMsg("ToloMapAPIExt.createNoteWidget.title") : config.title,
			height: 250,
			iconCls: (mode=='update' ? 'iconEditNote' : 'iconInsertNote'),
			cls: 'clearCSS',
			modal: true,
			items:[{
		    	xtype: 'htmleditor',
		    	cls: 'clearCSS',
		    	enableAlignments: false,
		    	enableFont: false,
		    	enableLinks: true,
		    	enableSourceEdit: false,
		    	value: !config.htmlValue ? '' : config.htmlValue,
		    	listeners: {
	    			change: function(htlmcontrol, newval, oldval, eopts) {
	    				Ext.getCmp(thisid + '-btnOkInsNota').setDisabled(newval=="");
		    		},
		    		render: {
		    			fn: function(){
		    				this.getToolbar().add('-');
		    				this.getToolbar().add({
		    					tooltip: ToloI18n.getMsg("ToloMapAPIExt.createNoteWidget.tooltip"),
		    					iconCls : 'iconAddLocation',
		    					handler : function(){
		    						cnw.down('htmleditor').setValue(cnw.down('htmleditor').getValue() + "<hr noshade>" + formattedCoords.x + " - " + formattedCoords.y );
		    						Ext.getCmp(thisid + '-btnOkInsNota').setDisabled(false);
		    					}
		    				});
		    			}		    			
		    		}
		    	}
			}],
			buttons: [
		          { text: ToloI18n.getMsg("ToloMapAPIExt.createNoteWidget.btnOK"),
		        	id: thisid + '-btnOkInsNota',
		        	disabled: true,
		        	listeners: {
		        		click: function() {
		        			var text = cnw.down('htmleditor').getValue();
			        		if(mode=='update'){			   
			        			this.viewer.pluginUpdatePopup(config.popupId,text);
			        			cnw.close();
			        		} else {
								//var viewPos = this.getViewerPosition(); 
								//var coordinate = this.viewer.pluginGetCoordinateFromPixel({x:(config.x-viewPos.x),y:(config.y-viewPos.y)}); 
								this.viewer.pluginAddPopup(coordinate.x,coordinate.y,text,false,true);
								cnw.close();
			        		}
			        	},
			        	scope: this
			         }
		          },
		          {  
		          	 text: ToloI18n.getMsg("ToloMapAPIExt.createNoteWidget.btnAnnulla"),
		        	 handler: function () { this.up('.window').close(); }
		          }
		    ],
		    listeners : {
	        	show : {
	        		fn :  function(){		   		
	        			Ext.defer(function(){
	        				cnw.down('htmleditor').focus();
	        			},200);
	        			
	        		}
	        	}
	        }
		});
		
		return cnw;
	},
	
	releaseLocationEnable: function(posX,posY,crs){	
		var f = function(){
			var viewPos = this.getViewerPosition(); 
			var coordinate = this.viewer.pluginGetCoordinateFromPixel({x:(posX-viewPos.x),y:(posY-viewPos.y)}); 
			this.releaseCoordinate(coordinate.x,coordinate.y,crs); 
		}
		if(crs == this.getProjectionCode()){
			f.call(this);
		}else{
			this.lazyLoadScript(
				'proj4js',
				f,
				function(){Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.releaseLocationEnable.title"), ToloI18n.getMsg("ToloButtonPanelExt.mnuGuida.text", {ProjectionCode: this.getProjectionCode()}),function(){this.contextMenu.setCrsSelected(this.getProjectionCode());f.call(this)},this);},
				this);
		}
	},
	
	releaseStreetviewEnable: function(posX,posY){
		
		var crs = "EPSG:4326";
		
		var f = function(){
			var viewPos = this.getViewerPosition(); 
			var coordinate = this.viewer.pluginGetCoordinateFromPixel({x:(posX-viewPos.x),y:(posY-viewPos.y)});
			// Messa solamente per assicurarmi che la proiezione attuale sia già stata caricata
			var currProj = new TolomeoExt.Projection(this.getProjectionCode());	
			
			if(!this.svWin){
			
				// da mettere afterrender altrimenti api non esiste
				this.svPanel = new TolomeoExt.ToloStreetviewViewerPanel({
					viewer: this.viewer
				});
	
				this.svWin = new Ext.Window({
					cls: 'clsStreetViewWindow',
					height: 400,
					width: 500,
					collapsible: true,
					maximizable: true,
					layout: 'fit',
					items: [this.svPanel],
					closable: true,
					closeAction: 'hide',
					cls: 'clearCSS',
					listeners: {					
						hide: {
							fn: function () {
								this.viewer.pluginStreetViewDeactivate();
							},
						  	scope: this
						}
					}
				});
				
				this.svWin.on('afterlayout',
					function(){
						this.setStreetviewPosition(coordinate.x,coordinate.y,crs);
					},this,{single: true});			
					
			} else {
				this.setStreetviewPosition(coordinate.x,coordinate.y,crs);
			}
			
			this.svWin.show();		
			this.svWin.setPosition(this.viewer.getPosition());
			this.viewer.pluginStreetViewActivate();
		}
		
		if(crs == this.getProjectionCode()){
			f.call(this);
		}else{
			this.lazyLoadScript(
				'proj4js',
				f,
				function(){Ext.Msg.alert(ToloI18n.getMsg("ToloMapAPIExt.releaseStreetviewEnable.title"), ToloI18n.getMsg("ToloMapAPIExt.releaseStreetviewEnable.msg", {ProjectionCode:this.getProjectionCode()}));},
				this);
		}
			
	},
	
	/**
	 * Method: setStreetviewPosition
	 * rilascia un popup sul viewer con le coordinate nel sistema di riferimento scelto
	 *
	 * Parameters:
	 * mousexy - {Point} posizione del mouse
     * coordsxy - {Point} posizione nel sistema di riferimento base
	 * crs - {String} Codice EPSG del sistema in cui si vogliono le coordinate (Es. EPSG:26591)
	 */
	setStreetviewPosition: function(coordX,coordY,crs){				

		var currProj = new TolomeoExt.Projection(this.getProjectionCode());	
		var currPoint = new Point(coordX,coordY);
		var currReproj = currProj;

		if(crs!= this.getProjectionCode() && TolomeoExt.lazyLoad.checkLoad('proj4js')){
			currReproj = new TolomeoExt.Projection(crs);
			currPoint = TolomeoExt.Projection.transform(currPoint.clone(),currProj,currReproj);
		}
		
		this.svPanel.setViewPosition(currPoint.x,currPoint.y);
			
	},	
	
	/**
	 * Method: releaseCoordinate
	 * rilascia un popup sul viewer con le coordinate nel sistema di riferimento scelto
	 *
	 * Parameters:
	 * coordX - Coordinata x nel sistema di riferimento indicato
     * coordY - Coordinata y nel sistema di riferimento indicato
	 * srid - {String} Codice EPSG del sistema in cui si vogliono le coordinate (Es. EPSG:26591)
	 */
	releaseCoordinate: function(coordX,coordY,srid){				
		
		var crsCode = srid?srid:this.getProjectionCode();
		var currCrsCode = this.getProjectionCode();
		var currProj = new TolomeoExt.Projection(currCrsCode);
		var reprojSrs = this.projectionCrs;		
		var currPoint = new Point(coordX,coordY);
		var currReproj = currProj;

		if(crsCode!= this.getProjectionCode() && TolomeoExt.lazyLoad.checkLoad('proj4js')){
			currReproj = new TolomeoExt.Projection(crsCode);
			currPoint = TolomeoExt.Projection.transform(currPoint.clone(),currProj,currReproj);
			currCrsCode = crsCode;
		}
		
		var currDescr = currReproj.getTitle();
		if(reprojSrs[currCrsCode]){ 
			if (reprojSrs[currCrsCode].precision){
				currPoint.round(reprojSrs[currCrsCode].precision);
			}
			if(reprojSrs[currCrsCode].description){
				currDescr = reprojSrs[currCrsCode].description ;
			}
		}
		var units = currReproj.getUnits()? " [" + currReproj.getUnits() + "]" : "";

		var htmlText = "<div style='font: 11px tahoma,arial,helvetica,sans-serif;'><b><u>" + currCrsCode + "</u></b><br><b>" + currDescr + units + "</b><br>" + currPoint.toString()+"</div>";
		
		this.addPopup(coordX,coordY,htmlText,false,false);
			
	},	
	
	/**
	 * Method: getViewPosition
	 * Restituisce un oggetto con le proprieta x e y che rappresentano la posizione del viewer	
	 */
	getViewerPosition: function(){
		if (this.viewer) {
			var viewerPos = this.viewer.getPosition();
			return {x:viewerPos[0],y:viewerPos[1]};
		}
		return {x:0,y:0};
	},
	
	/**
	 * Method: getProjectionCode
	 * @return {String} restituisce il codice EPSG del sistema di riferimento
	 */
	getProjectionCode: function(){
		return this.viewer.pluginGetProjectionCode();
	},
	
	/**
	 * Method: reprojectToCurrentCrs
	 * Metodo per la riproiezione di un punto con un certo sistemm di riferimento nel sistema di riferimento attuale
	 *
	 * Parameters:
	 * point - {Point} punto con le coordinate x e y
     * sourceSrid - {String} Codice EPSG del sistema in cui si vogliono le coordinate (Es. EPSG:26591)
	 */
	reprojectToCurrentCrs: function(point,sourceSrid){
		var crsCode = sourceSrid?sourceSrid:this.getProjectionCode();
		var currCrsCode = this.getProjectionCode();
		var currProj = new TolomeoExt.Projection(currCrsCode);	
		var currPoint = new Point(point.x,point.y);
		var currReproj = currProj;

		if(crsCode!= currCrsCode && TolomeoExt.lazyLoad.checkLoad('proj4js')){
			currReproj = new TolomeoExt.Projection(crsCode);
			currPoint = TolomeoExt.Projection.transform(currPoint.clone(),currReproj,currProj);			
		}
		
		return currPoint;		
	},

	/**
	 * Method: lazyLoadScript
	 * Metodo per i caricamento onDemand degli script. Durante il caricamento mette il sistema onBusy
	 *
	 * Parameters:
	 * jsKeys - {String/Array} Chiave o array di chiavi definite nel file toloLazyLoad per il caricamento in sequenza degli script correlati
     * onLoad - {Function} Funzione da chiamare quando lo script è caricato
	 * onFail - {Function} Funzione da chiamare quando non si riesce a recuperare lo script
	 * scope - {Object} Ambito della chiamata delle funzioni	 
	 */
	lazyLoadScript: function (jsKeys,onLoad,onFail,scope){
		
		var jsKeysArr = [];
		
		if(jsKeys instanceof Array){
			jsKeysArr = jsKeys;
		}else{
			jsKeysArr.push(jsKeys);	
		}
										
		if(jsKeysArr.length == 0) return;
		
		var me = this;
		
		if(jsKeysArr.length > 1) {
	        var first = jsKeysArr.shift();
	        me.lazyLoadScript(first,
	                      function() { me.lazyLoadScript(jsKeysArr, onLoad, onFail, scope); },
	                      onFail,
	                      scope);
	        return;
	    }
	    
	    var jsKey = jsKeysArr[0];
		
		if(TolomeoExt.lazyLoad.checkLoad(jsKey)){
			if(onLoad){
				onLoad.call(scope);
			}
		}else{
			this.onBusy(true);
			var me = this;
			TolomeoExt.lazyLoad.get(
				jsKey,
				function(){
					this.onBusy(true);
					if(onLoad){
						onLoad.call(scope);
					}
				},
				function(){
					this.onBusy(true);
					if(onFail){
						onFail.call(scope);
					}
				}
				,this);
		}
	},
	// AutoIdentify - FINE
	
	generateCustomInfo : function(helpInfo){				
		
		var infoItems = [];
		
		for(var i = 0; i < helpInfo.infoList.length; i++){
			
			var info = helpInfo.infoList[i];
			var infoItem;
			
			if(info.framed){			
				infoItem = {
					title: info.title,    				
    				autoScroll: false,    				
    				items: [{
						xtype : 'box',												
						autoEl : {
							tag : 'iframe',
							style : 'border-width: 0px',
							src : (info.url ? info.url : 'about:blank')
						}
					}]
					
					//oppure
					//html = '<iframe width="100%" height="100%" frameborder="0" src="' + (info.url ? info.url : 'about:blank') + '"></iframe>';
				}	
			} else {								
				infoItem = {
					title: info.title,
					loader: {
						url: (info.url ? info.url : 'about:blank'),
						autoLoad: true
					}					 	
				}
			}
			
			infoItems.push(infoItem);
		}
		
		this.customInfoWin = new Ext.Window({
			
			title: helpInfo.mainTitle,
			bodyStyle: 'padding: 0px',
			cls: 'clearCSS',
			width: 550,
			height: 450,			
			modal: true,
			closeAction: 'hide',
			constrain: true,
			layout: 'fit',
			buttons: [{
	        	text: ToloI18n.getMsg("ToloMapAPIExt.generateCustomInfo.btnChiudi"),
	       		listeners: {click: {fn: function() {
	       			this.customInfoWin.hide();
	       		},scope: this}}	       		
	        }],
	        items : {
	        	xtype : 'tabpanel',
	        	activeItem : 0,
	        	minTabWidth : 120,
    			tabWidth    : 135,
    			enableTabScroll: true,
    			border: false,
    			defaults: {
    				xtype: 'panel',
    				layout: 'fit',
    				autoScroll: true
    			},
    			items: infoItems
	        }
		});
				
	},
	
	showCustomInfo : function(helpInfo){
		if(!this.customInfoWin){
			this.generateCustomInfo(helpInfo)
		}
		this.customInfoWin.show();
	},
	
	showGuide: function(url){
		window.open(url);
	},
	
	showFaq: function(url){
		window.open(url);
	},
	
	regeneratePage: function(){
		window.location.reload(true);
	},
	
	mailToAdmin : function(to,subject){
		
		var url = 'mailto:' + to;
    	var subject = subject ? subject : "Tolomeo";
    	var body = "";        	
    	body += "User Agent : " + navigator.userAgent;
    	body += "\n\r";
    	body += "Url : " + location.href;
    	body += "\n\r";
    	body += "Versione Tolomeo : " + TolomeoExt.Vars.TOLOMEOVersion;
    	body += "\n\r";
    	body += "SIT core : " + this.paramsJS.sitCoreVersion;
    	body += "\n\r";
		body += "ExtJS : " + Ext.getVersion();
		body += "\n\r";
		body += "OpenLayers : " + OpenLayers.VERSION_NUMBER;
    	body += "\n\r";        	
    	body += "----------------------------------------";
    	body += "\n\r";        	
    	body += "Problema : ";
    	
    	url = Ext.String.urlAppend(url,"subject=" + subject);
    	url = Ext.String.urlAppend(url,"body=" + escape(body)); 
    	
    	window.open(url);
	},
	
	formatCoords : function (coords,crsCode){
		
		var formatter = Ext.util.Format;		
		var currCrsCode = !crsCode? this.getProjectionCode() : crsCode;
		var pattern = '0,000';
		var reprojSrs = this.projectionCrs;
		
		if(reprojSrs[currCrsCode]){ 
			if (reprojSrs[currCrsCode].precision){
				for(var i = 0; i < reprojSrs[currCrsCode].precision; i++){
					if(i == 0) pattern += '.';
					pattern += '0';
				}
			}
        } 
        
        return {
        	x: formatter.number(coords.x,pattern),        
        	y: formatter.number(coords.y,pattern)
        }
	},
	
	addLayerToQGis : function(catIdx, layIdx){				
		if (catIdx && layIdx){			
			
			var mappa = this.paramsJS.mappe.mappaList[0];
			var currCat = this.TOCPanel.tocInfo.getCategoryInfo(catIdx);
			var currLayer =  currCat.layers[layIdx];
			
			var currCatPreset = this.TOCPanel.tocInfo.getCategoryPresetInfo(catIdx);
			var currLayerPreset =  currCatPreset.layerList[layIdx];
			
			var server = this.paramsJS.getServer(currLayerPreset.serverID, mappa);
			
			if (server) {
				var url = server.url;	       
				var layer = currLayerPreset.name;
				var crs = this.paramsJS.mappe.SRID;
				var descr = currLayer.descr;
				//Ext.Msg.alert('Parametri','descrizione = ' + descr + '<br>url = ' + url + '<br>layer = ' + layer + '<br>crs = ' + crs);
	       		JQ.AddWMSLayer(descr,url,layer,'image/png',crs);	       		
			}	
							
		}
			
	},
	
	/**
	 * Method: addRouting
	 * Sulla mappa e' possibile evidenziare dei tracciati, nel caso in cui si richieda il calcolo di un percorso.
	 * Questa funzione consente di evidenziare il tracciato, di aggiornare di conseguenza la mappa e di fare le altre azioni necessarie.
	 * L'attuale implementazione prevede che un solo oggetto possa essere evidenziato, quindi ogni nuovo va a sostituirsi a quello eventualmente presente.
	 *
	 * Parameters:
	 * geoms - {JSGeometryArray o JSGeometry} oggetto da evidenziare se passato un JSGeometryArray viene utilizzato il primo.
	 * bMulti - {boolean} se non definito o false non è consentita la presenza di più di un oggetto, se True è consentita.
	 */
	addRouting: function(routeResponse, bMulti)
	{
		if (!bMulti) this.routingCorrente.clear();
		
		this.routingCorrente.add(routeResponse.geometry);
		
		if (this.viewer != null) {
			this.viewer.pluginAddRouting(routeResponse, bMulti);
			//this.viewer.pluginAddRouting(this.routingCorrente, bMulti);
			
			// Il loop parte volutamente da 1 per escludere la prima istruzione, il cui punto di partenza coincide con lo start point
			/*for (var i = 1; i < routeResponse.instructions.length; i++) {
				this.viewer.pluginAddInstruction(routeResponse.instructions[i].geometry, { tooltip: routeResponse.instructions[i].textInstruction });
			}*/
		}
	},
	
	/**
	 * Method: clearRouting
	 * Svuota il tracciato corrente, 
	 * deselezionando gli oggetti dalla mappa ed effettuando il resto delle operazioni necessarie (come l'aggiornamento delle operazioni di editing, interrogazione etc. possibili)
	 *
	 * Parameters:
	 * bRedraw - {boolean} Se non definito o false non è consentita la presenza di più di un oggetto, se True è consentita.
	 */
	clearRouting: function (bRedraw) {
		this.routingCorrente.clear();
		if (this.viewer.pluginClearRouting(bRedraw)) this.viewer.pluginRefreshMap();
	},
	
	/**
	 * Method: zoomToRouting
	 * Esegue lo zoom al tracciato corente.
	 * 
	 * Parameters:
	 * zoom - {Number} il valore di zoom.
	 * buffer - {Integer} buffer, se valorizzato viene fatto lo zoom aggiungendo il buffer passato
	 */
	zoomToRouting: function(zoom, buffer) {
		this.viewer.pluginZoomToRouting(zoom, buffer || 100);
	},
	
	/**
	 * Method: centerOnRouting
	 * Riposiziona la mappa mettendo al centro il tracciato corrente e mantenendo la scala attuale.
	 */
	centerOnRouting: function(){
		var zoom = this.viewer.pluginGetCurrentZoom();
		this.zoomToRouting(zoom);
	},
	
	/**
	 * Method: addStartRoutingMarker
	 * Aggiunge il marker di inizio tracciato per il routing
	 *
	 * Parameters:
	 * coordX - {} longitudine
	 * coordY - {} latitudine
	 * crsCode - {} sistema di riferimento
	 */
	addStartRoutingMarker: function(coordX, coordY, crsCode) {
		var currPoint = new Point(coordX,coordY);
		
		if (crsCode && this.projectionCrs[crsCode]) {
			var currSrsCode = this.getProjectionCode();
			if (currSrsCode != crsCode) {
				var currProj = new TolomeoExt.Projection(currSrsCode);
				var sourceProj = new TolomeoExt.Projection(crsCode);		
				currPoint = TolomeoExt.Projection.transform(currPoint,sourceProj,currProj);
			}
		}
		
		this.viewer.pluginAddStartRoutingMarker(currPoint.x, currPoint.y); 
	},
	
	/**
	 * Method: addEndRoutingMarker
	 * Aggiunge il marker di inizio tracciato per il routing
	 *
	 * Parameters:
	 * coordX - {} longitudine
	 * coordY - {} latitudine
	 * crsCode - {} sistema di riferimento
	*/
	addEndRoutingMarker: function(coordX, coordY, crsCode) {
		var currPoint = new Point(coordX,coordY);
		
		if (crsCode && this.projectionCrs[crsCode]) {
			var currSrsCode = this.getProjectionCode();
			if (currSrsCode != crsCode) {
				var currProj = new TolomeoExt.Projection(currSrsCode);
				var sourceProj = new TolomeoExt.Projection(crsCode);		
				currPoint = TolomeoExt.Projection.transform(currPoint,sourceProj,currProj);
			}
		}
		
		this.viewer.pluginAddEndRoutingMarker(currPoint.x, currPoint.y); 
	},
	
	/**
	 * Method: addViaRoutingMarker
	 * Aggiunge il marker di inizio tracciato per il routing
	 *
	 * Parameters:
	 * viaId - {} identificativo del punto intermedio
	 * coordX - {} longitudine
	 * coordY - {} latitudine
	 * crsCode - {} sistema di riferimento
	 */
	addViaRoutingMarker: function(viaId, coordX, coordY, crsCode) {
		var currPoint = new Point(coordX,coordY);
		
		if (crsCode && this.projectionCrs[crsCode]) {
			var currSrsCode = this.getProjectionCode();
			if (currSrsCode != crsCode) {
				var currProj = new TolomeoExt.Projection(currSrsCode);
				var sourceProj = new TolomeoExt.Projection(crsCode);		
				currPoint = TolomeoExt.Projection.transform(currPoint,sourceProj,currProj);
			}
		}
		
		this.viewer.pluginAddViaRoutingMarker(viaId, currPoint.x, currPoint.y); 
	},
	
	/**
	 * Method: setViaRoutingMarkers
	 * Imposta tutti i marker di tappa intermedia per il routing
	 *
	 * Parameters:
	 * viaPoints - {Array[ToloOLSVertex]} vertici intermedi
	 * crsCode - {} sistema di riferimento
	*/
	setViaRoutingMarkers: function(viaPoints, crsCode) {
		this.viewer.pluginClearViaRoutingMarkers();
		
		for (var i = 0; i < viaPoints.length; i++) {
			var	point = viaPoints[i].point;
			
			this.addViaRoutingMarker(viaPoints[i].id, point.x, point.y, crsCode);
		}
	},
	
	/**
	 * Method: setViaRoutingMarkers
	 * Restituisce il nome corretto aggiungendo il suffisso se presente
	 *
	 * Parameters:
	 * baseName - {String} nome base del parametro 
	*/
	_getPermalinkParameterName: function(baseName){
		return this.permalinkParameterSuffix ? (baseName + this.permalinkParameterSuffix) : baseName;			
		
	},
	
	
	/* Servizi di mapping esterni */
	/**
	 * @method scaleToZoomLevel
	 * 
	 * Converte una scala (scaledenom) in zoomLevel
	 * 
	 * @pparam {Number} scale scala
	 * @return {Number} zoomLevel corrispondente
	 */
	scaleToZoomLevel: function(scale) {
		
		if (scale>this.zoomLevelConvTable[0].scaledenom) {
			return 0;
		}
		
		for (var i=0; i<this.zoomLevelConvTable.length; i++) {
			 if (this.zoomLevelConvTable[i].scaledenom < scale) {
				 var distUp   = this.zoomLevelConvTable[i-1].scaledenom - scale;
				 var distDown = scale - this.zoomLevelConvTable[i].scaledenom;
				 if (distUp < distDown) {
					 return i-1;
				 } else {
					 return i;
				 }
			 }
		}
		return this.zoomLevelConvTable.length -1;
		
	},
	
	/**
	 * @method apriMappaHere
	 * 
	 * Apre una mappa del servizio Here nella stessa posizione attuale e con il livello di zoom, tra quelli supportati dal servizio, 
	 * più vicino possibile a quello attuale.
	 * 
	 * @pparam {Number} tipo tipo di mappa da aprire
	 * 
	 */
	apriMappaHere: function(tipo) {
		
		var crs = "EPSG:4326";
		var urlTmpl = "https://www.here.com?map=%lat%,%lon%,%zoomlev%,%tipo%";
		urlTmpl = urlTmpl.replace("%tipo%", tipo);
		
		var url = this.urlMappaDaTemplate(urlTmpl, crs);
		
		window.open(url, "_blank");
		
	},
	
	/**
	 * @method apriMappaBing
	 * 
	 * Apre una mappa del servizio Bing nella stessa posizione attuale e con il livello di zoom, tra quelli supportati dal servizio, 
	 * più vicino possibile a quello attuale.
	 * 
	 * @pparam {Number} tipo tipo di mappa da aprire
	 * @pparam {Number} dir direzione per mappe oblique
	 * 
	 */
	apriMappaBing: function(tipo, dir) {
		
		var crs = "EPSG:4326";
		var urlTmpl = "http://www.bing.com/maps/default.aspx?cp=%lat%~%lon%&lvl=%zoomlev%&style=%tipo%&dir=%dir%";
		urlTmpl = urlTmpl.replace("%tipo%", tipo);
		urlTmpl = urlTmpl.replace("%dir%",  dir);
		
		var url = this.urlMappaDaTemplate(urlTmpl, crs);
		
		window.open(url, "_blank");
		
		//https://www.here.com/?map=43.88491,11.09798,20,satellite
		//https://www.here.com/?map=43.88491,11.09798,20,normal
		//https://www.here.com/?map=43.88491,11.09798,20,terrain
		
	},
	

	/**
	 * @method apriMappaOSM
	 * 
	 * Apre una mappa del servizio Openstreetmap nella stessa posizione attuale e con il livello di zoom, tra quelli supportati dal servizio, 
	 * più vicino possibile a quello attuale.
	 * 
	 */
	apriMappaOSM: function() {
		
		var crs = "EPSG:4326";
		var urlTmpl = "http://www.openstreetmap.org/#map=%zoomlev%/%lat%/%lon%";
		
		var url = this.urlMappaDaTemplate(urlTmpl, crs);
		
		window.open(url, "_blank");
		
		//https://www.here.com/?map=43.88491,11.09798,20,satellite
		//https://www.here.com/?map=43.88491,11.09798,20,normal
		//https://www.here.com/?map=43.88491,11.09798,20,terrain
		
	},
	
	/**
	 * @method apriMappaGoogle
	 * 
	 * Apre una mappa del servizio Google nella stessa posizione attuale e con il livello di zoom, tra quelli supportati dal servizio, 
	 * più vicino possibile a quello attuale.
	 * 
	 * @pparam {Number} tipo tipo di mappa da aprire
	 * @pparam {boolean} bclassic indica se aprire la viusualizzazione "classic"
	 * 
	 */
	apriMappaGoogle: function(tipo, bclassic) {
		
		var crs = "EPSG:4326";
		//
		var urlTmpl = "";
		if (bclassic) {
			urlTmpl = "https://www.google.it/lochp?ll=%lat%,%lon%&z=%zoomlev%&t=%tipo%";
		} else {
			urlTmpl = "https://www.google.it/maps?ll=%lat%,%lon%&z=%zoomlev%&t=%tipo%";
			
		/*	var urlTmpl = "https://www.google.it/maps/@%lat%,%lon%,%zoomlev%z";
			urlTmpl += ',20y,%dir%h,%incl%t/data=!3m1!1e3';
			urlTmpl = urlTmpl.replace("%dir%",  dir);
			urlTmpl = urlTmpl.replace("%incl%",  incl);*/
		}
		urlTmpl = urlTmpl.replace("%tipo%",  tipo);
		var url = this.urlMappaDaTemplate(urlTmpl, crs);

		window.open(url, "_blank");
		
		//https://www.here.com/?map=43.88491,11.09798,20,satellite
		//https://www.here.com/?map=43.88491,11.09798,20,normal
		//https://www.here.com/?map=43.88491,11.09798,20,terrain
		
	},
	
	
	/**
	 * @method urlMappaDaTemplate
	 * 
	 * Sostituisce nel template della url i valori relativi alla paosiozne ed al livello di zoom correnti
	 * 
	 * @pparam {Number} urlTmpl tipo di mappa da aprire
	 * @pparam {boolean} crs indica se aprire la viusualizzazione "classic"
	 * 
	 */
	urlMappaDaTemplate: function(urlTmpl, crs) {
	
		//var crs = "EPSG:4326";
		//var urlTmpl = "https://www.here.com/?map=%lat%,%lon%,%zoomlev%,%tipo%";
		//var url = urlTmpl.replace("%tipo%", tipo);
		
		// Sostituzione zoomLevel
		var zoomLev = this.scaleToZoomLevel(this.viewer.pluginGetCurrentZoom());
		var url = urlTmpl.replace("%zoomlev%", zoomLev);
		
		// Sostituzione longitudine e latitudine centro mappa
		var coordX = this.viewer.pluginGetCurrentX();
		var coordY = this.viewer.pluginGetCurrentY();
		var currPoint = new Point(coordX,coordY);
		var currProj = new TolomeoExt.Projection(this.getProjectionCode());	
		
		if(crs!= this.getProjectionCode() && TolomeoExt.lazyLoad.checkLoad('proj4js')){
			currReproj = new TolomeoExt.Projection(crs);
			currPoint = TolomeoExt.Projection.transform(currPoint.clone(),currProj,currReproj);
		}
		
		url = url.replace("%lon%", currPoint.x);
		url = url.replace("%lat%", currPoint.y);
		
		return url;
	},
	
	/**
	 * @method getSelezioneCorrente
	 * 
	 * Recupera l'oggetto selezione corrente
	 * 
	 * @return {JSGeometryArray} Selezione corrente
	 * 
	 */
	getSelezioneCorrente: function() {
		return this.selezioneCorrente;
	},
	
	/**
	 * @method getEvidenziazioneCorrente
	 * 
	 * Recupera l'oggetto evidenziazione corrente
	 * 
	 * @return {JSGeometryArray} Evidenziazione corrente
	 * 
	 */
	getEvidenziazioneCorrente: function() {
		return this.evidenziazioneCorrente;
	}
	
	
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * @class TolomeoExt.ToloPointFromRefPanelExt
 * @extends Ext.Window
 * 
 * 
 */
Ext.define('TolomeoExt.ToloPointFromRefPanelExt', {
	
	extend: 'Ext.Window',
	
	/**
	 * @property {Boolean} [closable=false]
	 * 
	 * 
	 */
	closable: false,
	
	/**
	 * @property {Boolean} [bodyBorder=false]
	 * 
	 * 
	 */
	bodyBorder: false,
	
	/**
	 * @property {Boolean} [border=false]
	 * 
	 * 
	 */
	border: false,
	
	/**
	 * @property {Boolean} [frame=true]
	 * 
	 * 
	 */
	frame: true,
	
	/**
	 * @property {Boolean} [resizable=false]
	 * 
	 * 
	 */
	resizable: false,
	
	/**
	 * @property {Boolean} [constraint=true]
	 * 
	 * 
	 */
	constraint: true,
	
	/*
	 * @property {Boolean} [monitorResize=true]
	 * 
	 * 
	 */
	//monitorResize: true,
	
	/**
	 * @property {Number} [width=280]
	 * 
	 * 
	 */
	width: 280,
	
	/**
	 * @property {Boolean} [collapsible=true]
	 * 
	 * 
	 */
	collapsible: true,
	
	/*
	 * @property {String} [bodyStyle='background-color:white;']
	 * 
	 * 
	 */
	//bodyStyle: 'background-color:white;',
		
	/**
	 * @method initComponent
	 * 
	 * 
	 */
	initComponent: function() {
		
		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
		this.callParent(arguments);
		this.title = ToloI18n.getMsg("ToloPointFromRefPanelExt.title");
		this.addEvents('pressSetDistance');
		this.notifyDistanceSetting = function(){		
			var distance = this.getCmp(this.getId()+"-distanceFromRef").getValue(); //this.pfr.find("name","distanceFromRef")[0].getValue();
		    distance = (""+distance).replace(",",".");			
		    if (!distance || isNaN(distance)) {
				Ext.Msg.alert(ToloI18n.getMsg("ToloPointFromRefPanelExt.alert.title"),ToloI18n.getMsg("ToloPointFromRefPanelExt.alert.msg"));
				return;
			}				    
			this.fireEvent('pressSetDistance', distance);
		}
		
		this.selectAndFocus = function(){			
			this.getCmp(this.getId()+"-distanceFromRef").focus().selectText();    //this.pfr.find("name","distanceFromRef")[0].focus().selectText();
		}
		this.helpWin;
		this.pfr = new Ext.FormPanel({
	        labelWidth: 110, // label settings here cascade unless overridden
	        url:'#',
	        frame:true,
	        title: '',
	      //  bodyStyle:'padding:5px 5px 0',
	        
	      	bodyStyle: 'padding:2px;',
	       // width: 350,
	      	
	        items: [{
	            xtype: 'numberfield',	            	
                fieldLabel: ToloI18n.getMsg("ToloPointFromRefPanelExt.fldDist"),
                id: this.getId()+'-distanceFromRef',
	            name: 'distanceFromRef',
	            allowBlank: true,
	            allowDecimals: true,
	            allowNegative: false
		            
	        }
	        /*
	        ,{
	        	xtype: 'displayfield', 
	        	fieldLabel: 'Distanza attuale', 
	        	value: 'prova',
	        	name: 'currenteDistanceFromRef',
	        	id: 'currentDistanceFromRef'
	        }
	        */],
	        buttons: [{
	            text: 'Imposta',
	            type: 'submit',
	            handler: this.notifyDistanceSetting,
				scope: this
	        },{
	        	minWidth: 20,
	            text: ' ? ',
	            type: 'submit',
	            handler:  function(){
		        // create the window on the first click and reuse on subsequent clicks
		        if(!this.helpWin){
		            this.helpWin = new Ext.Window({
		                layout:'fit',
		                width:500,
		                autoHeight: true,
		                title: ToloI18n.getMsg("ToloPointFromRefPanelExt.helpWin.title"),
		                //plain: true,
		                		
		                items: new Ext.Panel({
		                    deferredRender:false,
		                    border:false,
		                    html:ToloI18n.getMsg("ToloPointFromRefPanelExt.helpWin.html")		                    	 	                    	
		                }),		
		                buttons: [{
		                    text: ToloI18n.getMsg("ToloPointFromRefPanelExt.helpWin.btnChiudi"),
		                    handler: function(){
		                        this.helpWin.hide();
		                    },
		                    scope: this
		                }]
		            });
		        }
		        this.helpWin.show(this);
		    },
				scope: this
	        }],
	        
	        keys: [{
	        	key: [Ext.EventObject.ENTER], 
             	handler: this.notifyDistanceSetting,
                scope: this
             }]  	        
	    });
			    	    
		this.add(this.pfr);
		this.doLayout();
	},
	   
	/**
	 * @method show
	 * Mostra il pannello CAD e lo rimette nella posizione 0,0
	 * 
	 */ 
    show: function () {
		this.setPosition(0,0);
		this.callParent(arguments);
		Ext.Function.defer(this.selectAndFocus,200,this);
		//this.displayDistance(0);
	},

	/**
	 * @method hide
	 * Nasconde il pannello CAD e e resetta il comando
	 * 
	 */ 
	hide: function () {
		this.getCmp(this.getId()+"-distanceFromRef").setValue(""); 
		//this.pfr.find("name","distanceFromRef")[0].setValue("");
		this.callParent(arguments);
		//this.displayDistance(0);
	},
	
/*
	displayDistance: function (distance) {
		Ext.getCmp('currentDistanceFromRef').setValue(Math.round(distance*100)/100);
		this.setVisible(true);
	},	
*/	

	/**
	 * @method bindToViewerPanel
	 * 
	 * 
	 * @param {Object} viewer
	 * 
	 * 
	 */ 
	bindToViewerPanel: function(viewer) {
		if (viewer!=null) {
			// Registrazione in viewerPanel
			viewer.on('onDrawFirstPointFromRef', this.show, this );
			viewer.on('onDigitizePointFromRefEnd', this.hide, this);
			//viewer.on('onDigitizePointFromRefCallback', function(){this.clear();this.hide();}, this);
			//viewer.on('onDrawDistanceFromRefChange', this.displayDistance, this);
		}
	}
			
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * 
 * @class TolomeoExt.ToloCADPanelExt
 * @extends Ext.Window
 * 
 * 
 */
Ext.define('TolomeoExt.ToloCADPanelExt', {
	extend: 'Ext.Window',

	/** 
	 * @property {Boolean} [closable=false]
	 * 
	 * 
	 */
	closable: false,
	
	/** 
	 * @property {Boolean} [bodyBorder=false]
	 * 
	 * 
	 */
	bodyBorder: false,
	
	/** 
	 * @property {Boolean} [border=false]
	 * 
	 * 
	 */
	border: false,
	
	/** 
	 * @property {Boolean} [frame=true]
	 * 
	 * 
	 */
	frame: true,
	
	/** 
	 * @property {Boolean} [resizable=false]
	 * 
	 * 
	 */
	resizable: false,
	
	/** 
	 * @property {Boolean} [constraint=true]
	 * 
	 * 
	 */
	constraint: true,
	
	/*
	 * @property {Boolean} [monitorResize=true]
	 * 
	 * 
	 */
	//monitorResize: true,
	
	/** 
	 * @property {Number} [width=380]
	 * 
	 * 
	 */
	width: 390,
	
	/**
	 * @property {Boolean} [collapsible=true]
	 * 
	 */
	collapsible: true,	
		
	/**
	 * @method initComponent
	 * Create a new TolomeoExt.ToloCADPanelExt
	 * 
	 */	
	initComponent: function() {
		
		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		this.callParent(arguments);
			
		this.title = ToloI18n.getMsg("ToloCADPanelExt.title");
		var me = this;
		// 
		this.addEvents('pressSetCommand');
		this.addEvents('changeAngleSetting');	
		this.addEvents('pressDeleteFirstLine');
		
		/*
		 * @method notifyCommandSetting
		 * Notifica l'impostazione di un comando CAD
		 * 
		 */ 
		this.notifyCommandSetting = function(){		
			var commandCmp = Ext.getCmp(this.getId()+'-command');
			//var commandCmp = this.cad.find("name","command")[0];
			this.fireEvent('pressSetCommand', "@" + commandCmp.getValue());
			commandCmp.setValue("");
			this.selectAndFocus();
		}
		
		/*
		 * @method notifyAngleSetting
		 * Notifica l'impostazione dell'angolo
		 *
		 * @param {Object} radioGrp
		 * il gruppo di radio button.
		 * 
		 * @param {Object} radio
		 * il radio button.
		 * 
		 */ 
		this.notifyAngleSetting = function(radioGrp,radio){						
			this.fireEvent('changeAngleSetting', radio.getRawValue());	
			this.selectAndFocus();					
		}
		
		/*
		 * @method notifyDeleteFirstLine
		 * Notifica la richiesta di cancellazione della prima linea		
		 * 
		 */ 
		this.notifyDeleteFirstLine = function(){						
			this.fireEvent('pressDeleteFirstLine');
			this.selectAndFocus();						
		}
		
		/*
		 * @method selectAndFocus
		 * Imopsta il focus e seleziona il testo sul input text del comando.	
		 * 
		 */ 
		this.selectAndFocus = function(){
			Ext.getCmp(this.getId()+'-command').focus();
			//this.cad.find("name","command")[0].focus();//.selectText();
		}
		
		this.helpWin;		
		this.cad = Ext.create('Ext.FormPanel',{	        
	        url:'#',
	        frame:true,
	        title: '',
	        //  bodyStyle:'padding:5px 5px 0',	        
	      	//  bodyStyle: 'padding:2px;',
	        //  width: 350,
	      	defaults: {
	      		labelWidth: 120
	      	},	      	
		    items: [{		    	
            	anchor: '98%',
	            xtype: 'textfield',	
	            labelSeparator : ' ',            	
                fieldLabel: ToloI18n.getMsg("ToloCADPanelExt.fldProssimoPunto"),
                id: this.getId()+'-command',
	            name: 'command',
	            maskRe:/\d|<|,|\.|\-/,	
	            regex: /^((<(\-)?\d+(\.\d+)?)|(,(\-)?\d+(\.\d+)?)|((\-)?\d+(\.\d+)?((,|<)((\-)?\d+(\.\d+)?)?)?))$/,
	            allowBlank: true,
	            allowDecimals: true,
	            allowNegative: false,
	            listeners: {
	                specialkey: function(field, e){
	                    if (e.getKey() == e.ENTER) {
	                    	this.notifyCommandSetting();
	                    }
	                },
	                scope: this
	            }
		    },{
		    	xtype: 'radiogroup',
	            fieldLabel: ToloI18n.getMsg("ToloCADPanelExt.fldRelativoA"),
	            listeners: {change:{fn:this.notifyAngleSetting,scope:this}},
	            items: [{
	                checked: true,
	                boxLabel: ToloI18n.getMsg("ToloCADPanelExt.fldRelativoA.latoprec"),
	                name: 'relativeAngle',
	                inputValue: '1'
	            },{		                
	                boxLabel: ToloI18n.getMsg("ToloCADPanelExt.fldRelativoA.orizzontale"),
	                name: 'relativeAngle',
	                inputValue: '0'
	            }]		           
		    }],
	        buttons: [{
	            text: ToloI18n.getMsg("ToloCADPanelExt.btnImposta"),
	            type: 'submit',
	            handler: this.notifyCommandSetting,
				scope: this
	        },{
	            text: ToloI18n.getMsg("ToloCADPanelExt.btnCancellaPrimaLinea"),
	            type: 'submit',
	            handler: this.notifyDeleteFirstLine,
				scope: this
	        }],
	        
	        keys: [{
	        	key: [Ext.EventObject.ENTER], 
             	handler: this.notifyCommandSetting,
                scope: this
             }]  	        
	    });
			    	    
		this.add(this.cad);
		this.tools = [{
			id : 'help', // 6
			handler : function() {
			// create the window on the first click and reuse on subsequent clicks
		        if(!this.helpWin){
		            this.helpWin = Ext.create('Ext.Window',{
		                layout:'fit',
		                width:500,
		                height: 500,		                
		                title: ToloI18n.getMsg("ToloCADPanelExt.winHelp.title"),	
		                closeAction: 'hide',
		                		
		                items: Ext.create('Ext.Panel',{
		                    deferredRender:false,
		                    autoScroll: true,		                    		                    
		                    bodyStyle: 'padding:10px;font-size:12px;line-height:150%',
		    				frame: false,
		    				border: false,
		    				plain: true,
		                    html: ToloI18n.getMsg("ToloCADPanelExt.winHelp.html")
		                }),		
		                buttons: [{
		                    text: ToloI18n.getMsg("ToloCADPanelExt.winHelp.btnChiudi"),
		                    handler: function(){
		                        this.helpWin.hide();
		                    },
		                    scope: this
		                }]
		            });
		        }
		        this.helpWin.show(this);
}
}];
		this.doLayout();
	},
		
  /**
	 * @method show
	 * Mostra il pannello CAD e lo rimette nella posizione 0,0
	 * 
	 */ 
    show: function () {		
		this.setPosition(0,0);
		this.callParent(arguments);
		Ext.Function.defer(this.selectAndFocus,200,this);		
	},

	/**
	 * @method hide
	 * Nasconde il pannello CAD e e resetta il comando
	 * 
	 */ 
	hide: function () {	
		Ext.getCmp(this.getId()+'-command').setValue("");
		//this.cad.find("name","command")[0].setValue("");
		this.callParent(arguments);
	},

	
	/**
	 * @method bindToViewerPanel
	 * Imposta i gestori degli eventi di interesse lanciati dal viewer
	 *
	 * @param {Object} viewer
	 * visualizzatore della mappa
	 * 
	 */ 
	bindToViewerPanel: function(viewer) {
		if (viewer!=null) {
			// Registrazione in viewerPanel
			viewer.on('onDrawFirstPointByCAD', this.show, this );
			viewer.on('onDigitizePolygonByCADEnd',this.hide, this);
			viewer.on('onDigitizeLineByCADEnd',this.hide, this);
			viewer.on('onDigitizePointByCADEnd',this.hide, this);
		}
	}
			
});
 /* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * @class TolomeoExt.ToloGotoLocationWindowExt
 * @extends Ext.Window
 * 
 * 
 */
Ext.define('TolomeoExt.ToloGotoLocationWindowExt', {
		
	extend: 'Ext.Window',
	/** 
	 * @property {Boolean} [closable=false]
	 * 
	 * 
	 */
	closable: false,
	
	/** 
	 * @property {Boolean} [bodyBorder=false]
	 * 
	 * 
	 */
	bodyBorder: false,
	
	/** 
	 * @property {Boolean} [border=false]
	 * 
	 * 
	 */
	border: false,
	
	/** 
	 * @property {Boolean} [frame=true]
	 * 
	 * 
	 */
	frame: true,
	
	/** 
	 * @property {Boolean} [resizable=false]
	 * 
	 */
	resizable: false,
	
	/** 
	 * @property {Boolean} [constraint=true]
	 * 
	 */
	constraint: true,
	
	/*
	 * @property {Boolean} [monitorResize=true]
	 * 
	 * 
	 */
	//monitorResize: true,
	
	/** 
	 * @property {Number} [width=370]
	 * 
	 */
	width: 450,
	
	/**
	 * @property {Boolean} [collapsible=false]
	 * 
	 */
	collapsible: false,	
	
	/**
	 * @property {Object} [projectionCrs={}]
	 * 
	 */
	projectionCrs : {},
	
	/**
	 * @property {String} projectionCode
	 * 
	 */
	projectionCode: null,
	
	/*
	 * @property {String} [cls=clearCSS]
	 * 
	 */
	//cls: 'clearCSS',
		
	/**
	 * @method initComponent
	 * Create a new TolomeoExt.ToloGotoLocationWindowExt
	 * 
	 */	
	initComponent: function() {
		
		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
		this.callParent(arguments);
		this.title = ToloI18n.getMsg("ToloGotoLocationWindowExt.title");
		this.addEvents('gotoLocation');	
		
		var thisGotoLoc = this;
		
		/**
		 * @method splitValue
		 * Divide i valori su x ed y se entrambi sono stati messi divisi da virgola in x.
		 * 
		 */
		var splitValue = function(){	
		
			var coordXCmp = Ext.getCmp(thisGotoLoc.getId()+"-coordX");  	//this.find("name","coordX")[0];		
			if(coordXCmp.isValid()){
				var xValue = coordXCmp.getValue();			
				if(xValue.indexOf(",") != -1){
					var xy = xValue.split(/\s*,\s*/g);
					var coordYCmp =Ext.getCmp(thisGotoLoc.getId()+"-coordY"); // this.find("name","coordY")[0];
					coordXCmp.setValue(xy[0]);
					coordYCmp.setValue(xy[1]);
					//.replace(/^\s+|\s+$/gm,'');
				}
			}					
		}
		
		var invert = function(){
			
			splitValue.call(thisGotoLoc);
			
			var coordXCmp = Ext.getCmp(thisGotoLoc.getId()+"-coordX");
			var coordYCmp = Ext.getCmp(thisGotoLoc.getId()+"-coordY");			
			
			var xValue = coordXCmp.getValue();
			var yValue = coordYCmp.getValue();			

			coordXCmp.setValue(yValue);
			coordYCmp.setValue(xValue);			
		}
		
		var notifyGotoLocation = function(){
			
			splitValue.call(thisGotoLoc);
			
			var coordXCmp = Ext.getCmp(thisGotoLoc.getId()+"-coordX"); // this.goToLoc.find("name","coordX")[0];
			var coordYCmp = Ext.getCmp(thisGotoLoc.getId()+"-coordY"); //this.goToLoc.find("name","coordY")[0];		
			
			if(!coordXCmp.isValid()){
				Ext.Msg.alert(ToloI18n.getMsg("ToloGotoLocationWindowExt.error.XNonValido.title"),
						ToloI18n.getMsg("ToloGotoLocationWindowExt.error.XNonValido.msg"));
				return;
			}
			
			if(Ext.isEmpty(coordYCmp.getValue())){
				Ext.Msg.alert(ToloI18n.getMsg("ToloGotoLocationWindowExt.error.YAssente.title"),
						ToloI18n.getMsg("ToloGotoLocationWindowExt.error.YAssente.msg"));
				return;
			}
			
			if(!coordYCmp.isValid()){
				Ext.Msg.alert(ToloI18n.getMsg("ToloGotoLocationWindowExt.error.YNonValido.title"),
						ToloI18n.getMsg("ToloGotoLocationWindowExt.error.YNonValido.msg"));
				return;
			}
			
			var xValue = coordXCmp.getValue();
			var yValue = coordYCmp.getValue();			
			var crs = Ext.getCmp(thisGotoLoc.getId()+"-crs").getValue(); //this.goToLoc.find("name","crs")[0].getValue();
			
			this.fireEvent('gotoLocation', xValue, yValue, crs);
			coordXCmp.setValue("");
			coordYCmp.setValue("");
			this.hide();				
		}
						
		var myData = new Array();		      
		      
		for(var i in this.projectionCrs){         
			if(this.projectionCode && this.projectionCode == i) {
				myData.unshift([i,this.projectionCrs[i].description,ToloI18n.getMsg("ToloGotoLocationWindowExt.error.YNonValido.title")]); 
			} else {
				myData.push([i,this.projectionCrs[i].description,'']);            
			}
	    }
	    
	    if(!this.projectionCode) this.projectionCode = myData[0][0];
		  
	    var myStore = new Ext.data.ArrayStore({
	         fields: [
	           'code',
	           'description',
	           'extraInfo'
	         ],
	         data : myData
	    });
    
		this.goToLoc = Ext.create('Ext.form.Panel',{
	        labelWidth: 60, // label settings here cascade unless overridden
	     	labelAlign: 'left',	     	
	        url:'#',
	        frame:true,
	        title: '',
	        //defaultType: 'textfield',
		    items: [{            	
	            xtype: 'label',	
	            html: ToloI18n.getMsg("ToloGotoLocationWindowExt.html")
		    },{
		    	xtype: 'container',
		    	layout: 'hbox',
            	frame: false,	
            	//xtype: 'fieldset',
	            items:[{
	                flex: 1,
                    xtype: 'textfield',
		            padding: '5 5 5 0',
		            labelStyle: 'font-weight: bold;',
		            labelWidth: 60,
		            labelSeparator : ' ',            	
	                fieldLabel: 'X|Lon : ',
	                id: this.getId()+'-coordX',
		            name: 'coordX',
		            maskRe:/\d|,|\.|\-|\+/,	
		            regex: /^(\-|\+)?\d+(\.\d+)?\s*(,\s*(\-|\+)?\d+(\.\d+)?)?$/,
		            allowBlank: false,
		            allowDecimals: true,
		            allowNegative: true,
		            listeners: {blur:  {fn: splitValue, scope: thisGotoLoc}}
	            },{
	                flex: 1,
	                anchor:'95%',
                    xtype: 'textfield',
                    padding: '5 5 5 5',
                    labelStyle: 'font-weight: bold;',
                    labelWidth: 50,
		            labelSeparator : ' ',            	
	                fieldLabel: 'Y|Lat : ',
	                id: this.getId()+'-coordY',
		            name: 'coordY',
		            maskRe:/\d|\.|\-|\+/,	
		            regex: /^(\-|\+)?\d+(\.\d+)?$/,
		            allowBlank: true,
		            allowDecimals: true,
		            allowNegative: true,
		            anchor: '-5'
	           
	            }]
        	},
        	new Ext.form.ComboBox({
		    	labelStyle: 'font-weight: bold;',
		    	labelWidth: 60,
		    	id: this.getId()+'-crs',
		    	name: 'crs',
		        store: myStore,
		        //width: 270,
		        displayField:'code',
		        valueField: 'code',
		        fieldLabel:'CRS',        
		        hideTrigger:false,
				lazyInit: false,
				//typeAhead: true,
			    triggerAction: 'all',
			    lazyRender:true,
			    mode: 'local',
				forceSelection: true,
				autoSelect: true,
				listConfig: {
				    itemTpl: "<b><u>{code}</u></b> {extraInfo}<br>{description}"
				
				},
		        anchor: '-5',
		        value: this.projectionCode
		    })],
	        buttons: [{
	        	text: 'XY > YX',
	            type: 'button',
	            handler: invert,
				scope: this
	        },{
	            text: 'Vai',
	            type: 'submit',
	            handler: notifyGotoLocation,
				scope: this
	        }],
	        
	        keys: [{
	        	key: [Ext.EventObject.ENTER], 
             	handler: notifyGotoLocation,
                scope: this
             }]  	        
	    });
		    	    
		this.add(this.goToLoc);
		this.doLayout();
	},
	
	/**
	 * @method centerTo
	 * Centra rispetto all'elemnto passto (viewer ad esempio)
	 * o rispetto al container se non viene passato l'elemento.
	 * 
	 * @param {Object} el
	 * 
	 * 
	 */	
	centerTo: function(el){
		el = Ext.get(el);                
		if(!el || !el.dom){            
			el = this.container;        
		}				
		var xy = this.getEl().getAlignToXY(el, 'c-c');        
  		this.setPagePosition(xy[0], xy[1]);
	}
					
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloContextMenu
 * @extends Ext.Toolbar
 * 
 * 
 */
Ext.define('TolomeoExt.ToloContextMenu', {
	
	extend: 'Ext.menu.Menu',

	/**
	 * @type {TolomeoExt.ToloMapAPIExt}
	 * Settato automaticamente da bindToApi. 
	 * 
	 */
	api: null,
	
	/** 
	 * @property {Object} paramsJS
	 * 
	 * 
	 */
	paramsJS :null,

	/** 
	 * @property {String} TOLOMEOServer
	 * 
	 * 
	 */
	TOLOMEOServer: null,

	/** 
	 * @property {String} TOLOMEOContext
	 * 
	 * 
	 */
	TOLOMEOContext: null,	

	/** 
	 * @property {String} iconBasePath
	 * 
	 * 
	 */
	iconBasePath:null,
	
	/** 
	 * @property {String} [cls='clearCSS']
	 * 
	 * 
	 */
	cls: 'clearCSS',
	
	/** 
	 * @property {String} crsSelected
	 * 
	 * 
	 */
	crsSelected: null,
	
	/** 
	 * @property {String} projectionCrs
	 * 
	 * 
	 */
	projectionCrs: null,
	
	/**
	 * Metodo di calcolo routing con openls
	 */
	olsMethod: 'Fastest',
	
	/**
	 * @method initComponent
	 * Create a new button toolbar
	 * 
	 */
	initComponent: function() {

		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);

		this.callParent(arguments);
		var thisContextMenu = this;	
		
		if (this.iconBasePath==null) this.iconBasePath =  TolomeoExt.Vars.TOLOMEOServer + TolomeoExt.Vars.TOLOMEOStaticRoot + 'img/icone/16-default/';
		
		//var hide_context_menu = function () { thisContextMenu.hide() }; 
		
		//fixIE10
		//if (Ext.isIE10){this.setWidth(200);}
		
		// necessarie perchè altrimenti il menu non scompare dalla più dalla pagina
		//this.on('show', function () {Ext.getDoc().on('mouseup', hide_context_menu)},this);
		//this.on('hide', function () {Ext.getDoc().un('mouseup', hide_context_menu)},this);		

		this.xEvtPos = 0;
		this.yEvtPos = 0;
		
		// add custom events
		this.addEvents('onGotoLocClickFn');
		this.addEvents('onReleaseLocClickFn');
		this.addEvents('onReleaseStreetviewClickFn');
		this.addEvents('onNotaClickFn');
		
			
		var listenersClick  = {click:  {fn: this.btnClickHandler,  scope: thisContextMenu}};
			
		var notifyReleaseLoc = function(){
			thisContextMenu.fireEvent('onReleaseLocClickFn', thisContextMenu.getXEvtPos(), thisContextMenu.getYEvtPos(), thisContextMenu.crsSelected);
		}
		
		var notifyReleaseStreetview = function(){
			thisContextMenu.fireEvent('onReleaseStreetviewClickFn', thisContextMenu.getXEvtPos(), thisContextMenu.getYEvtPos());
		}

		var notifyNota = function(){
			thisContextMenu.fireEvent('onNotaClickFn', thisContextMenu.getXEvtPos(), thisContextMenu.getYEvtPos());
		}
		
		var onCrsCheck = function(item, checked){
        		thisContextMenu.crsSelected = item.crs;
        		notifyReleaseLoc(item,null);          	
    	}

    	this.add({
			text: ToloI18n.getMsg("ToloContextMenu.nota"),
			icon: this.iconBasePath + 'note.gif',
			handlerBaseName : 'Nota',
			listeners: {click: {fn: notifyNota,  scope: thisContextMenu}}
		});
		
		this.add("-");
		
		this.add({
			text: ToloI18n.getMsg("ToloContextMenu.vaiAPos"),
			icon: this.iconBasePath + 'target.gif',
			handlerBaseName : 'GotoLoc',
			listeners: listenersClick
		});
		
		if(this.projectionCrs){		
			
			var crsItem = [];
			for(var i in this.projectionCrs){
				crsItem.push({
                    text: this.projectionCrs[i].description,
                    id: 'chk_'+ i,
                    crs: i,
                    group: 'crs',                        
                    handlerBaseName : 'ReleaseLoc',
                    xtype: 'menucheckitem',
                    listeners: {click: {fn: onCrsCheck,  scope: thisContextMenu}}
                });
			}
			
			this.add({
				text: ToloI18n.getMsg("ToloContextMenu.rilasciaIndic"),
				id: this.getId() + 'releaseLoc',
				menu: {
					cls: 'clearCSS',
	                items: crsItem
	            },
				icon: this.iconBasePath + 'rilasciaxy.gif',
				handlerBaseName : 'ReleaseLoc',
				listeners: {click: {fn: notifyReleaseLoc,  scope: thisContextMenu}}
			});
			
			//fixIE10
			//if (Ext.isIE10){Ext.getCmp('releaseLoc').menu.setWidth(200);}
			
		}

		if (this.paramsJS.layOut.ols){
			this.addEvents('onOlsMethod');
			this.addEvents('onOlsReverseGeocoding');
			this.addEvents('onOlsStartPoint');
			this.addEvents('onOlsViaPoint');
			this.addEvents('onOlsEndPoint');
			this.addEvents('onOlsReset');
			
			var onOlsMethod = function(item, checked) {
				if (item.method != this.olsMethod) {
					this.olsMethod = item.method;
					this.fireEvent('onOlsMethod', this.olsMethod);
				}
			}
			
			this.add("-");
			
			var olsMethodItems = [
				{
                    text: ToloI18n.getMsg("ToloContextMenu.OLS.piuveloce"),
                    method: 'Fastest',
                    group: 'olsMethods',                        
                    xtype: 'menucheckitem',
                    listeners: {click: {fn: onOlsMethod,  scope: this}},
                    checked: true
                },{
                    text: ToloI18n.getMsg("ToloContextMenu.OLS.piucorto"),
                    method: 'Shortest',
                    group: 'olsMethods',                        
                    xtype: 'menucheckitem',
                    listeners: {click: {fn: onOlsMethod,  scope: this}}
                },{
                    text: ToloI18n.getMsg("ToloContextMenu.OLS.apiedi"),
                    method: 'Pedestrian',
                    group: 'olsMethods',                        
                    xtype: 'menucheckitem',
                    listeners: {click: {fn: onOlsMethod,  scope: this}}
                }
            ];
			
			var olsItems = [
				{
                    text: ToloI18n.getMsg("ToloContextMenu.OLS.indirizzo"),
                    listeners: {click: {fn: function() {
            			this.fireEvent('onOlsReverseGeocoding', this.getXEvtPos(), this.getYEvtPos());
        			},  scope: this}}
                },{
                    text: '-',
                    xtype: 'menuseparator'
                },{
                    text: ToloI18n.getMsg("ToloContextMenu.OLS.partenza"),
    				icon: this.iconBasePath + 'ols/start.png',
                    listeners: {click: {fn: function() {
            			this.fireEvent('onOlsStartPoint', this.getXEvtPos(), this.getYEvtPos());
        			},  scope: this}}
                },{
                    text: ToloI18n.getMsg("ToloContextMenu.OLS.destIntermedia"),
    				icon: this.iconBasePath + 'ols/via.png',
                    listeners: {click: {fn: function() {
            			this.fireEvent('onOlsViaPoint', this.getXEvtPos(), this.getYEvtPos());
        			},  scope: this}}
                },{
                    text: ToloI18n.getMsg("ToloContextMenu.OLS.arrivo"),
    				icon: this.iconBasePath + 'ols/stop.png',
                    listeners: {click: {fn: function() {
            			this.fireEvent('onOlsEndPoint', this.getXEvtPos(), this.getYEvtPos());
        			},  scope: this}}
                },{
                    text: '-',
                    xtype: 'menuseparator'
                },{
    				text: ToloI18n.getMsg("ToloContextMenu.OLS.modalita"),
    				menu: {
    					cls: 'clearCSS',
    	                items: olsMethodItems
    	            }
    			},{
                    text: '-',
                    xtype: 'menuseparator'
                },{
                    text: ToloI18n.getMsg("ToloContextMenu.OLS.cancellapercorso"),
                    icon: this.iconBasePath + 'ols/erase.png',
                    listeners: {click: {fn: function() {
            			this.fireEvent('onOlsReset', this.getXEvtPos(), this.getYEvtPos());
        			},  scope: this}}
                }
            ];
			
			this.add({
				text: ToloI18n.getMsg("ToloContextMenu.OLS.navigazione"),
				menu: {
					cls: 'clearCSS',
	                items: olsItems
	            },
				handlerBaseName : 'ReleaseOls'
			});
		}
		
		if (this.paramsJS.layOut.conStreetView){
			this.add("-");
			this.add({
				text: ToloI18n.getMsg("ToloContextMenu.Streetview"),
				icon: this.iconBasePath + 'streetview16.png',
				//icon: this.iconBasePath + 'eye-arrow.png',
				handlerBaseName : 'ReleaseStreetview',
				listeners: {click: {fn: notifyReleaseStreetview,  scope: thisContextMenu}}
			});
		}
		
		
		this.doLayout();
	
	},

	/**
	 * @method menuItemHandler
	 * 
	 * 
	 * @param {Object} button
	 * il pulsante.
	 * 
	 * @param {Object} e
	 * l'evento.
	 * 
	 * @return {Boolean}
	 * 
	 * 
	 */
	btnClickHandler: function (button, e) {
		if (button.handlerBaseName !=null) {
			var eventName = 'on' + button.handlerBaseName + 'ClickFn';
			this.fireEvent(eventName, button);
		}		
		return true;
	},

	/**
	 * @method bindToAPI
	 * 
	 * 
	 * @param {Object} api
	 * API.
	 * 
	 */
	bindToAPI: function(api) {
		this.api=api;
		// Eventi API ai quali viene registrato buttonsPanel
		/*
		api.on('onOperationEnable', function (opCode) {this.operationEnable(opCode, true);}, this );
		api.on('onOperationDisable', function (opCode) {this.operationEnable(opCode, false);}, this );
		api.on('onOperationPressDefault', function (group) {this.pressDefault(group);}, this );
		*/
	},
	
	/**
	 * @method showAt
	 * Metodo sovrascritto per registrarsi la posizione x ed y
	 * 
	 * @param {Array} xyPosition
	 * 
	 * 
	 * @param {Object} parentMenu
	 * 
	 * 
	 */ 
	showAt: function(xyPosition,parentMenu) {
		this.xEvtPos = xyPosition[0];
		this.yEvtPos = xyPosition[1];
		//TolomeoExt.ToloContextMenu.superclass.showAt.apply(this, arguments);
		this.callParent(arguments);
	},
	
	/**
	 * @method getXEvtPos
	 * 
	 * 
	 * @return {Number}
	 * posizione x dell'evento originante
	 * 
	 */
	getXEvtPos: function(){
		return this.xEvtPos;
	},
	
	/**
	 * @method getYEvtPos
	 * 
	 * 
	 * @return {Number}
	 * posizione y dell'evento originante
	 * 
	 */
	getYEvtPos: function(){
		return this.yEvtPos;
	},
	
	/**
	 * @method setCrsSelected
	 * Imposta il Sistema di coordinate da mostrare
	 * 
	 * @param {String} projCode
	 * 
	 * 
	 */
	setCrsSelected: function(projCode){
		var relLoc = this.getComponent(this.getId() + 'releaseLoc');
		if(relLoc){
			var prjItem = relLoc.menu.items.get('chk_'+projCode);
			if(prjItem){
				prjItem.setChecked(true,true);
			}
		}
		this.crsSelected = projCode;
	}
			
});
/**
 * Basic status bar component that can be used as the bottom toolbar of any {@link Ext.Panel}.  In addition to
 * supporting the standard {@link Ext.toolbar.Toolbar} interface for adding buttons, menus and other items, the StatusBar
 * provides a greedy status element that can be aligned to either side and has convenient methods for setting the
 * status text and icon.  You can also indicate that something is processing using the {@link #showBusy} method.
 *
 *     Ext.create('Ext.Panel', {
 *         title: 'StatusBar',
 *         // etc.
 *         bbar: Ext.create('Ext.ux.StatusBar', {
 *             id: 'my-status',
 *      
 *             // defaults to use when the status is cleared:
 *             defaultText: 'Default status text',
 *             defaultIconCls: 'default-icon',
 *      
 *             // values to set initially:
 *             text: 'Ready',
 *             iconCls: 'ready-icon',
 *      
 *             // any standard Toolbar items:
 *             items: [{
 *                 text: 'A Button'
 *             }, '-', 'Plain Text']
 *         })
 *     });
 *
 *     // Update the status bar later in code:
 *     var sb = Ext.getCmp('my-status');
 *     sb.setStatus({
 *         text: 'OK',
 *         iconCls: 'ok-icon',
 *         clear: true // auto-clear after a set interval
 *     });
 *
 *     // Set the status bar to show that something is processing:
 *     sb.showBusy();
 *
 *     // processing....
 *
 *     sb.clearStatus(); // once completeed
 *
 */
Ext.define('Ext.ux.statusbar.StatusBar', {
    extend: 'Ext.toolbar.Toolbar',
    alternateClassName: 'Ext.ux.StatusBar',
    alias: 'widget.statusbar',
    requires: ['Ext.toolbar.TextItem'],
    /**
     * @cfg {String} statusAlign
     * The alignment of the status element within the overall StatusBar layout.  When the StatusBar is rendered,
     * it creates an internal div containing the status text and icon.  Any additional Toolbar items added in the
     * StatusBar's {@link #cfg-items} config, or added via {@link #method-add} or any of the supported add* methods, will be
     * rendered, in added order, to the opposite side.  The status element is greedy, so it will automatically
     * expand to take up all sapce left over by any other items.  Example usage:
     *
     *     // Create a left-aligned status bar containing a button,
     *     // separator and text item that will be right-aligned (default):
     *     Ext.create('Ext.Panel', {
     *         title: 'StatusBar',
     *         // etc.
     *         bbar: Ext.create('Ext.ux.statusbar.StatusBar', {
     *             defaultText: 'Default status text',
     *             id: 'status-id',
     *             items: [{
     *                 text: 'A Button'
     *             }, '-', 'Plain Text']
     *         })
     *     });
     *
     *     // By adding the statusAlign config, this will create the
     *     // exact same toolbar, except the status and toolbar item
     *     // layout will be reversed from the previous example:
     *     Ext.create('Ext.Panel', {
     *         title: 'StatusBar',
     *         // etc.
     *         bbar: Ext.create('Ext.ux.statusbar.StatusBar', {
     *             defaultText: 'Default status text',
     *             id: 'status-id',
     *             statusAlign: 'right',
     *             items: [{
     *                 text: 'A Button'
     *             }, '-', 'Plain Text']
     *         })
     *     });
     */
    /**
     * @cfg {String} [defaultText='']
     * The default {@link #text} value.  This will be used anytime the status bar is cleared with the
     * `useDefaults:true` option.
     */
    /**
     * @cfg {String} [defaultIconCls='']
     * The default {@link #iconCls} value (see the iconCls docs for additional details about customizing the icon).
     * This will be used anytime the status bar is cleared with the `useDefaults:true` option.
     */
    /**
     * @cfg {String} text
     * A string that will be <b>initially</b> set as the status message.  This string
     * will be set as innerHTML (html tags are accepted) for the toolbar item.
     * If not specified, the value set for {@link #defaultText} will be used.
     */
    /**
     * @cfg {String} [iconCls='']
     * A CSS class that will be **initially** set as the status bar icon and is
     * expected to provide a background image.
     *
     * Example usage:
     *
     *     // Example CSS rule:
     *     .x-statusbar .x-status-custom {
     *         padding-left: 25px;
     *         background: transparent url(images/custom-icon.gif) no-repeat 3px 2px;
     *     }
     *
     *     // Setting a default icon:
     *     var sb = Ext.create('Ext.ux.statusbar.StatusBar', {
     *         defaultIconCls: 'x-status-custom'
     *     });
     *
     *     // Changing the icon:
     *     sb.setStatus({
     *         text: 'New status',
     *         iconCls: 'x-status-custom'
     *     });
     */

    /**
     * @cfg {String} cls
     * The base class applied to the containing element for this component on render.
     */
    cls : 'x-statusbar',
    /**
     * @cfg {String} busyIconCls
     * The default {@link #iconCls} applied when calling {@link #showBusy}.
     * It can be overridden at any time by passing the `iconCls` argument into {@link #showBusy}.
     */
    busyIconCls : 'x-status-busy',
    /**
     * @cfg {String} busyText
     * The default {@link #text} applied when calling {@link #showBusy}.
     * It can be overridden at any time by passing the `text` argument into {@link #showBusy}.
     */
    busyText : 'Loading...',
    /**
     * @cfg {Number} autoClear
     * The number of milliseconds to wait after setting the status via
     * {@link #setStatus} before automatically clearing the status text and icon.
     * Note that this only applies when passing the `clear` argument to {@link #setStatus}
     * since that is the only way to defer clearing the status.  This can
     * be overridden by specifying a different `wait` value in {@link #setStatus}.
     * Calls to {@link #clearStatus} always clear the status bar immediately and ignore this value.
     */
    autoClear : 5000,

    /**
     * @cfg {String} emptyText
     * The text string to use if no text has been set. If there are no other items in
     * the toolbar using an empty string (`''`) for this value would end up in the toolbar
     * height collapsing since the empty string will not maintain the toolbar height.
     * Use `''` if the toolbar should collapse in height vertically when no text is
     * specified and there are no other items in the toolbar.
     */
    emptyText : '&#160;',

    // private
    activeThreadId : 0,

    // private
    initComponent : function(){
        var right = this.statusAlign === 'right';

        this.callParent(arguments);
        this.currIconCls = this.iconCls || this.defaultIconCls;
        this.statusEl = Ext.create('Ext.toolbar.TextItem', {
            cls: 'x-status-text ' + (this.currIconCls || ''),
            text: this.text || this.defaultText || ''
        });

        if (right) {
            this.cls += ' x-status-right';
            this.add('->');
            this.add(this.statusEl);
        } else {
            this.insert(0, this.statusEl);
            this.insert(1, '->');
        }
    },

    /**
     * Sets the status {@link #text} and/or {@link #iconCls}. Also supports automatically clearing the
     * status that was set after a specified interval.
     *
     * Example usage:
     *
     *     // Simple call to update the text
     *     statusBar.setStatus('New status');
     *
     *     // Set the status and icon, auto-clearing with default options:
     *     statusBar.setStatus({
     *         text: 'New status',
     *         iconCls: 'x-status-custom',
     *         clear: true
     *     });
     *
     *     // Auto-clear with custom options:
     *     statusBar.setStatus({
     *         text: 'New status',
     *         iconCls: 'x-status-custom',
     *         clear: {
     *             wait: 8000,
     *             anim: false,
     *             useDefaults: false
     *         }
     *     });
     *
     * @param {Object/String} config A config object specifying what status to set, or a string assumed
     * to be the status text (and all other options are defaulted as explained below). A config
     * object containing any or all of the following properties can be passed:
     *
     * @param {String} config.text The status text to display.  If not specified, any current
     * status text will remain unchanged.
     *
     * @param {String} config.iconCls The CSS class used to customize the status icon (see
     * {@link #iconCls} for details). If not specified, any current iconCls will remain unchanged.
     *
     * @param {Boolean/Number/Object} config.clear Allows you to set an internal callback that will
     * automatically clear the status text and iconCls after a specified amount of time has passed. If clear is not
     * specified, the new status will not be auto-cleared and will stay until updated again or cleared using
     * {@link #clearStatus}. If `true` is passed, the status will be cleared using {@link #autoClear},
     * {@link #defaultText} and {@link #defaultIconCls} via a fade out animation. If a numeric value is passed,
     * it will be used as the callback interval (in milliseconds), overriding the {@link #autoClear} value.
     * All other options will be defaulted as with the boolean option.  To customize any other options,
     * you can pass an object in the format:
     * 
     * @param {Number} config.clear.wait The number of milliseconds to wait before clearing
     * (defaults to {@link #autoClear}).
     * @param {Boolean} config.clear.anim False to clear the status immediately once the callback
     * executes (defaults to true which fades the status out).
     * @param {Boolean} config.clear.useDefaults False to completely clear the status text and iconCls
     * (defaults to true which uses {@link #defaultText} and {@link #defaultIconCls}).
     *
     * @return {Ext.ux.statusbar.StatusBar} this
     */
    setStatus : function(o) {
        var me = this;

        o = o || {};
        Ext.suspendLayouts();
        if (Ext.isString(o)) {
            o = {text:o};
        }
        if (o.text !== undefined) {
            me.setText(o.text);
        }
        if (o.iconCls !== undefined) {
            me.setIcon(o.iconCls);
        }

        if (o.clear) {
            var c = o.clear,
                wait = me.autoClear,
                defaults = {useDefaults: true, anim: true};

            if (Ext.isObject(c)) {
                c = Ext.applyIf(c, defaults);
                if (c.wait) {
                    wait = c.wait;
                }
            } else if (Ext.isNumber(c)) {
                wait = c;
                c = defaults;
            } else if (Ext.isBoolean(c)) {
                c = defaults;
            }

            c.threadId = this.activeThreadId;
            Ext.defer(me.clearStatus, wait, me, [c]);
        }
        Ext.resumeLayouts(true);
        return me;
    },

    /**
     * Clears the status {@link #text} and {@link #iconCls}. Also supports clearing via an optional fade out animation.
     *
     * @param {Object} [config] A config object containing any or all of the following properties.  If this
     * object is not specified the status will be cleared using the defaults below:
     * @param {Boolean} config.anim True to clear the status by fading out the status element (defaults
     * to false which clears immediately).
     * @param {Boolean} config.useDefaults True to reset the text and icon using {@link #defaultText} and
     * {@link #defaultIconCls} (defaults to false which sets the text to '' and removes any existing icon class).
     *
     * @return {Ext.ux.statusbar.StatusBar} this
     */
    clearStatus : function(o) {
        o = o || {};

        var me = this,
            statusEl = me.statusEl;

        if (o.threadId && o.threadId !== me.activeThreadId) {
            // this means the current call was made internally, but a newer
            // thread has set a message since this call was deferred.  Since
            // we don't want to overwrite a newer message just ignore.
            return me;
        }

        var text = o.useDefaults ? me.defaultText : me.emptyText,
            iconCls = o.useDefaults ? (me.defaultIconCls ? me.defaultIconCls : '') : '';

        if (o.anim) {
            // animate the statusEl Ext.Element
            statusEl.el.puff({
                remove: false,
                useDisplay: true,
                callback: function() {
                    statusEl.el.show();
                    me.setStatus({
                        text: text,
                        iconCls: iconCls
                    });
                }
            });
        } else {
             me.setStatus({
                 text: text,
                 iconCls: iconCls
             });
        }
        return me;
    },

    /**
     * Convenience method for setting the status text directly.  For more flexible options see {@link #setStatus}.
     * @param {String} text (optional) The text to set (defaults to '')
     * @return {Ext.ux.statusbar.StatusBar} this
     */
    setText : function(text) {
        var me = this;
        me.activeThreadId++;
        me.text = text || '';
        if (me.rendered) {
            me.statusEl.setText(me.text);
        }
        return me;
    },

    /**
     * Returns the current status text.
     * @return {String} The status text
     */
    getText : function(){
        return this.text;
    },

    /**
     * Convenience method for setting the status icon directly.  For more flexible options see {@link #setStatus}.
     * See {@link #iconCls} for complete details about customizing the icon.
     * @param {String} iconCls (optional) The icon class to set (defaults to '', and any current icon class is removed)
     * @return {Ext.ux.statusbar.StatusBar} this
     */
    setIcon : function(cls) {
        var me = this;

        me.activeThreadId++;
        cls = cls || '';

        if (me.rendered) {
            if (me.currIconCls) {
                me.statusEl.removeCls(me.currIconCls);
                me.currIconCls = null;
            }
            if (cls.length > 0) {
                me.statusEl.addCls(cls);
                me.currIconCls = cls;
            }
        } else {
            me.currIconCls = cls;
        }
        return me;
    },

    /**
     * Convenience method for setting the status text and icon to special values that are pre-configured to indicate
     * a "busy" state, usually for loading or processing activities.
     *
     * @param {Object/String} config (optional) A config object in the same format supported by {@link #setStatus}, or a
     * string to use as the status text (in which case all other options for setStatus will be defaulted).  Use the
     * `text` and/or `iconCls` properties on the config to override the default {@link #busyText}
     * and {@link #busyIconCls} settings. If the config argument is not specified, {@link #busyText} and
     * {@link #busyIconCls} will be used in conjunction with all of the default options for {@link #setStatus}.
     * @return {Ext.ux.statusbar.StatusBar} this
     */
    showBusy : function(o){
        if (Ext.isString(o)) {
            o = { text: o };
        }
        o = Ext.applyIf(o || {}, {
            text: this.busyText,
            iconCls: this.busyIconCls
        });
        return this.setStatus(o);
    }
});
/*!
 * Ext Core Library $version&#xD;&#xA;http://extjs.com/&#xD;&#xA;Copyright(c) 2006-2009, $author.&#xD;&#xA;&#xD;&#xA;The MIT License&#xD;&#xA;&#xD;&#xA;Permission is hereby granted, free of charge, to any person obtaining a copy&#xD;&#xA;of this software and associated documentation files (the &quot;Software&quot;), to deal&#xD;&#xA;in the Software without restriction, including without limitation the rights&#xD;&#xA;to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&#xD;&#xA;copies of the Software, and to permit persons to whom the Software is&#xD;&#xA;furnished to do so, subject to the following conditions:&#xD;&#xA;&#xD;&#xA;The above copyright notice and this permission notice shall be included in&#xD;&#xA;all copies or substantial portions of the Software.&#xD;&#xA;&#xD;&#xA;THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&#xD;&#xA;IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&#xD;&#xA;FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&#xD;&#xA;AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&#xD;&#xA;LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,&#xD;&#xA;OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN&#xD;&#xA;THE SOFTWARE.&#xD;&#xA;
 */
Ext.define('Ext.ux.Carousel', {
		extend: 'Ext.util.Observable',

    interval: 3,
    transitionDuration: 1,
    transitionType: 'carousel',
    transitionEasing: 'easeOut',
    itemSelector: 'img',
    activeSlide: 0,
    autoPlay: false,
    showPlayButton: false,
    pauseOnNavigate: false,
    wrap: false,
    freezeOnHover: false,
    navigationOnHover: false,
    hideNavigation: false,
    width: null,
    height: null,

    constructor: function(elId, config) {
        config = config || {};
        Ext.apply(this, config);

        this.callParent(arguments);
        
        this.addEvents(
            'beforeprev',
            'prev',
            'beforenext',
            'next',
            'change',
            'play',
            'pause',
            'freeze',
            'unfreeze'
        );

        this.el = Ext.get(elId);
        this.slides = this.els = [];
        
        if(this.autoPlay || this.showPlayButton) {
            this.wrap = true;
        };

        if(this.autoPlay && typeof config.showPlayButton === 'undefined') {
            this.showPlayButton = true;
        }

        this.initMarkup();
        this.initEvents();

        if(this.carouselSize > 0) {
            this.refresh();
        }
    },

    initMarkup: function() {
        var dh = Ext.DomHelper;
        
        this.carouselSize = 0;
        var items = this.el.select(this.itemSelector);
        this.els.container = dh.append(this.el, {cls: 'ux-carousel-container'}, true);
        this.els.slidesWrap = dh.append(this.els.container, {cls: 'ux-carousel-slides-wrap'}, true);

        this.els.navigation = dh.append(this.els.container, {cls: 'ux-carousel-nav'}, true).hide();
        this.els.caption = dh.append(this.els.navigation, {tag: 'h2', cls: 'ux-carousel-caption'}, true);
        this.els.navNext = dh.append(this.els.navigation, {tag: 'a', href: '#', cls: 'ux-carousel-nav-next'}, true);
        if(this.showPlayButton) {
            this.els.navPlay = dh.append(this.els.navigation, {tag: 'a', href: '#', cls: 'ux-carousel-nav-play'}, true)
        }
        this.els.navPrev = dh.append(this.els.navigation, {tag: 'a', href: '#', cls: 'ux-carousel-nav-prev'}, true);

        // set the dimensions of the container
        this.slideWidth = this.width || this.el.getWidth(true);
        this.slideHeight = this.height || this.el.getHeight(true);
        this.els.container.setStyle({
            width: this.slideWidth + 'px',
            height: this.slideHeight + 'px'
        });        
        
        var w = Math.max((this.slideWidth - (this.els.navNext.getWidth()*2) - (this.showPlayButton ? this.els.navPlay.getWidth() : 0) - 20),0);        
        this.els.caption.setWidth(w  + 'px');
        
        items.appendTo(this.els.slidesWrap).each(function(item) {
            item = item.wrap({cls: 'ux-carousel-slide'});
            this.slides.push(item);
            item.setWidth(this.slideWidth + 'px').setHeight(this.slideHeight + 'px');
        }, this);
        this.carouselSize = this.slides.length;
        if(this.navigationOnHover) {
            this.els.navigation.setStyle('top', (-1*this.els.navigation.getHeight()) + 'px');
        }
        this.el.clip();
    },

    resize: function() {

    	   	
    	var items = this.el.select(this.itemSelector);
    	
        // set the dimensions of the container
        this.slideWidth = this.el.getWidth(true);
        this.slideHeight = this.el.getHeight(true);
        
        var offset = this.activeSlide * this.slideWidth;
    	this.els.slidesWrap.setStyle('left', (-1 * offset) + 'px');
    	this.els.slidesWrap.setWidth((this.slideWidth * this.carouselSize) + 'px');
    	
        this.els.container.setStyle({
            width: this.slideWidth + 'px',
            height: this.slideHeight + 'px'
        });

        this.els.caption.setWidth((this.slideWidth - (this.els.navNext.getWidth()*2) - (this.showPlayButton ? this.els.navPlay.getWidth() : 0) - 20) + 'px')
        
        items.each(function(item) {
        	var item = item.parent();
            item.setWidth(this.slideWidth + 'px').setHeight(this.slideHeight + 'px');
        	//ale
            item = item.parent();
            item.setWidth(this.slideWidth + 'px').setHeight(this.slideHeight + 'px');
        }, this);
        if(this.navigationOnHover) {
            this.els.navigation.setStyle('top', (-1*this.els.navigation.getHeight()) + 'px');
        }
        
        
        //this.el.clip();
    },
    
    setOffset: function(x,y) {
    	
        var items = this.el.select(this.itemSelector);
        
    	items.each(function(item) {
        	var item = item.parent();
            item.setStyle('top',  y + 'px')
            item.setStyle('left',  x + 'px')
        }, this);
    	
    },
    
    initEvents: function() {
        this.els.navPrev.on('click', function(ev) {
            ev.preventDefault();
            var target = ev.getTarget();
            target.blur();            
            if(Ext.fly(target).hasCls('ux-carousel-nav-disabled')) return;
            this.prev();
        }, this);

        this.els.navNext.on('click', function(ev) {
            ev.preventDefault();
            var target = ev.getTarget();
            target.blur();
            if(Ext.fly(target).hasCls('ux-carousel-nav-disabled')) return;
            this.next();
        }, this);

        if(this.showPlayButton) {
            this.els.navPlay.on('click', function(ev){
                ev.preventDefault();
                ev.getTarget().blur();
                if(this.playing) {
                    this.pause();
                }
                else {
                    this.play();
                }
            }, this);
        };

        if(this.freezeOnHover) {
            this.els.container.on('mouseenter', function(){
                if(this.playing) {
                    this.fireEvent('freeze', this.slides[this.activeSlide]);
                    Ext.TaskManager.stop(this.playTask);
                }
            }, this);
            this.els.container.on('mouseleave', function(){
                if(this.playing) {
                    this.fireEvent('unfreeze', this.slides[this.activeSlide]);
                    Ext.TaskManager.start(this.playTask);
                }
            }, this, {buffer: (this.interval/2)*1000});
        };

        if(this.navigationOnHover) {
            this.els.container.on('mouseenter', function(){
                if(!this.navigationShown) {
                    this.navigationShown = true;
                    this.els.navigation.stopFx(false).shift({
                        y: this.els.container.getY(),
                        duration: this.transitionDuration
                    })
                }
            }, this);

            this.els.container.on('mouseleave', function(){
                if(this.navigationShown) {
                    this.navigationShown = false;
                    this.els.navigation.stopFx(false).shift({
                        y: this.els.navigation.getHeight() - this.els.container.getY(),
                        duration: this.transitionDuration
                    })
                }
            }, this);
        }

        if(this.interval && this.autoPlay) {
            this.play();
        };
    },

    prev: function() {
        if (this.fireEvent('beforeprev') === false) {
            return;
        }
        if(this.pauseOnNavigate) {
            this.pause();
        }
        this.setSlide(this.activeSlide - 1);

        this.fireEvent('prev', this.activeSlide);        
        return this; 
    },
    
    next: function() {
        if(this.fireEvent('beforenext') === false) {
            return;
        }
        if(this.pauseOnNavigate) {
            this.pause();
        }
        this.setSlide(this.activeSlide + 1);

        this.fireEvent('next', this.activeSlide);        
        return this;         
    },

    play: function() {
        if(!this.playing) {
            this.playTask = this.playTask || {
                run: function() {
                    this.playing = true;
                    this.setSlide(this.activeSlide+1);
                },
                interval: this.interval*1000,
                scope: this
            };
            
            this.playTaskBuffer = this.playTaskBuffer || new Ext.util.DelayedTask(function() {
                Ext.TaskManager.start(this.playTask);
            }, this);

            this.playTaskBuffer.delay(this.interval*1000);
            this.playing = true;
            if(this.showPlayButton) {
                this.els.navPlay.addCls('ux-carousel-playing');
            }
            this.fireEvent('play');
        }        
        return this;
    },

    pause: function() {
        if(this.playing) {
            Ext.TaskManager.stop(this.playTask);
            this.playTaskBuffer.cancel();
            this.playing = false;
            if(this.showPlayButton) {
                this.els.navPlay.removeCls('ux-carousel-playing');
            }
            this.fireEvent('pause');
        }        
        return this;
    },
        
    clear: function() {
        this.els.slidesWrap.update('');
        this.slides = [];
        this.carouselSize = 0;
        this.pause();
        return this;
    },
    
    add: function(el, refresh) {
    	
        /* Originale
        var item = Ext.fly(el).appendTo(this.els.slidesWrap).wrap({cls: 'ux-carousel-slide'});
        item.setWidth(this.slideWidth + 'px').setHeight(this.slideHeight + 'px');
        */
    	//ale
        //  item = item.wrap({style: 'position: relative; left:0px; top: 0px; '});
    	
    	var item = Ext.fly(el).appendTo(this.els.slidesWrap).wrap({style: 'position: relative; left:0px; top: 0px; '});
    	item.setWidth(this.slideWidth + 'px').setHeight(this.slideHeight + 'px');
    	
    	item =item.wrap({cls: 'ux-carousel-slide'});
        item.setWidth(this.slideWidth + 'px').setHeight(this.slideHeight + 'px');
    	
    	
        this.slides.push(item);                        
        if(refresh) {
            this.refresh();
        }        
        return this;
    },
    
    refresh: function(slide) {
        this.carouselSize = this.slides.length;
        this.els.slidesWrap.setWidth((this.slideWidth * this.carouselSize) + 'px');
        if(this.carouselSize > 0) {
            if(!this.hideNavigation) this.els.navigation.show();
            this.setSlide((slide) ? slide : 0, slide==null || slide==0);
        }                
        return this;        
    },
    
    setSlide: function(index, initial) {
        if(!this.wrap && !this.slides[index]) {
            return;
        }
        else if(this.wrap) {
            if(index < 0) {
                index = this.carouselSize-1;
            }
            else if(index > this.carouselSize-1) {
                index = 0;
            }
        }
        if(!this.slides[index]) {
            return;
        }

        // controllo se immagine è caricata ed eventualmente la carico
        var a = this.slides[index].dom.childNodes[0].childNodes[0];
        if (a.isLoaded && a.isLoaded()==false) {
        	a.load();
        }
        
        this.els.caption.update(this.slides[index].child(':first-child', false).child(':first-child', true).title || '');
        var offset = index * this.slideWidth;
        if (!initial) {
            switch (this.transitionType) {
                case 'fade':
                    this.slides[index].setOpacity(0);
                    //this.slides[this.activeSlide].stopFx(false).fadeOut({
                    this.slides[this.activeSlide].stopAnimation().fadeOut({
                        duration: this.transitionDuration / 2,
                        callback: function(){
                            this.els.slidesWrap.setStyle('left', (-1 * offset) + 'px');
                            this.slides[this.activeSlide].setOpacity(1);
                            this.slides[index].fadeIn({
                                duration: this.transitionDuration / 2
                            });
                        },
                        scope: this
                    })
                    break;

                default:
                    var xNew = (-1 * offset) + this.els.container.getX();
                    this.els.slidesWrap.stopAnimation();
                    this.els.slidesWrap.shift({
                        duration: this.transitionDuration,
                        x: xNew,
                        easing: this.transitionEasing
                    });
                    break;
            }
        }
        else {
            this.els.slidesWrap.setStyle('left', '0');
        }

        this.activeSlide = index;
        this.updateNav();
        this.fireEvent('change', this.slides[index], index);
    },

    updateNav: function() {
        this.els.navPrev.removeCls('ux-carousel-nav-disabled');
        this.els.navNext.removeCls('ux-carousel-nav-disabled');
        if(!this.wrap) {
            if(this.activeSlide === 0) {
                this.els.navPrev.addCls('ux-carousel-nav-disabled');
            }
            if(this.activeSlide === this.carouselSize-1) {
                this.els.navNext.addCls('ux-carousel-nav-disabled');
            }
        }
    }
});
/**
 * @class Ext.ux.CarouselPanel
 * @author Alessandro Radaelli - Comune di Prato
 * 
 */
Ext.define('Ext.ux.CarouselPanel', {
	extend: 'Ext.Panel',

	carousel: null,
	carouselItems: null,
	carouselConfig: null,
	carouselFixedLeft: null,
	carouselFixedTop: null,
	autoOffsetXAdjust:0,
	autoOffsetYAdjust:0,
	draggableObj: null,
	
	autoDraggableDetect: true,
	
	// private
	itemsToAppend: null,
	
	initComponent: function(){
		
		this.layout= 'fit';
		this.autoScroll= true;
	    this.itemsToAppend = (this.carouselItems!=null) ? this.carouselItems : [];
	    if (this.carouselFixedLeft!=null && this.carouselFixedTop!=null && this.autoDraggableDetect) this.on('added', this.addedHandler, this);
	    
	    this.addEvents('draggableObjInitialized');
	    
	    if (this.draggable && this.autoDraggableDetect) {
	    	this.draggableObj = this;
	    	this.fireEvent(draggableObjInitialized, this.draggableObj);
	    } 
	    
	    this.callParent(arguments);
	
	},
	
	addedHandler: function(thisComponent, ownerCt, index) {
		
		if (ownerCt && ownerCt.draggable) {
	    	this.setDraggableObj(ownerCt);
	    }
		
	},
	/*
	_setDDOwner: function() {

			var thisPanel=this;
    		//if (!this.draggableObj.dd) {
    			//Ext.dd.DD
    			this.draggableObj.dd = new Ext.dd.DD(this.draggableObj.getId()); //this.draggableObj
    			//this.draggableObj.dd.setDragElId(this.body.id);
    			this.draggableObj.dd.constrainTo(Ext.get(this.getEl())); //this.draggableObj.renderTo
        		this.draggableObj.dd.startDrag = this.draggableObj.dd.startDrag.createSequence(
    		    										function(){
    										    	        var w = thisPanel.draggableObj;
    										    	        var so = w.el.shadowOffset;
    										    	        this.constrainTo(w.container, {right: so, left: so, bottom: so});
    										    	    });
    		//}
    		this.draggableObj.dd.onDrag = this.draggableObj.dd.onDrag.createSequence(thisPanel.onDragHandler, thisPanel);
    		
    		this.fireEvent('draggableObjInitialized', this.draggableObj);
	}, 
	*/
	setDraggableObj: function(draggableObj) {

		if ( draggableObj.draggable) {
			this.draggableObj = draggableObj;
			if (this.draggableObj) {
				//var thisPanel=this;
		    	/*if (this.draggableObj.rendered) {
		    		if (this.rendered) {
		    			this._setDDOwner();	
		    		}
		    		else 
		    			this.on('afterrender', this._setDDOwner, thisPanel, {single:true});
		    	} else {
		    		this.draggableObj.on('afterrender', this._setDDOwner, thisPanel, {single:true});
		    	}*/
		    	
		    	this.draggableObj.on('move', this.onDragHandler, this);	    		
		    	this.draggableObj.on('expand', this.onDragHandler, this);
		
		    }
		} 
	},
	
	
	removed : function( thisComponent, ownerCt ) {
		//????
		
	}, 
	
    /**
     * Method: afterRender
     * Metodo privato invocato dopo che il pannello è stato renderizzato.
     * 
     */
    afterRender: function() {    	
    	this.callParent(arguments);
    	
		this.carousel = Ext.create('Ext.ux.Carousel', this.body.id, this.carouselConfig);
		this.on('resize', this.carousel.resize, this.carousel);
		this.on('maximize', this.carousel.resize, this.carousel);
		if (this.itemsToAppend!=null) {
			for (var i=0; i<this.itemsToAppend.length; i++) {
				this.carousel.add(this.itemsToAppend[i], false);
			}
			this.autoOffset();
			this.carousel.refresh();
		}
			
    },
	
	prevItem: function() {
		if (this.carousel) this.carousel.prev();
        return this.carousel; 
    },
    
    nextItem: function() {
    	if (this.carousel) this.carousel.next();
        return this.carousel;         
    },

    play: function() {
    	if (this.carousel) this.carousel.play();
        return this.carousel;
    },

    pause: function() {
    	if (this.carousel) this.carousel.pause();        
        return this.carousel;
    },
        
    clearItems: function() {
    	if (this.carousel) this.carousel.clear();
        return this.carousel;
    },
    
    addImgItem: function(imgid, imgurl, imgtitle, refresh) {
    	
    	var docbody = Ext.getBody();
      	var imgel = Ext.DomHelper.insertHtml('afterBegin', docbody.dom, '<img id="'+ imgid +'" title="'+ imgtitle +'" />');
    	
      	imgel.srctoload=imgurl;
      	imgel.isLoaded=function() {return this.src && this.src!=""; };
      	imgel.load=function() { this.src=this.srctoload; };
      	
      	
      	this.addItem(imgid, refresh);
    },
    
    addItem: function(el, refresh) {
    	
    	if (this.carousel) {
    		this.carousel.add(el, refresh);
    		this.autoOffset();
    	} else {
    		this.itemsToAppend.push(el);
    	}
        return this.carousel;
    },
    
    refresh: function(slide) {
    	if (this.carousel) this.carousel.refresh(slide);                
        return this.carousel;        
    },
    
    setSlide: function(index, initial) {
    	if (this.carousel) this.carousel.setSlide(index, initial);
    },
    
    setOffset: function(xOffset, yOffset) {
    	if (this.carousel) this.carousel.setOffset(xOffset, yOffset);
    },
    
    autoOffset: function() {
    	var pos = this.getPosition(false);
    	var xOffset = this.carouselFixedLeft - pos[0] + this.autoOffsetXAdjust;
    	var yOffset = this.carouselFixedTop - pos[1] + this.autoOffsetYAdjust;
    	
    	if (this.carousel) this.setOffset(xOffset, yOffset);
    	
    },

	setCarouselFixed: function(newCarouselFixedLeft, newCarouselFixedTop) {
		this.carouselFixedLeft = newCarouselFixedLeft;
		this.carouselFixedTop  = newCarouselFixedTop;
		this.autoOffset();
	},
    
    onDragHandler: function() {

    	this.autoOffset();
		return 	true;
    
    }
    
	
});/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloTimeMachinePanel
 * @extends Ext.Panel
 * Pannello ExtJs che implementa la funzionalità TimeMachine
 * 
 * @param {Object} config
 * 
 * 
 * @param {Object} config.paramsJS
 * Oggetto i parametri contenuti nel file di preset.  
 * 
 * @author Ing. Alessandro Radaelli
 */
Ext.define('TolomeoExt.ToloTimeMachinePanel', {
	
		extend: 'Ext.ux.CarouselPanel',
		
		/** 
		 * @property {Object} paramsJS
		 * 
		 * 
		 */
		paramsJS : null,
		
		
		/** 
		 * @property {TolomeoExt.ToloViewerOLPanel} viewer 
		 * 
		 * 
		 */
		viewer: null,
		
		/**
		 * @property {Object} timeMachineToShow
		 * In caso di multiTimeMachine (this.paramsJS.mappe.mappaList[0].timeMachineList.length>1) indica quale deve essere visualizzata da questo pannello. Se non valorizzato per retrocampatibilità mostra this.paramsJS.mappe.mappaList[0].timeMachine 
		 * 
		 */
		timeMachineToShow: null,
		
		/**
		 * @property {Number} mapViewerRatio
		 * @private
		 * 
		 * 
		 */
		mapViewerRatio: null,
		
		/**
		 * @property {Number} [mapviewerWidth=0]
		 * @private
		 * 
		 * 
		 */
		mapviewerWidth: 0, 
		
		/**
		 * @property {Number} [mapviewerHeight=0]
		 * @private
		 * 
		 * 
		 */
		mapviewerHeight: 0,
		
		/**
		 * @property {Number} [mapviewerWidthWithRatio=0]
		 * @private
		 * 
		 * 
		 */
		mapviewerWidthWithRatio: 0,
		
		/**
		 * @property {Number} [mapviewerHeightWithRatio=0]
		 * @private
		 * 
		 * 
		 */
		mapviewerHeightWithRatio: 0,
		
		/** 
		 * @property {Array} [itemsList=[]]
		 * <pre>
		 * <br/>
		 * Esempio:<br/>
		 * 
		 * { tipo: '', url: '', styles: '', mappa: '', layer: '', formato: '', srid: '', testo: '' }
		 * 
		 * var itemList = [ { tipo: 'mapserver', url: "", styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto1954.map", layer: "FOTO1954", formato:"",	testo: "1954" },
	                 { url: "", styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto1978.map", layer: "FOTO1978", formato:"",	testo: "1978" },
	                 { tipo: 'mapserver', styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto1996.map", layer: "FOTO1996", formato:"",	testo: "1996" },
	                 { tipo: 'mapserver', url: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto2002.map", layer: "FOTO2002", formato:"",	testo: "2002" },
	                 { tipo: 'mapserver', url: "", styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto2003.map", layer: "FOTO2003", testo: "2003" },
	                 { tipo: 'mapserver', url: "", styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto2009.map", layer: "FOTO2009", formato:"",	testo: "2009" },
	                 { tipo: 'WMS', url: "http://geoserver.comune.prato.it/geoserver/wms", styles: "", mappa: "", layer: "comunepo:ortofoto2009", formato:"image/jpeg", srid:"EPSG:3003",	testo: "2009 geoserver" },
	                 { tipo: 'WMS', url: "http://web.rete.toscana.it/sgrwms/com.rt.wms.RTmap", styles: "", mappa: "", layer: "idsfondodtm50kcol", formato:"image/jpeg", srid:"EPSG:3003",	testo: "Hillshade Geoscopio" }  ];
	 		 * </pre>
		 * 
		 * {Object}[]
		 */
		itemsList: [],
		
		/**
		 * @method initComponent
		 * 
		 * 
		 */
		initComponent: function(){
			
			if (this.carouselConfig==null) this.carouselConfig = {};
			TolomeoExt.applyIfEmpty(this.carouselConfig, 
											{   interval: 3,
											    autoPlay: true,
											    showPlayButton: true,
											    pauseOnNavigate: true,
											    freezeOnHover: true,
											    transitionType: 'fade',
											    transitionEasing: 'fadeIn',    
											    navigationOnHover: false    
											});


			
			if (this.viewer) this.bindToViewer();
			if (this.paramsJS && this.itemsList.length==0) {
				// TODO gestire il caso nel quale la mappa selezionata non sia la 0 (del preset)
				this.itemsList = [];
				if (this.timeMachineToShow==null) {
					if (this.paramsJS.mappe.mappaList[0].timeMachine && this.paramsJS.mappe.mappaList[0].timeMachine.length>0) {
						this.itemsList = this.paramsJS.mappe.mappaList[0].timeMachine.layerList;
					}
				} else {
					this.itemsList = this.paramsJS.mappe.mappaList[0].timeMachineList[this.timeMachineToShow].layerList;
				}
			}
			this.callParent(arguments);
			
		},
		
		/**
		 * @method setMapViewerValues
		 * 
		 * 
		 * @param {Number} mapViewerRatio
		 * 
		 * 
		 * @param {Number} mapviewerWidth
		 * 
		 * 
		 * @param {Number} mapviewerHeight
		 * 
		 * 
		 */
		setMapViewerValues: function(mapViewerRatio, mapviewerWidth, mapviewerHeight) {
			
			if (mapViewerRatio)  this.mapViewerRatio  = mapViewerRatio;
			if (mapviewerWidth)  this.mapviewerWidth  = mapviewerWidth;
			if (mapviewerHeight) this.mapviewerHeight = mapviewerHeight;
			this.mapviewerWidthWithRatio  = Math.round(this.mapviewerWidth * ((this.mapViewerRatio) ? this.mapViewerRatio : 1));
			this.mapviewerHeightWithRatio = Math.round(this.mapviewerHeight * ((this.mapViewerRatio) ? this.mapViewerRatio : 1));
			this.calculateAutoOffset();
			this.autoOffset();
		}, 
		
		/**
		 * @method calculateAutoOffset
		 * 
		 * 
		 */
		calculateAutoOffset: function() {
			
			this.autoOffsetXAdjust= (this.mapViewerRatio) ? - this.mapviewerWidth/2 * (this.mapViewerRatio-1) : 0  ;
			this.autoOffsetYAdjust= (this.mapViewerRatio) ? - this.mapviewerHeight/2 * (this.mapViewerRatio-1) : 0  ;  
		}, 
		
		/**
		 * @method mapPanelSizeUpdate
		 * 
		 * 
		 */
		mapPanelSizeUpdate: function() {
			this.setMapViewerValues(null, this.viewer.getWidth(), this.viewer.getHeight());
		},
		
		/**
		 * @method getMapViewerValuesFromViewer
		 * 
		 * 
		 */
		getMapViewerValuesFromViewer: function() {
			
			this.mapViewerRatio =  this.viewer.pluginGetMapRatio();   // 1.5
			this.mapviewerWidth  = Math.round(this.viewer.pluginGetMapViewerWidth());
			this.mapviewerHeight = Math.round(this.viewer.pluginGetMapViewerHeight());
			this.mapviewerWidthWithRatio  =  Math.round(this.mapviewerWidth * ((this.mapViewerRatio) ? this.mapViewerRatio : 1));
			this.mapviewerHeightWithRatio = Math.round(this.mapviewerHeight * ((this.mapViewerRatio) ? this.mapViewerRatio : 1));
			
		},
		
		/**
		 * @method bindToViewer
		 * 
		 * 
		 * @param {Object} viewer
		 * 
		 * 
		 */
		bindToViewer: function(viewer) {
			var thisPanel=this;
			
			if (viewer) this.viewer = viewer;
			
			if (this.viewer) {
				// collegare ad un evento alla fine del quale siano disponibili i dati (postinit? afterrender?)
				var pos = this.viewer.getPosition();
				this.carouselFixedLeft = pos[0];
				this.carouselFixedTop  = pos[1];   
				
				this.getMapViewerValuesFromViewer();
				
				
				
				// registra evento nel caso cambi il layer selezionato nel layer switcher
				this.mon(this.viewer,'changebaselayer', 
									function() {
											this.getMapViewerValuesFromViewer();
											this.setMapViewerValues();
											}, this);
				
				// registrarsi per ricevere le modifiche di dimensioni del viewer
				this.mon(this.viewer, 'resize', this.mapPanelSizeUpdate, this);
				
				
				
				// TODO vedere funzione e contesto 
				this.mon(this.viewer, 'onMapMoveEnd', this.reloadItems, this);
			 	
			 	//this.on ('afterrender', function() {
			 	//this.viewer.on('move',  function() { alert('pippo'); /*this.setCarouselFixed*/}, this);
			 	

			 	this.on('draggableObjInitialized', 
			 				function(draggableObj) {
			 							
						 			draggableObj.dd.startDrag = draggableObj.dd.startDrag.createSequence(
																		function(){
																	
																			var pos = this.viewer.getPosition();
																			if ((this.carouselFixedLeft != pos[0]) || (this.carouselFixedTop  != pos[1])) {
																				var newCarouselFixedLeft = pos[0];
																				var newCarouselFixedTop  = pos[1];
																				thisPanel.setCarouselFixed(newCarouselFixedLeft, newCarouselFixedTop);	    
																			}
																		}, thisPanel);
									}, thisPanel);

			 
			 	
			 	
			}	
			
		},		
		
		/**
		 * @method loadItem
		 * <pre>
		 * Aggiunge un item alla lista. E' comunque possibile caricarla direttamente all'inizializzazione passando il parametro itemList.
		 * Esempio di utilizzo:
		 * 
		 * var itemList = [ { tipo: 'mapserver', url: "", styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto1954.map", layer: "FOTO1954", formato:"",	testo: "1954" },
	                 { url: "", styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto1978.map", layer: "FOTO1978", formato:"",	testo: "1978" },
	                 { tipo: 'mapserver', styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto1996.map", layer: "FOTO1996", formato:"",	testo: "1996" },
	                 { tipo: 'mapserver', url: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto2002.map", layer: "FOTO2002", formato:"",	testo: "2002" },
	                 { tipo: 'mapserver', url: "", styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto2003.map", layer: "FOTO2003", testo: "2003" },
	                 { tipo: 'mapserver', url: "", styles: "", mappa: "/usr1/test/vh/tolomeo/mapfiles/ortofoto2009.map", layer: "FOTO2009", formato:"",	testo: "2009" },
	                 { tipo: 'WMS', url: "http://geoserver.comune.prato.it/geoserver/wms", styles: "", mappa: "", layer: "comunepo:ortofoto2009", formato:"image/jpeg", srid:"EPSG:3003",	testo: "2009 geoserver" },
	                 { tipo: 'WMS', url: "http://web.rete.toscana.it/sgrwms/com.rt.wms.RTmap", styles: "", mappa: "", layer: "idsfondodtm50kcol", formato:"image/jpeg", srid:"EPSG:3003",	testo: "Hillshade Geoscopio" }
	                  ];
	
			timeMachinePnl =	new TolomeoExt.ToloTimeMachinePanel({
										viewer: tolomeoPnl.api.viewer,
										//itemsList: itemList,
										//carouselItems: ['1','2','3','4','5'],
										carouselConfig: {
											    interval: 3,
											    autoPlay: true,
											    showPlayButton: true,
											    pauseOnNavigate: true,
											    freezeOnHover: true,
											    transitionType: 'fade',
											    transitionEasing: 'fadeIn',    
											    navigationOnHover: false    
											}
										});
			
			for (var i=0; i<itemList.length; i++) {
				timeMachinePnl.loadItem(	itemList[i],
									(i==itemList.length-1)
								);
				}
				 	
			 var timeMachineWin = new Ext.Window({ title: "pp",  
						layout: 'fit',
						maximizable:true,
						collapsible:true,
						left:0, right:0,
						height: 200, width:200,
						autoScroll: true,
						renderTo: tolomeoPnl.api.viewer.body,
						items: [ timeMachinePnl
							] });
		 *</pre>
		 * map, layername, text,
		 * 
		 * @param {Object} item
		 * 
		 * 
		 * @param {Object} refresh
		 * 
		 * 
		 */
		loadItem: function(item, refresh) {
			//item
			this.itemsList.push(item);
			this.addImgItem ("" + Math.random(), 
							// map, layername
							this.viewer.pluginServerUrl(item, this.mapviewerWidthWithRatio, this.mapviewerHeightWithRatio), 
							item.testo, 
							refresh);
			
		},
		
		/**
		 * @method reloadItems
		 * svuota la lista degli item e la ricarica con gli stessi item, allo scopo di farli richiedere al server
		 * 
		 */
		reloadItems: function () {
		
			this.activeSlide =null;
			if (this.carousel) this.activeSlide = this.carousel.activeSlide;
			this.clearItems();
			var items = this.itemsList; 
			this.itemsList=[];
			
			var bDaSettareSlide = (this.carousel!=null && this.activeSlide!=null && this.activeSlide!=0);
			
			for (var i=0; i<items.length; i++) {
				this.loadItem( items[i], false); 	
			}
			this.refresh(this.activeSlide);
			/*if (bDaSettareSlide) {
				this.carousel.setSlide();
			}*/
			
		}
		
		
	});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * @class TolomeoExt.ToloTimeMachineMultiPanel
 * @extends Ext.Panel
 * Pannello ExtJs che implementa la funzionalità TimeMachine multipla
 * 
 * @param {Object} config
 * 
 * 
 * @param {Object} config.paramsJS
 * Oggetto i parametri contenuti nel file di preset.
 * 
 * @author Ing. Alessandro Radaelli
 */
Ext.define('TolomeoExt.ToloTimeMachineMultiPanel', {
	
		extend: 'Ext.Panel',

		/** 
		 * @property {Object} paramsJS
		 * 
		 * 
		 */
		paramsJS : null,
		
		
		/** 
		 * @property {TolomeoExt.ToloViewerOLPanel} viewer 
		 * 
		 * 
		 */
		viewer: null,
			
		/** 
		 * @property {Object} voices
		 * 
		 * 
		 */
		voices: null,
			
		/** 
		 * @property {Object} timeMachinePanels
		 * 
		 * 
		 */
		timeMachinePanels: null,
		
		/** 
		 * @property {Object} draggableParent
		 * 
		 * 
		 */
		draggableParent: null,
		
		/** 
		 * @method initComponent
		 * 
		 * 
		 */
		initComponent: function(){
			
			this.frame= false;
			
			this.timeMachinePanels = [];
			for (var i = 0; i<this.paramsJS.mappe.mappaList[0].timeMachineList.length; i++) {
				this.timeMachinePanels.push(null);
			}
			
			//this.tbar = { items: this.createMenuVoices() };
			//this.contextMenu = this.createContextMenus();
			
			this.layout='fit';
			this.callParent(arguments);
			
			//this.setDraggableObj(ownerCt);
			this.on('added',  function(thisComponent, ownerCt, index) { 
												if (ownerCt.draggable) this.draggableParent= ownerCt;
												this._layoutTmPanels(1);
												/*for (var i=0; i < this.timeMachinePanels.length; i++) {
													this.timeMachinePanels[i].setDraggableObj(this.draggableParent);
												}*/
										},  
										this, 
										{single: true});
			

		}, 
		
		/** 
		 * @method _layoutTmPanels
		 * 
		 * 
		 * @param {Object} tipoLayout
		 * 
		 * 
		 */
		_layoutTmPanels: function(tipoLayout) {
			
			switch (tipoLayout) {
			case 1: 
				var tabsContents = [];
				for (var i = 0; i<this.paramsJS.mappe.mappaList[0].timeMachineList.length; i++) {
					this.timeMachinePanels[i] = this.createNewTimeMachinePanel(i); 
					tabsContents.push({ title: this.paramsJS.mappe.mappaList[0].timeMachineList[i].nome,
										layout: 'fit',
										items: [this.timeMachinePanels[i]]});
				}
				
				//activeItem: 0,
				//
				//{ items: [{ text: 'aaa'}, ], { text: 'aaa'} }
				var tabs = new Ext.TabPanel({ activeItem: 0, items: tabsContents });
				this.add(tabs);
				this.doLayout();
				
				this.on('afterlayout', function() {
					for (var i=0; i < tabsContents.length; i++) {
						this.timeMachinePanels[i].calculateAutoOffset();
						this.timeMachinePanels[i].reloadItems();
					}
				}, this);
				
				//afterrender
				tabs.on('tabchange', function(tabpanel, tab) {
					var tm = tab.items.items[0];
					//for (var i=0; i < tabsContents.length; i++) {
					tm.setDraggableObj(this.draggableParent);
					tm.calculateAutoOffset();
					tm.reloadItems();
					
					//this.timeMachinePanels[i].setDraggableObj(this.draggableParent);
					//this.timeMachinePanels[i].calculateAutoOffset();
					//this.timeMachinePanels[i].reloadItems();
					//}
				}, this); //, {single:true}
				
				break;
			case 2: 
				break;
			
			}
		},
		
		/** 
		 * @method cambiaTimeMachine
		 * 
		 * 
		 * @param {Number} index
		 * 
		 * 
		 */
		cambiaTimeMachine: function(index){

			this.timeMachinePanels[index]=this.createNewTimeMachinePanel(index);
			
			this.removeAll(true);
			this.add(this.timeMachinePanels[index]);
			this.doLayout();
			this.timeMachinePanels[index].calculateAutoOffset();
			this.timeMachinePanels[index].reloadItems();
		},
		
		/** 
		 * @method createNewTimeMachinePanel
		 * 
		 * 
		 * @param {Object} timeMachineToShow
		 * 
		 * 
		 */
		createNewTimeMachinePanel: function(timeMachineToShow) {

			var pnl = new TolomeoExt.ToloTimeMachinePanel({
							aaaa: timeMachineToShow,
							viewer: this.viewer,
							paramsJS: this.paramsJS,
							cls: 'clsTimeMachinePanel',
							timeMachineToShow: timeMachineToShow,
							autoDraggableDetect: false
					});

			pnl.on('afterrender', 
					function() {
						var el = pnl.getEl();
						//pnl.getEl().on('contextmenu', function( e, t, o) { e.stopEvent(); this.contextMenu.showAt(e.getXY());}, this);

						if (this.draggableParent!=undefined && this.draggableParent!=null) {
							pnl.setDraggableObj(this.draggableParent);
						}
						
					}, this, {single: true});
			
			//this.on('added',  function(thisComponent, ownerCt, index) { this.setDraggableObj(ownerCt); },  this.pnl, {single: true});
			//pnl.calculateAutoOffset();
        	//pnl.reloadItems();
        	
			return pnl;
		}, 
		
		/** 
		 * @method createContextMenus
		 * 
		 * 
		 */
		createContextMenus: function() {
			var voices = this.createMenuVoices();
			var ctxMenu = new Ext.menu.Menu({
								items: voices
								//menu: {
								//		items: voices
								//	}
								});
			return ctxMenu;
		},
		
		/** 
		 * @method createMenuVoices
		 * 
		 * 
		 */
		createMenuVoices: function() {
			//if (this.voices!=null) return this.voices;
			
			this.voices = [];
			
			// Scelta timemachine
			var store = new Ext.data.JsonStore({
	        	//id: 0,
	        	fields: [
	        	         'nome', 'layerList'],
	        	 data: this.paramsJS.mappe.mappaList[0].timeMachineList
	        	 //[[1, 'item1'], [2, 'item2']]
	    	});
			this.cmbScelta = new Ext.form.ComboBox({
							store: store,
							//valueField: 'myId',
						    displayField: 'nome',
						    forceSelection: true,
						    editable: false,
						    autoSelect: true,
						    typeAhead: true,
						    triggerAction: 'all',
						    fieldLabel: ToloI18n.getMsg("ToloTimeMachineMultiPanel.cmbScelta"),
						    //lazyRender:false,
						    mode: 'local',
						    listeners: {
						    	select: { fn: function( combo, record, index) { this.cambiaTimeMachine(index)}, scope: this}
						    }
		
			});
			this.voices.push(this.cmbScelta);
			
			this.voices.push({
					//xtype: 'menuitem',
					text: ToloI18n.getMsg("ToloTimeMachineMultiPanel.layout"),
					menu: {
						items: [
								{
								 	checked: true, 	
									text: ToloI18n.getMsg("ToloTimeMachineMultiPanel.toolbar"),
									listeners: {
										click: { fn: function(thisBtn, e) { if (thisBtn.checked) {
																		this.getTopToolbar().hide();
																		//thisBtn.setChecked(false);
																	} else {
																		this.getTopToolbar().show();
																		//thisBtn.setChecked(true);
																	}},
												  scope: this
												}
									}
								}, '-', {
						        	text: ToloI18n.getMsg("ToloTimeMachineMultiPanel.singolo")
						        },{
						        	text: ToloI18n.getMsg("ToloTimeMachineMultiPanel.tab")
						        }]
					}
					
			});
			
			return this.voices;
			
		}
				
	});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Iframe in cui vengono caricate le risorse informative esterne.<br/>
 * ATTENZIONE: se questo pannello viene inserito in contenitori che sono collapsible ed hanno animCollapse=true ad ogni apertura e 
 * 	chiusura del pannello il contenuto dell'iframe viene ricaricato, o più precisamente viene ricaricata url se definita alla creazione, altrimenti resta vuoto.
 *  Questo comportamento è molto pericoloso in caso di compilazione di form ed editing, per evitarlo accertarsi che i contenitori "padri" collapsible abbiano <br/>
 *  
 * - animCollapse: false<br/>
 * - floatable : false <br/>	
				
 * @class TolomeoExt.ToloIFrame
 * @extends Ext.BoxComponent
 */
Ext.define('TolomeoExt.ToloIFrame', {

	extend: 'Ext.Component',
	alias: 'widget.tx_toloIFrame',

	/** 
	 * @property {String} [url='']
	 * url da caricare nell'iframe
	 * 
	 */
	url: '',
	
	/** 
	 * @property {String} [iFrameName='pannello']
	 * Nome dell'iframe da utilizzare poi come target
	 * 
	 */
	iFrameName: 'pannello',
	
	/** 
	 * @property {String} iFrameId
	 * Id dell'iframe da utilizzare per recuperare l'elemento
	 * 
	 */
	iFrameId: null,
	
	/** 
	 * @property {String} iFrameTagId
	 * 
	 * 
	 */
	iFrameTagId: null, 	 	 
	
	/** 
	 * @method initComponent
	 * 
	 * 
	 */
	initComponent: function() {		

	/* TODO PLUGIN????
		var initializeVisModePlugin = true;
		this.plugins = this.plugins || []; 
		
		// Se è stato passato uno specifico plugin lo aggiungo nell'array dei plugins prima 
		// di aggiungere quello per il VisibilityMode
		if(!(this.plugins instanceof Array)){
			var obj = this.plugins;
			this.plugins = [];
			this.plugins.push(obj);			
		}		
		
		// Controllo se il plugin è già stato passato da fuori
		for(var i=0; i < this.plugins.length; i++){
			if(this.plugins[i] instanceof Ext.ux.plugin.VisibilityMode){
				initializeVisModePlugin = false;
				break;
			}
		}
		
		if(initializeVisModePlugin && !Ext.isIE){
			this.plugins.push(new Ext.ux.plugin.VisibilityMode({ bubble : true }));
		}		
		*/

		this.iFrameTagId = this.iFrameId ? this.iFrameId : 'iframe-' + this.iFrameName;
		
		this.autoEl = {tag: 'iframe', id: this.iFrameTagId, name: this.iFrameName, frameBorder: 0, src: this.url};
		
		// Applico i default
		//TODO RIATTIVARE TolomeoExt.Vars.ApplyIfDefaults(this);		
		this.callParent(arguments);
		
		//this.on('beforecollapse', this.copyIFrame);
		//this.on('hide', this.copyIFrame);
		
		//this.on('expand', this.restoreIFrame);
		//this.on('show', this.restoreIFrame);
		
	},
	
	/** 
	 * @method loading
	 * 
	 * 
	 * @param {Object} config
	 * 
	 * 
	 */
	loading: function(config){
		var frame = top.frames[this.iFrameName];
		var message = null;
		var htmlTemplate = config.htmlTemplate || "<html><head></head><body><p style='{1}'>{0}</p></body></html>";
		
		if(config.url){
			frame.location.replace(config.url);
			if(config.message){
				message = config.message;
			}
		}else{
			message = config.message || ToloI18n.getMsg("ToloIFrame.caricamento");
		}
		if(message){
			frame.document.open();
			frame.document.write(String.format(htmlTemplate,message,config.style));
	    	frame.document.close();
		}
	},
	
	/** 
	 * @method replaceUrl
	 * 
	 * 
	 * @param {String} url
	 * 
	 * 
	 * @param {Object} loadingConfig
	 * 
	 * 
	 */
	replaceUrl: function(url,loadingConfig){
		if(!url) return;
		if(loadingConfig){
			this.loading(loadingConfig);
		}
		top.frames[this.iFrameName].location.replace(url);
	},
	
	/** 
	 * @method changeUrl
	 * 
	 * 
	 * @param {Object} loadingConfig
	 * 
	 * 
	 */
	changeUrl: function(loadingConfig){
		if(!url) return;
		if(loadingConfig){
			this.loading(loadingConfig);
		}
		top.frames[this.iFrameName].location.href = url;
	}
	
	/*
	copyIFrame: function() {
		//this.elCopy = Ext.get(this.iFrameTagId).dom;
		if (this.getEl().dom) this.elCopy = Ext.clone(this.getEl().dom);
		//this.saveState();
	},
	
	
	restoreIFrame: function() {
		//Ext.get(this.iFrameTagId).dom = this.elCopy;
		if (this.elCopy) this.getEl().dom = this.elCopy;
	}
	*/
	
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * @class TolomeoExt.ToloTOCInfo
 * @extends Ext.util.Observable
 * Inizialmente alimentato con la deserializzazione di TOCBean contiene lo stato della legenga ed i metodi per interagire con tale stato 
 * 
 */

Ext.define('TolomeoExt.ToloTOCInfo', {

	extend: 'Ext.util.Observable',
	
	/**
	 * @property {Object} presetLegenda
	 * 
	 * 
	 */
	presetLegenda: null,
	
	/**
	 * @property {Object} [CATEGORIESARRAYNAME="categories"]
	 * 
	 * 
	 */
	CATEGORIESARRAYNAME: "categories",

	/**
	 * @constructor TolomeoExt.ToloTOCInfo
	 * Create a new TolomeoExt.ToloTOCInfo
	 * 
	 * @param {Object} config
	 * La configurazione
	 *  
	 * @returns {TolomeoExt.ToloTOCInfo}
	 * A new TolomeoExt.ToloTOCInfo
	 * 
	 */
	constructor: function(config) {

		Ext.apply(this, config);
		this.callParent(arguments);
		
		
		this.addEvents("catTreeIdxUpdate");
		this.addEvents("layTreeIdxUpdate");
		
	},

	/**
	 * @method _getCategoryInfoPriv
	 * @private
	 * Metodo privato per lo scorrimento ricorsivo delle categorie
	 * 
	 * @param {Object} catInfo
	 * 
	 * 
	 * @param {Array} idxs
	 * 
	 * 
	 * @param {Object} catArrayName
	 * 
	 * 
	 */
	_getCategoryInfoPriv: function(catInfo, idxs, catArrayName) {
		
		if (idxs.length==1) return catInfo[catArrayName][idxs[0]];
		else {
			var idx = idxs.shift();
			return this._getCategoryInfoPriv(catInfo[catArrayName][idx], idxs, catArrayName);
		}
	},
	
	/**
	 * @method getCategoryInfo
	 * Metodo per recuperare le info della categoria a partire dall'indice (che è complesso nella forma 0/1/...)
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 */
	getCategoryInfo: function(catIdx) {
		
		var idxs = catIdx.split(this.TOCCATSEPARATOR);
		return 	this._getCategoryInfoPriv(this, idxs, this.CATEGORIESARRAYNAME);
	
	},
	
	/**
	 * @method getCategoryPresetInfo
	 * Metodo per recuperare le info della categoria contenute nel preset a partire dall'indice (che è complesso nella forma 0/1/...)M
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 */
	getCategoryPresetInfo: function(catIdx) {
		
		if (this.presetLegenda==null) return null;
		
		var idxs = catIdx.split(this.TOCCATSEPARATOR);
		return 	this._getCategoryInfoPriv(this.presetLegenda, idxs, "categoryList");
	},
	
	/**
	 * @method onEachCategory
	 * Metodo per il lancio della funzione fn all'interno del contesto scope per ogni categoria a partire da quella con indice startCatIdx. 
	 * Il metodo agisce in maniera ricorsiva se bRecurce non è definito o è true 
	 * La funzione fn viene chiamata passando gli argomenti cat ecatIdx  
	 * 
	 * @param {Object} fn
	 * 
	 * 
	 * @param {Object} scope
	 * 
	 * 
	 * @param {Object} startCatIdx
	 * 
	 * 
	 * @param {Object} bRecurse
	 * 
	 * 
	 */
	onEachCategory: function(fn, scope, startCatIdx, bRecurse) {
		var scopebuff = (scope) ? scope : this;
		var bRecurse = (bRecurse!=undefined && bRecurse !=null) ?  bRecurse : true;
		
		if (!startCatIdx) {
			if (this[this.CATEGORIESARRAYNAME].length!=0) {
				for (var i=0; i<this[this.CATEGORIESARRAYNAME].length; i++) {
					this.onEachCategory(fn, scope,""+i, bRecurse);
				}
				return;
			} else {
				return;
			}
		} 
		
		var scIdx = startCatIdx;
		
		var sc = this.getCategoryInfo(startCatIdx);

		fn.call(scope, sc, scIdx);
		 
		// categorie annidate
		if (bRecurse && sc.categories) {
			for (var i=0; i<sc.categories.length; i++) {
				var scIdxBuff = scIdx + this.TOCCATSEPARATOR + i;
				this.onEachCategory(fn, scope, scIdxBuff);
			}
		}
	},
	
	/**
	 * @method onEachLayer
	 * Metodo per il lancio della funzione fn all'interno del contesto scope per ognilayer contenuto in ogni categoria a partire da quella con indice startCatIdx. 
	 * Il metodo agisce in maniera ricorsiva se bRecurce non è definito o è true.
	 * La funzione fn viene chiamata passando gli argomenti cat, lay, catIdx e layIdx 
	 * 
	 * @param {Object} fn
	 * 
	 * 
	 * @param {Object} scope
	 * 
	 * 
	 * @param {Object} startCatIdx
	 * 
	 * 
	 * @param {Object} bRecurse
	 * 
	 * 
	 */
	onEachLayer: function(fn, scope, startCatIdx, bRecurse) {
		
		this.onEachCategory(
			function(cat, catIdx) {
				if (cat.layers) {
					for (var i=0; i<cat.layers.length; i++) {
						fn.call(scope, cat, cat.layers[i], catIdx, i);
					}
				}
			},
			this,
			startCatIdx,
			bRecurse
		);
		
	},
	
	/**
	 * @method getParentCategoryIdx
	 * Metodo per ottenere l'indice della categoria padre 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 */
	getParentCategoryIdx: function(catIdx) {
		
		if (!catIdx) return "";
		
		var idx = catIdx.lastIndexOf(this.TOCCATSEPARATOR);
		
		if (idx==-1) return "";
		else {
			return catIdx.substring(0, idx);
		}
		
	},
	
	/**
	 * @method onEachParentCategory
	 * Metodo per il lancio della funzione fn all'interno del contesto scope per ogni categoria padre a partire da quella con indice startCatIdx. 
	 * Il metodo agisce in maniera ricorsiva se bRecurce non è definito o è true.
	 * La funzione fn viene chiamata passando gli argomenti cat, catIdx  
	 * 
	 * @param {Object} fn
	 * 
	 * 
	 * @param {Object} scope
	 * 
	 * 
	 * @param {Object} startCatIdx
	 * 
	 * 
	 */	
	onEachParentCategory: function(fn, scope, startCatIdx) {
		
		var parentIdx = this.getParentCategoryIdx(startCatIdx);
		while (parentIdx!="") {
			var cat = this.getCategoryInfo(parentIdx);
			fn.call(scope, cat, parentIdx);
			parentIdx = this.getParentCategoryIdx(parentIdx);
		}
		
	},
	
	/**
	 * @method areAllParentCatChecked
	 * Metodo che verifica se tutte le categorie padre hanno il flag checked==true
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 */
	areAllParentCatChecked: function(catIdx) {
		var retObj1 = { bAllChecked: true };
		this.onEachParentCategory(
				function(parentCat, parentCatIdx) {
					if (!parentCat.checked) this.bAllChecked = false;
				}, 
				retObj1,
				catIdx
		);
		return retObj1.bAllChecked;
	},
	
	/**
	 * @method setLayerNeedRequestVisibleData
	 * Metodo per settare a value il flag needRequestVisibleData del layer individuato dalla coppia catIdx, layIdx 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 * @param {Object} value
	 * 
	 * 
	 */
	setLayerNeedRequestVisibleData: function(catIdx, layIdx, value) {
		this.getCategoryInfo(catIdx).layers[layIdx]._needRequestVisibleData = value;
	},
	
	/**
	 * @method getLayerNeedRequestVisibleData
	 * Metodo recuperare il valore del needRequestVisibleData del layer individuato dalla coppia catIdx, layIdx 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 */
	getLayerNeedRequestVisibleData: function(catIdx, layIdx) {
		var val = this.getCategoryInfo(catIdx).layers[layIdx]._needRequestVisibleData;
		
		return (val!=undefined && val!=null) ? val : false;
	},
	
	/**
	 * @method getLayerBoundingBox
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 */
	getLayerBoundingBox: function(catIdx, layIdx) {
		var layer = this.getCategoryInfo(catIdx).layers[layIdx];		
		
		if (layer.bboxMinX!=0 && layer.bboxMinY!=0 && layer.bboxMaxX!=0 && layer.bboxMaxY != 0) {
			var bbox = new BBox(layer.bboxMinX, layer.bboxMinY, layer.bboxMaxX, layer.bboxMaxY);
			return bbox
			
		} else {
			return null;
		}
	}, 
	
	
	/**
	 * @method getCategoryBoundingBox
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 */
	getCategoryBoundingBox: function(catIdx) {
		
		var bboxTot = null;
		
		this.onEachLayer( 
				function(cat, lay, catIdx, layIdx) {
					var bbox = this.getLayerBoundingBox(catIdx, layIdx);
					if (bbox!=null) {
						if (bboxTot==null) {
							bboxTot = new BBox(bbox.left,bbox.bottom,bbox.right,bbox.top);
						} else { 
							bboxTot.bottom = Math.min(bboxTot.bottom, bbox.bottom);
							bboxTot.left = Math.min(bboxTot.left, bbox.left);
							bboxTot.top = Math.max(bboxTot.top, bbox.top);
							bboxTot.right = Math.min(bboxTot.right, bbox.right);
						}
					}
				},
				this, 
				catIdx, true);
		
		return bboxTot;
		
	}, 
	
	/**
	 * @method getBoundingBox
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 */
	getBoundingBox: function(catIdx, layIdx) {
		if (layIdx==null) return this.getCategoryBoundingBox(catIdx);
		else return this.getLayerBoundingBox(catIdx, layIdx);
	}, 
	
	/**
	 * @method getServer
	 * 
	 * 
	 * @param {Object} serverId
	 * 
	 * 
	 */
	getServer: function (serverId) {
		return this.server[serverId]; 
	},
	
	/**
	 * @method searchLayerInfo
	 * 
	 * 
	 * @param {Object} serverID
	 * 
	 * 
	 * @param {Object} name
	 * 
	 * 
	 */
	searchLayerInfo: function(serverID, name) {
		var retVal = null;
		
		this.onEachLayer( 
				function(cat, lay, catIdx, layIdx) {
					if(retVal) return;
					// TODO SERVER IMPLICITO
					var serverIdToCompare = (serverID == "INLINESERVERID") ? "" : serverID;  
										
					if (lay.name==name && (!serverID || lay.serverID==serverIdToCompare)) {
						retVal = lay;
					}						
					
				},
				this);
				
		return retVal;
	}, 
	
	/**
	 * @method searchLayerInfoByDesc
	 * 
	 * 
	 * @param {Object} desc
	 * 
	 * 
	 * @param {Object} itemType
	 * 
	 * 
	 */
	searchLayerInfoByDesc: function(desc, itemType) {
		var retVal = null;
		
		this.onEachLayer( 
				function(cat, lay, catIdx, layIdx) {
					if (lay.descr == desc && lay.itemType == itemType) {
						retVal = lay;
					}
				},
				this);
				
		return retVal;
	}, 
	
	/**
	 * @method addCategory
	 * Aggiunge una categoria nella posizione indicata dai parametri.
	 * 
	 * @param {Object} catInfo
	 * Categoria da aggiungere
	 * 
	 * @param {Object} addPointCatIdx
	 * categoria prima o dopo della quale va aggiunta la roba
	 * 
	 * @param {Object} bBefore
	 * indica se aggiungere prima o dopo 
	 * 
	 */
	addCategory: function(catInfo, addPointCatIdx, bBefore) {
		
		// Cerca addPoint
		var addPoint = this.getCategoryInfo(addPointCatIdx);
		
		// Cerca il parent
		var addPointParentIdx = this.getParentCategoryIdx(addPointCatIdx);
		var addPointParent = null;
		
		// Se parent == null sono sul primo livello
		if (addPointParentIdx == "") {
			addPointParent = this;
		} else {
			addPointParent = this.getCategoryInfo(addPointParentIdx);
		}

		var c = (addPointParent.catTreeIdx) ? addPointParent.catTreeIdx : ""; 
				
		// Calcolo la posizione di inserimento
		var idxs = addPointCatIdx.split(this.TOCCATSEPARATOR);
		var pos = parseInt(idxs[idxs.length-1]) + ((bBefore) ? 0 : 1);
		
		// Aggiorno indice cat
		catInfo.catTreeIdx = c + ((c!="") ?  this.TOCCATSEPARATOR : "") + pos; 
		
		//Inserisco nella giusta posizione
		if (pos > 0) {
			addPointParent.categories.splice(pos, 0, catInfo);
		} else {
			addPointParent.categories.unshift(catInfo);
		}
		
		// rinumero indici
		this.updateIdxs(addPointParent, c);
		
	},
	
	/**
	 * @method updateIdxs
	 * Aggiorna tutti gli indici catTreeIdx e layTreeIdx.
	 * 
	 * @param {Object} cat
	 * categoria dalla quale iniziare ad aggiornare gli indici
	 * 
	 * @param {Object} newCatTreeIdx
	 * nuovo valore di treeIdx
	 * 
	 */
	updateIdxs: function(cat, newCatTreeIdx) {
		
		if (newCatTreeIdx!="") {
			// Aggiorna questa categoria
			var oldCatTreeIdx = cat.catTreeIdx;
			cat.catTreeIdx = newCatTreeIdx;
			if (oldCatTreeIdx != newCatTreeIdx) {
				this.fireEvent("catTreeIdxUpdate", cat.catId, oldCatTreeIdx, newCatTreeIdx);
			}
		
			// Aggiorna tutti i layer
			var layArray = cat.layers;
			if (layArray) {
				for (var i=0; i<layArray.length; i++) {
					layArray[i].catTreeIdx = newCatTreeIdx;
					if (oldCatTreeIdx != newCatTreeIdx) {
						// Utilizza i il nuovo catTreeIdx perchè ha già rinumerato la categoria con l'evento precedente
						this.fireEvent("layTreeIdxUpdate", layArray[i].layId, oldCatTreeIdx, layArray[i].layTreeIdx, newCatTreeIdx, layArray[i].layTreeIdx);
					}
				}
			}
		} 
		
		// Cicla sulle categorie figlie
		var catArray = cat.categories;
		if (catArray) {
			for (var i=0; i<catArray.length; i++) {
				var newPrefix = newCatTreeIdx + ((newCatTreeIdx != "") ? this.TOCCATSEPARATOR : "") + i ;				
				this.updateIdxs(catArray[i],newPrefix);		
			}
		}
		
	},
	
	/**
	 * @method addLayer
	 * Aggiunge un layer nella posizione indicata dai parametri.
	 * 
	 * @param {Object} layInfo
	 * Layer da aggiungere
	 * 
	 * @param {Object} addPointCatIdx
	 * Indice della categoria nella quale viene aggiunto il layer 
	 * 
	 * @param {Object} addPointLayIdx
	 * Indice layer prima o dopo del quale aggiungere il nuovo layer
	 * 
	 * @param {Object} bBefore
	 * indica se aggiungere prima o dopo
	 * 
	 */
	addLayer: function(layInfo, addPointCatIdx, addPointLayIdx, bBefore) {
		// Cerca addPoint
		var addPoint = this.getCategoryInfo(addPointCatIdx);
		
		// Calcolo la posizione di inserimento
		var pos;
		if (addPointLayIdx) {
			pos = parseInt(addPointLayIdx) + ((bBefore) ? 0 : 1);
		} else { // se non è definito addPointLayIdx inserire come primo elemento della categoria
			pos = addPoint.layers.length-1;
		}
		
		// Aggiorno indice cat
		layInfo.catTreeIdx = addPointCatIdx; 
		layInfo.layTreeIdx = "" + pos;
		
		//Inserisco nella giusta posizione
		if (pos > 0) {
			addPoint.layers.splice(pos, 0, layInfo);
		} else {
			addPoint.layers.unshift(layInfo);
		}
		
		// rinumero indici
		
		for (var i=0; i<addPoint.layers.length; i++) {
			var l = addPoint.layers[i];
			var oldLayTreeIdx = l.layTreeIdx
			l.layTreeIdx = i;
			if (oldLayTreeIdx!=i) { 
				this.fireEvent("layTreeIdxUpdate", l.layId, addPointCatIdx, oldLayTreeIdx, addPointCatIdx, l.layTreeIdx);
			}
		}
	},
	
	/**
	 * @method JSONEncodeInfo
	 * 
	 * 
	 * @param {Object} catIdx
	 * 
	 * 
	 * @param {Object} layIdx
	 * 
	 * 
	 */
	JSONEncodeInfo: function(catIdx, layIdx) {
		
		var info = {
			TOCCATSEPARATOR: this.TOCCATSEPARATOR,
			categories: this.categories,
			server: this.server
		}
		
		return Ext.JSON.encode(info);
	}
	
	
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/
// @include "../tolomeoExt/include.js"

Ext.namespace("TolomeoExt");


/**
 * @class TolomeoExt.ToloIIPPanel
 * @extends Ext.Panel
 * Pannello per contenere un viewer per IIPImage. In funzione del valore della variabile di configurazione tipo sono utilizzati: <br/>
 * tipo='iipzoom'
 * tipo='iipmooviewer'
 * in caso di utilizzo di iipmooviewer includere i seguenti css
 * <pre>
 * <link rel="stylesheet" type="text/css" media="all" href="<%=tolomeoServer %>/js/ext/iipmooviewer-2.0/css/iip.compressed.css">
 * <!--[if lt IE 10]>
 * <link rel="stylesheet" type="text/css" media="all" href="<%=tolomeoServer %>/js/ext/iipmooviewer-2.0/css/ie.compressed.css" />
 * <![endif]-->
 * </pre>
 * <br />
 * Esempio di utilizzo
 * 
 *  <pre>
   new TolomeoExt.ToloIIPPanel({ ///TolomeoProxyServlet?
	    	title: 'Anteprima',
			proxy: '/tolomeobinj/TolomeoProxyServlet?',
			// TODO generalizzare
			iipserver:  "http://localhost:80/fcgi-bin/iipsrv.fcgi",
			iipimage:  "/opt/grandeformato/vips.tif",
			copyright: "copyright or information message",
			tipo: 'iipzoom', //'iipzoom' 'iipmooviewer'
			api: this.api,
			
			region: 'east',
			width: 250,
			split:true,
			collapsible:true,
			renderTo: 'mydiv'
	    });
	    
	</pre>
 *
 */
Ext.define('TolomeoExt.ToloIIPPanel', {

	extend: 'Ext.Panel',
 

	/** 
	 * @cfg {String} TOLOMEOServer
	 * 
	 * 
	 */
	TOLOMEOServer: null,

	/** 
	 * @cfg {String} TOLOMEOContext
	 * 
	 * 
	 */
	TOLOMEOContext: null,
	
	/** 
	 * @cfg {String} TOLOMEOStaticRoot
	 * 
	 * 
	 */	
	TOLOMEOStaticRoot: null, 
	
	/**
	 * @cfg {Object} viewerPanel
	 * 
	 * 
	 */
	viewerPanel: 	null, 

	/**
	 * @cfg {String} iipserver
	 * Indirizzo del server IIPImage (per esempio "http://localhost/fcgi-bin/iipsrv.fcgi" )  
	 * 
	 */
	iipserver: 		null,
	
	/**
	 * @cfg {String} iipimage
	 * Immagine da caricare all'avvio. Per esempio "/opt/grandeformato/vips.tif"  
	 * 
	 */
	iipimage: 		null,
	
	/**
	 * @cfg {String} copyright
	 * testo del copyright da inserire nell'immagine  
	 * 
	 */
	copyright: 		"",
	
	/**
	 * @cfg {String} proxy
	 * Il server iipimage deve essere raggiunto dal javascript, per evitare problemi di Cross Domain Ajax Call 
	 * potrebbe essere necessario impostare un proxy. E' possibile utilizzare la servlet di proxy fornita con tolomeo impostando questo valore a
	 * '/tolomeobinj/TolomeoProxyServlet?' ed inserendo il server iipimage nel file tolomeo.properties in PROXY.HOSTNAME_WHITELIST   
	 * N:B: Questa impostazione è valida solo per visualizzatore iipmooviewer
	 * 
	 */
	proxy:			null,
	
	/**
	 * @cfg {String} api
	 * Oggetto API di tolomeo  
	 * 
	 */
	api: 			null, 
	
	/**
	 * @cfg {String} tipo
	 * tipo di viewer. Sono possibili i valori iipzoom (visualizzatore flash) e iipmooviewer (visualizzatore HTML5 e javascript)  
	 * 
	 */
	tipo: 			'iipzoom',
	
	/**
	 * @cfg {String} iipviewerId
	 * 
	 * 
	 */
	iipviewerId: null,
	
	/**
	 * @cfg {String} iipmooviewer
	 * 
	 * 
	 */
	iipmooviewer: null, 
	
	/**
	 * @method initComponent
	 * 
	 * 
	 */
	initComponent: function(){
		
		var thisPanel=this;
		this.layout = 'fit';
		
		TolomeoExt.Vars.ApplyIfDefaults(this);	
		
		this.callParent(arguments);
		
		this.iipviewerId=this.id+'-iipviewer';
		
        this.viewerPanel = new Ext.Panel({	
        	style: 'z-index:0;',
			layout: 'fit',	
			bodyBorder: false,
			border: false,
			anchor: '100% 100%',
			html: '<div id="' + this.iipviewerId + '" style="width:99%;height:99%;margin-left:auto;margin-right:auto" />', 
			monitorResize: true
		});
        this.add(this.viewerPanel);
		
		this.on('afterlayout', function() { 
			this.showViewer();
		}, this, {single: true}); 

		this.doLayout();

    },
    
    /**
     * @method showViewer
     * @private
     * 
     * 
     */
    showViewer: function() {
		
    	var iipopt = {
			server: this.iipserver, // this.iipserver,
			image: this.iipimage,
			credit: this.copyright
    	};
    	
    	var flashOnlyOpts = {
    		navigation: true
    	}

    	var iipmoo1OnlyOpts = {
    	   zoom: 1,
    	   render: 'random',
           showNavButtons: true,
			//prefix: this.TOLOMEOServer + this.TOLOMEOStaticRoot + 'js/ext/iipmooviewer-2.0/images/',
           prefix: this.TOLOMEOServer + '/js/ext/iipmooviewer-2.0/images/',
			enableFullscreen: true
    	}
    	
		var params = {
			scale: "noscale",
			bgcolor: "#000000",
			allowfullscreen: "true",
			allowscriptaccess: "always"
		}
    	
		switch (this.tipo){
			case 'iipzoom':
				Ext.apply(iipopt, flashOnlyOpts);
				
				this.api.lazyLoadScript('swfobject',
						function() { this.embedIIPZoom(Ext.apply(iipopt, flashOnlyOpts) , params);}, 
						function(){
							Ext.Msg.alert(ToloI18n.getMsg("ToloIIPPanel.problemaLibrerie.alert.title"),
										ToloI18n.getMsg("ToloIIPPanel.problemaLibrerie.alert.msg"));},
						this
					);
				break;
			case 'iipmooviewer':
				iipopt.server = ((this.proxy!=null && this.proxy!="") ? this.proxy : "") + iipopt.server;
				Ext.apply(iipopt, iipmoo1OnlyOpts);
				
				this.api.lazyLoadScript(['mootoolscore145', 'mootoolsmore1401', 'iipmooviewer20'],
						function() { this.embedIIPMooviewer(iipopt); }, 
						function(){
							Ext.Msg.alert(ToloI18n.getMsg("ToloIIPPanel.problemaLibrerie.alert.title"),
										ToloI18n.getMsg("ToloIIPPanel.problemaLibrerie.alert.msg"));},
						this
					);
				break;
		}
    },
    
    /**
     * @method setAndShowNewImg
     * Imposta nuova immagine, server e copyright e visualizza
     * 
     * @param {String} iipimage
     * 
     * 
     * @param {String} iipserver
     * 
     * 
     * @param {String} copyright
     * 
     * 
     */
    setAndShowNewImg: function (iipimage, iipserver, copyright) {
    	
    	if (iipimage && iipimage!="") this.iipimage=iipimage;
    	if (iipserver && iipserver!="") this.iipserver=iipserver;
    	if (copyright && copyright!="") this.copyright=copyright;
    	
    	this.showViewer();
    	
    },
    
    /**
     * @method embedIIPMooviewer
     * @private
     * 
     * 
     * @param {Object} opt
     * 
     * 
     */
    embedIIPMooviewer: function(opt){
    	
        if (this.iipmooviewer!=null) {
        	// NB La versione iipmooviewer 2.0 beta utilizzata non ha metodo per cambiare immagine, quindi mi sono inventato questo metodo
        	this.viewerPanel.un('resize', this.iipmooviewer.reflow, this.iipmooviewer);
        	this.iipmooviewer.container.getChildren().each( function(el, i) { el.destroy(); } );
        } 
        this.iipmooviewer = new IIPMooViewer( this.iipviewerId, opt);
    	this.iipmooviewer.load();
    	this.viewerPanel.on('resize', this.iipmooviewer.reflow, this.iipmooviewer);

    },
    
    /**
     * @method embedIIPZoom
     * @private
     * 
     * 
     * @param {Object} opt
     * 
     * 
     * @param {Object} params
     * 
     * 
     */
    embedIIPZoom: function(opt, params){	
    	swfobject.embedSWF(this.TOLOMEOServer + this.TOLOMEOStaticRoot + "swf/iipzoom/IIPZoom.swf",this.iipviewerId, "100%", "100%", "9.0.0",this.TOLOMEOServer + this.TOLOMEOStaticRoot +"swf/iipzoom/expressInstall.swf", opt, params); 
    	//swfobject.embedSWF(this.TOLOMEOServer + "/swf/iipzoom/IIPZoom.swf",this.iipviewerId, "100%", "100%", "9.0.0",this.TOLOMEOServer +"/swf/iipzoom/expressInstall.swf", opt, params);
    }
	
});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.define('TolomeoExt.ToloLayerViewerAggregation', {

	extend: 'Ext.util.Observable',

	layers: null,
	tilesMultiple: null,
	overlay: null,
	SRID: null,
	units: null,
	mostraInLegenda: null,
	layerOptions: null,
	imagetype: null,
	server: null,
	checked: null,
	opacity: null,
	
	constructor: function(config) {
			
		if (config==undefined) config={};
		Ext.applyIf(config, { layers: [] });
		Ext.apply(this, config);
		
		this.callParent(arguments);
		
	}
	

});

function toloServerMappe(server) {
	
	/* 
	this.id						= null;
	this.nome 					= null;
	this.typeDescription       	= "Mapserver";
	this.typeCode             	= 0;
	this.nomeCredenziale   		= null;
	this.allowServerConnection 	= false;
	this.url          		  	= null;
	this.serverOpts				= null;
	*/
	if (server!=null) {
		Ext.apply(this, server);
	}
	
}

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.define('TolomeoExt.ToloMapDefinition', {

	extend: 'Ext.util.Observable',
	
	/**
	 * {Array} array contenete oggetti toloLayerViewerAggregation 
	 */
	layerAggregations: null,
	
	constructor: function(config) {
		
		if (config==undefined) config={};
		Ext.applyIf(config, { layerAggregations: [] });
		Ext.apply(this, config);
		this.callParent(arguments);
		//TolomeoExt.ToloMapDefinition.superclass.constructor.call(this, config);
		
	},
	
	addLayerAggregation: function(layerAggregation, index) {
		
		if (index==undefined || index==null) {
			layerAggregation.nPluginLayer=this.layerAggregations.length;
			this.layerAggregations.push(layerAggregation);
		} else {
			layerAggregation.nPluginLayer=index;
			this.layerAggregations[index] = layerAggregation;
		}
	
	},
	
	getLayerAggregation: function(index) {
		return this.layerAggregations[index];
	},
	
	getLayerAggregationCount: function() {
		return this.layerAggregations.length;
	},
	
	reverseLayerAggregationsOrder: function() {
		var buff = [];
		
		for (var i=0; i<this.getLayerAggregationCount(); i++) {
			buff[i] = this.getLayerAggregation(i).nPluginLayer;
		}
		this.layerAggregations.reverse();
		
		for (var i=0; i<this.getLayerAggregationCount(); i++) {
			this.getLayerAggregation(i).nPluginLayer = buff[i];
		}
	},
	
	/**
	 * Cerca quale layer aggregation contiene il layer individuato dagli indici catIdx, layIdx
	 */
	whichLayerAggregationContains: function (catTreeIdx, layTreeIdx) {
		
		for (var i=0; i<this.getLayerAggregationCount(); i++) {
			var lag = this.getLayerAggregation(i);
			for (var j=0; j < lag.layers.length; j++ ) {
				var layer = lag.layers[j];
				if (layer.catTreeIdx==catTreeIdx && layer.layTreeIdx==layTreeIdx) return lag;
			}
		}
		
		return null;
	},
	
    whichLayerAggregationContainsNPluginLayer: function (nPluginLayer) {
		
		for (var i=0; i<this.getLayerAggregationCount(); i++) {
			var lag = this.getLayerAggregation(i);
			if (lag.nPluginLayer==nPluginLayer) return lag;
		}
		
		return null;
	}

});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * @class TolomeoExt.ToloGenericWindow
 * @extends Ext.Window
 *
 */
Ext.define('TolomeoExt.Window', {

	extend: 'Ext.Window',
	alias: 'widget.tolomeoExtWindow',
	
	/**
	 * @method constructor
	 * 
	 * 
	 */
	constructor: function(config){		
		this.callParent(arguments);
		
		var clearCls = 'clearCSS';
		if(!this.cls){
			this.cls = clearCls;
		} else {
			if(this.cls.indexOf(clearCls) == -1){
				this.cls += ' ' + clearCls;
			}
		}					
	}
	
	
	
});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


Ext.define('TolomeoExt.ToloWMSExplorerPanel', {

	extend: 'Ext.Panel',
	
	paramsJS: null,
	
	initComponent: function(){
		//Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);	
		/*
		this.layout= {
	        type: 'vbox',
	        align: 'center'
	    };
		*/
		
		var me = this;
		this.frame = false;
		this.border = false;
		
		this.layout = 'border';
		/*
		this.bbar = [
			  { xtype: 'button', text: 'Button 1' }
			];
		*/
		
		/**
		 * @event addLayer
		 * Lanciato quando viene premuto il tasto "Aggiungi layer"
		 * Viene passato array  così composto
		 * [{ url: url del wms, name: nome del layer }.... ]
		 */
		this.addEvents('addLayer');
		
		/**
		 * @event closePressed
		 * Lanciato quando viene premuto il tasto "Chiudi"
		 */
		this.addEvents('closePressed');
		
		this.buttonAlign = 'center';
		
		this.btnAddLayer = Ext.create('Ext.Button',{
								text: ToloI18n.getMsg("ToloWMSExplorerPanel.btnAddLayer"),
								handler: function() {
									var selected = me.grid.getView().getSelectionModel().getSelection();
									var retVal = [];
									var url = me.cmbServerList.getValue(); 
									for (var i=0; i< selected.length; i++) {
										var l = { url: url,
												  name: selected[i].get('name') };
										retVal.push(l);
									}
									me.fireEvent('addLayer', retVal );
								}
		});
		
		this.buttons = [
			  this.btnAddLayer, //, margin: '0 10 5 0' 
			  { text: ToloI18n.getMsg("ToloWMSExplorerPanel.btnChiudi"),
			    handler: function() {
			    		me.fireEvent('closePressed');
			    	}
			    }
			];
		/*
		this.dockedItems = [{
			    xtype: 'panel',
			    dock: 'bottom',
			    items: [
			        { xtype: 'button', text: 'Button 1' }
			    ]
			}]
		*/

		this.callParent(arguments);
		
		var proxy = TolomeoExt.ToloCrossAjaxUtil.getProxy(null, this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxWMSExplorerServlet');
		
		/*proxy.extraParams= {
	       		format:"ext",
	        	idCampo: i,
	        	SRID: this.paramsJS.mappe.SRID,
	        	withGeom: this.suggestWithGeom
	        };*/
			
		var ds = Ext.create('Ext.data.JsonStore',{
		        proxy: proxy
				//autoLoad: true
		        /*idCampo: i,
				listeners: {
					beforeload: {
						fn: function(store, options) {
							return me.setAutoCompleteBaseParams(store, options);  
						}
					},
					load: {
						fn: function(store, records, options) {
							store.sort('descriptionSuggest'+store.idCampo,'ASC');
						}
					}
				}*/
			});
		var a = new TolomeoExt.ToloCrossAjax();
		proxy.on('exception', a.storeException);
		
		var data = (this.paramsJS.layOut.WMSExplorer.serverWMSList) ? this.paramsJS.layOut.WMSExplorer.serverWMSList : [] ;
		
		if (this.paramsJS.layOut.WMSExplorer.includeMapServers) {
			// Aggiunge i server della mappa
			var s = this.paramsJS.serverPool.serverList;
			
			for (var i=0; i<s.length; i++) {
				var serv = s[i];
				if (serv.showInWMSExplorer) {
					data.push ({nome: serv.nome, url: serv.url});
				}
			}
		}
		
		var serverStore = Ext.create('Ext.data.Store', {
		    fields: ['nome', 'url'],
		    data : data
		});

		this.cmbServerList = Ext.create('Ext.form.ComboBox', {
		    fieldLabel: ToloI18n.getMsg("ToloWMSExplorerPanel.cmbServerList.lbl"),
		    emptyText: ToloI18n.getMsg("ToloWMSExplorerPanel.cmbServerList.empty"),
		    //forceSelection: true,
		    labelWidth: 75,
		    store: serverStore,
		    queryMode: 'local',
		    displayField: 'nome',
		    valueField: 'url',
		    //name: 'serverUrl',
			listConfig: {
				    itemTpl: "<b><u>{nome}</u></b><br />{url}"
				},
			prevValue: null,
			changeServer: function() {
				if (this.prevValue!=this.getValue()) {
					ds.load({params: { serverUrl: this.getValue() }});
					this.prevValue = this.getValue();
     			}
			},
    		listeners:{    			
         		'select': function() {
		         			this.changeServer();
         				  },
         		'blur': function() {
         					this.changeServer();
         				},
         		specialkey: function(field, e){
	                    // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
	                    // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
	                    if (e.getKey() == e.ENTER) {
	                        //var form = field.up('form').getForm();
	                        //form.submit();
	                    	this.changeServer();
	                    }
                	}
    			}
    			
			});
			
		this.filterPanel = Ext.create('Ext.form.Panel', {
											//title: 'Filtro',
											bodyPadding: 10,
											layout: 'anchor',
										    defaults: {
										        anchor: '100%'
										    },
											collapsible: false,
											collapsed: false,
											frame: false,
											region: 'north',
											height: 70,
											items: [this.cmbServerList,
													{
														xtype: 'trigger',
														triggerCls : Ext.baseCSSPrefix + 'form-search-trigger',
														fieldLabel: ToloI18n.getMsg("ToloWMSExplorerPanel.filterPanel"),
														labelWidth: 40,
													    onTriggerClick: function() {
													        //Ext.Msg.alert('Status', 'You clicked my trigger!'+this.getValue());
													        me.applicaFiltro(this.getValue());
													    }
													}]
											
											});
		
											
		this.add(this.filterPanel);											
											
		this.grid = Ext.create('Ext.grid.Panel', {
							region: 'center',
							frame: false,
							store: ds,
							enableTextSelection: true,
							columns: [
							    { 	text: ToloI18n.getMsg("ToloWMSExplorerPanel.grid.titolo"),
							    	dataIndex: 'title',
							    	renderer: function(value, metadata) {
											        metadata.style = 'white-space: normal;';
											         return value;
											 }
							    },
							    { 	text: ToloI18n.getMsg("ToloWMSExplorerPanel.grid.desc"), 
							    	dataIndex: 'abstract', 
							    	flex: 4,
							    	renderer: function(value, metadata) {
											        metadata.style = 'white-space: normal;';
											         return value;
											 }
 								},
							    { text: ToloI18n.getMsg("ToloWMSExplorerPanel.grid.scalamax"),  dataIndex: 'maxscaledenom', flex:1, renderer: function(value, metadata) { if (value && value!="") return Math.round(value); else return "";} },
							    { text: ToloI18n.getMsg("ToloWMSExplorerPanel.grid.scalamin"),  dataIndex: 'minscaledenom', flex:1, renderer: function(value, metadata) { if (value && value!="") return Math.round(value); else return "";} }
							    ]	/*,
							    
							dockedItems: [{
							    xtype: 'pagingtoolbar',
						        store: ds,   // same store GridPanel is using
						        dock: 'bottom',
						        displayInfo: true
						    	}]*/
							});

		this.grid.getView().getSelectionModel().on('selectionchange', 
					function( selModel, selected, eOpts) {
											me.btnAddLayer.setDisabled(selected.length==0);
		});
							    										
							
		this.add(this.grid);
		
			
	}, 
	
	applicaFiltro: function(valore) {
		valore=valore.toUpperCase();
		var filter = new Ext.util.Filter({
			    filterFn: function(item) {
			        return (item.get('name').toUpperCase().indexOf(valore)!=-1) ||
			        	   (item.get('abstract').toUpperCase().indexOf(valore)!=-1) || 
			        	   (item.get('title').toUpperCase().indexOf(valore)!=-1);
			    }
			});
		
		this.grid.store.clearFilter();
		this.grid.store.addFilter(filter);
		
	}
	
});/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Pannello contenente visualizzatore WebGL Cesium
 * 
 */
Ext.define('TolomeoExt.ToloCesiumPanelExt', {
	extend: 'Ext.panel.Panel',

	cesiumWidget: null,
	
	/**
	 * @cfg {JSONObject}
	 * Parametri principali tolomeo (contenuti in paramPreset ed arricchiti)
	 */
	paramsJS: null,
	
	/** 
	 * Property: TOLOMEOServer
	 * {String}
	 */
	TOLOMEOServer: null,
	
	/** 
	 * Property: TOLOMEOStaticRoot
	 * {String}
	 */
	TOLOMEOStaticRoot: null,
	
	/** 
	 * Property: TOLOMEOContext
	 * {String}
	 */
	TOLOMEOContext: null,

	
	/**
	 * @cfg {Number}
	 * Longitudine Ovest iniziale
	 */
	startExtentWest: null,
	
	/**
	 * @cfg {Number}
	 * Longitudine Est iniziale
	 */
	startExtentEast: null,
	
	/**
	 * @cfg {Number}
	 * Latitudine nord iniziale
	 */
	startExtentNorth: null,
	
	/**
	 * @cfg {Number}
	 * Latitudine sud iniziale
	 */
	startExtentSouth: null,
	
	/**
	 * @cfg {String}
	 * Url da utilizzare per il terrain provider
	 */
	terrainUrl: null,
	
	/**
	 * @cfg {String}
	 * Credits da utilizzare per il terrain provider
	 */
	terrainCredits: null,
		

    initComponent: function(){
		
		//TODO FILTRARE OVUNQUE LAYERAGGREG MAPSERVER CGI
		
		TolomeoExt.Vars.ApplyIfDefaults(this);	
		
		this.terrainUrl = this.terrainUrl || this.paramsJS.layOut.visualizzazione3D.terrainUrl ; //'http://cesium.agi.com:80/smallterrain';
		this.terrainCredits = this.terrainCredits || this.paramsJS.layOut.visualizzazione3D.terrainCredits; //'Terrain data courtesy Analytical Graphics, Inc.';
		
		this.layout='fit';
		
		this.addEvents(
			/**
			 * @event onBeforePreInit
			 * Lanciato in fase di inizializzazione prima che al viewer cesium siano aggiunti i layer 
			 */
			'onBeforePreInit',
			
			/**
			 * @event onBeforePreInit
			 * Lanciato in fase di inizializzazione dopo che al viewer cesium sono stati aggiunti tutti i layer 
			 */
			'onAfterPreInit'
		);
		
        this.callParent(arguments);
	
        //this._creaCesiumWidget
        this.on('afterrender', function () { 
        							this.show3D( this.startExtentWest, this.startExtentNorth, this.startExtentEast, this.startExtentSouth ); 
        					}, 
        		this, {single:true});
     	//this.on('afterrender', this.pluginPreInit, this, {single:true});
        this.on('resize', this._resizeCesiumWidget);
                
	},
	
	/** 
	 * Esegue l'inizializzazione.
	 */
	pluginPreInit: function() {
		this.fireEvent("onBeforePreInit");				
	    this.addAllLayers();
		this.fireEvent("onAfterPreInit");
	},
	
	/**
	 * Rimuove tutti i layer.
	 */
	removeAllLayers: function() {
		 this.cesiumWidget.scene.imageryLayers.removeAll();
	},
	
	/** 
	 * Aggiunge tutti i layer
	 */
	addAllLayers: function() {
		var nMappa = 0;
		
		for (var i=0; i< this.paramsJS.mapDefinitions[nMappa].getLayerAggregationCount() ; i++) {
			this.refreshMap(i);
		}		
	},
	
	show3D: function(west, north, east, south) {
		if (this.cesiumWidget == null) {
			this._creaCesiumWidget();
		} else {
			this.removeAllLayers();		
		}
		this.gotoExtent(west, north, east, south);
		this.addAllLayers();
	}, 
	
	/**
	 * Aggiunge o modifica un layer WMS
	 * @param {Cesium.ImageryLayer} newCesiumLayer
	 * @param {Cesium.ImageryLayer} actualLayer
	 * @param {Number} position posizione in cui inserire il layer
	 */
	addOrChangeWMS: function(newCesiumLayer, actualLayer, position) {
		var layers = this.cesiumWidget.scene.imageryLayers;
		  if (actualLayer!=null) layers.remove(actualLayer);
		  if (position!==undefined && position!=null) {
			  layers.add(newCesiumLayer, position);
		  } else {
			  layers.add(newCesiumLayer);
		  }
	},
	
	/**
	 * Ricarica un layer
	 * @param {Number} numero identificativo dell'aggregazione layer da aggiornare
	 */
	refreshMap: function (nLayerAggreg) {
		
		var actualLayer = this.getLayerFromNAggreg(nLayerAggreg);
		var cl = this._creaCesiumLayer(nLayerAggreg);
		var position = this.getCesiumRequestedPosition(nLayerAggreg);
		
		if (cl!=null) {
			
			this.addOrChangeWMS(cl, actualLayer, position);
			
		} else {
			if (actualLayer) {
				if (position==1) {
					// se si sta per cancellare il baseLayer prima si porta un altro layer come base
					var layers = this.cesiumWidget.scene.imageryLayers;
					if (layers.length>1) {
						layers.lowerToBottom(layers.get(1));
					}
				}
				this.cesiumWidget.scene.imageryLayers.remove(actualLayer);
			}
		}
		
	},
	
	/**
	 * Restituisce il layer cesium che corrisponde ad una certa aggregazione
	 * @param {Number} nLayerAggreg numero di aggregazione del layer da recuperare
	 * @return {Cesium.ImageryLayer} il layer
	 */
	getLayerFromNAggreg: function(nLayerAggreg) {
		if (this.cesiumWidget==null) return null;
		var layers = this.cesiumWidget.scene.imageryLayers;
		for (var i=0; i<layers.length; i++) {
			var l = layers.get(i);
			if (l.nLayerAggreg==nLayerAggreg) return l;
		}
		return null;
	},
	
	/**
	 * Restituisce la posizione del layer cesium che corrisponde ad una certa aggregazione
	 * @param {Number} nLayerAggreg numero di aggregazione del layer da recuperare
	 * @return {Number} Posizione del layer
	*/
	getCesiumRequestedPosition: function(nLayerAggreg) {
		if (this.cesiumWidget==null) return null;
		var retVal=0;
		var layers = this.cesiumWidget.scene.imageryLayers;
		
		for (var i=0; i<layers.length; i++) {
			var l = layers.get(i);
			if (l.nLayerAggreg>nLayerAggreg) return i;
		}
		return null;
	},
	
	/**
	 * @private
	 */
	_resizeCesiumWidget: function() {
		if (this.cesiumWidget!=null) {
			this.cesiumWidget.resize(); 
		}
	},
	
	/**
	 * @private
	 */
	_creaCesiumWidget: function() {
		if (this.cesiumWidget==null){
			this.cesiumWidget = new Cesium.Viewer(this.body.id, {
													baseLayerPicker: false,
													geocoder: false,
													animation: false,
													homeButton: true,
													fullscreenButton: false,
													//fullscreenElement: this.body.id,
													sceneModePicker: false,
													timeline: false,
													imageryProvider: false } ); //new Cesium.CesiumWidget(this.body.id, { imageryProvider: false } );
			var cesiumTerrainProvider = new Cesium.CesiumTerrainProvider({
		    	url : this.terrainUrl,
		//    	proxy : new Cesium.DefaultProxy('http://localhost:8080/tolomeobinj/TolomeoProxyServlet'),
		    	credit : this.terrainCredits
			});
			
			this.cesiumWidget.scene.terrainProvider = cesiumTerrainProvider;
			this.gotoExtent(this.startExtentWest, this.startExtentNorth, this.startExtentEast, this.startExtentSouth);
			
			//this.cesiumWidget.fullscreenElement = this.body.id;
			
			var me = this;
			
			this.cesiumWidget.homeButton.viewModel.command.beforeExecute.addEventListener(function(commandInfo){
				me.gotoExtent(me.startExtentWest, me.startExtentNorth, me.startExtentEast, me.startExtentSouth);
				//Tell the home button not to do anything.
				commandInfo.cancel = true;
			});
			
		}
	},

	/**
	 * @private
	 */
	_creaCesiumLayer: function(nLayerAggreg){
		var nMappa=0;
		var sep = ',';
		var layerAggr = this.paramsJS.mapDefinitions[nMappa].getLayerAggregation(nLayerAggreg);
		var server = layerAggr.server;
		var layAndStyle = this.paramsJS.getLayerAggregLayersAndStylesStrings(nMappa, nLayerAggreg, sep, null); 
		
		if (layAndStyle.layers=="") {
			return null;
		}
		
		var laysep = layAndStyle.layers;
		if (sep!=',') {
			// sostituisce il separatore con quello giusto per WMS
			var pattern = new RegExp(sep,'g');
			laysep = laysep.replace(pattern, ',');
		} 
		
		var stilisep = layAndStyle.stili;
		if (sep!=',') {
			// sostituisce il separatore con quello giusto per WMS
			var pattern = new RegExp(sep,'g');
			stilisep = stilisep.replace(pattern, ',');
		}

		var currProxy = undefined;
		if (this.paramsJS.layOut.visualizzazione3D && server.usaProxyPer3D) {
			var pr = (server.proxyPer3dUrl && server.proxyPer3dUrl!="") ? server.proxyPer3dUrl : this.TOLOMEOServer + this.TOLOMEOContext + "/TolomeoProxyServlet"
			currProxy = new Cesium.DefaultProxy(pr)
		} 
		
		var attr = "";
		if (layAndStyle.attribution) {
			if (layAndStyle.attribution.constructor === Array) {
				if (layAndStyle.attribution.length>0) {
					var sep = "";
					for (var i = 0; i<layAndStyle.attribution.length>0; i++) {
						if (typeof layAndStyle.attribution[i] === 'string') {
							attr += sep + layAndStyle.attribution[i];
						} else {
							attr += sep + layAndStyle.attribution[i].title;
						}
						sep = ","
					}
				} else {
					attr = "";
				}
				
			} else {
				if (typeof layAndStyle.attribution === 'string') {
					attr += layAndStyle.attribution;
				} else {
					attr += layAndStyle.attribution.title;
				}
			}		
		}
		
		var wms = {
				url: server.url,
				layers : laysep,
				credit: attr,
				parameters: { 	styles: stilisep, 
						transparent : 'true',
        				format : 'image/png'
        		},
				//credit: server.id,
				proxy : currProxy
		};
		
		var layers = this.cesiumWidget.scene.imageryLayers;
		var provider = new Cesium.WebMapServiceImageryProvider(wms);
		var lay = new Cesium.ImageryLayer(provider, {
									alpha: layerAggr.opacity
								});
		lay.nLayerAggreg = nLayerAggreg;
		
		return lay;
	}, 
	
	gotoExtent: function(west, north, east, south) {
	
		if (west!=null && east!=null && north!=null && south!=null) {
			var westRad = Cesium.Math.toRadians(west);
		  	var southRad = Cesium.Math.toRadians(south);
		  	var eastRad = Cesium.Math.toRadians(east);
		  	var northRad = Cesium.Math.toRadians(north);

		  	var extent = new Cesium.Rectangle(westRad, southRad, eastRad, northRad);  
			
		    var scene = this.cesiumWidget.scene;
		    var ellipsoid = Cesium.Ellipsoid.WGS84;

		    scene.camera.viewRectangle(extent, ellipsoid);	
		}
	}

});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.define('TolomeoExt.ToloCommand', {

	//extend: 'Ext.util.Observable',

	statics: {
		//CMDZOOMTO: function() { return	1; },
		//CMDTOCUNCHECKALL: function() { return	2; },
		//CMDTOCCHECK: function() { return	3; },
		//cmdMapping: [
		//				'',
		//				'zoomTo',
		//				'tocCheck'
		//			],
		createCommand: function(commandCode, args) {
			return Ext.createByAlias(
				//cmdMapping['tolocommand.' + commandCode],
				'tolocommand.' + commandCode,
				args
			);
		}
	},
	
/*	config: {
		//commandCode: null,	
		args: null
	},
*/
	constructor: function(config) {

		this.initConfig(config);
		this.callParent(arguments);
	},
	
	run: function(api) {
		
	},
	
	encodeArgs: function() { },
	
	toJSON: function() {
		return "{\"c\":\"" + this.commandCode + "\",\"a\":" + this.encodeArgs() + "}";
	}

});

Ext.define('TolomeoExt.ToloCommand.tocCheck', {

	extend: 'TolomeoExt.ToloCommand',
	alias: ['tolocommand.tocCheck'],
	
	commandCode: 'tocCheck',
	
	config: {
		
		/**
		 * 
		 * @cfg {String[]} 
		 */
		pool: null,
		
		entry: null,
		
		/**
		 * @cfg {String[]} contiene elenco delle descrizioni delle voci di tipo text da attivare
		 */
		tentry: null,
		
		/**
		 * 
		 * @cfg {String} mode Valori possibili <br/>
		 * - listedOnly: modifica solo le voci di legenda listate
		 * - uncheckOthers: deseleziona le voci di legenda non listate
		 * - checkOthers: seleziona le voci di legenda non listate
		 */
		mode: 'listedOnly',
		
		/**
		 * @cfg {Number} ver versione tracciato. 
		 * - Non presente: versione originaria
		 * - 2: aggiunta trasparenza layer
		 */
		ver: null
	},
	
	constructor: function(config) {

		this.initConfig(config);
		this.callParent(arguments);
	},
	
	run: function(api) {
		
		
		api.TOCPanel.on('tocGuiCreate',
			function() {
				if (this.mode=='listedOnly') {
					// loop sulle voci listate
					for (var i=0; i<this.getTocEntryCount(); i++) {
						var tocEntry = this.getTocEntry(i);
						var serverID = this.pool[tocEntry.s]; //"RTCASTORE";
						var name = tocEntry.l; //"r_toscan_cst_fogli";
						var state = (this.mode=='listedOnly') ? tocEntry.a : (this.mode=='uncheckOthers');
						var lay = api.TOCPanel.tocInfo.searchLayerInfo(serverID, name);
						if (lay!=null  && lay.hidden==false && lay.itemType=='layer') {
							if (lay.state) {
								api.TOCPanel.checkAllParentCat(lay.catTreeIdx, true);
								// Se la legenda lo consente apre tutte le categorie fino al layer acceso 
								if (api.TOCPanel.expandCategories) {
									api.TOCPanel.expandCategories(lay.catTreeIdx);
								}
							}
							if (lay.checked!=state) api.TOCPanel.setLayerStateChange(lay.catTreeIdx, lay.layTreeIdx, state );
						}
					}
				} else {
					// Inizializzo stateBuff con il valore di default in funzione di mode
					// Solo per i layer non hidden
					api.TOCPanel.tocInfo.onEachLayer(
						function(cat, lay, catIdx, layIdx) {
								if (lay.hidden==false) lay.stateBuff = (this.mode=='checkOthers');
						},
						this
					);
					// modifico stateBuff e opacityBuff con il valore imposto da tocEntryList (solo per layer non hidden)
					for (var i=0; i<this.getTocEntryCount(); i++) {
						var tocEntry = this.getTocEntry(i);
						var serverID = this.pool[tocEntry.s]; //"RTCASTORE";
						var name = tocEntry.l; //"r_toscan_cst_fogli";
						var state = (this.mode=='listedOnly') ? tocEntry.a : (this.mode=='uncheckOthers');
						var opacityBuff = (this.ver && this.ver>=2) ? tocEntry.o : undefined;
						var styleBuff = (this.ver && this.ver>=3) ? tocEntry.style : undefined;
						var lay = api.TOCPanel.tocInfo.searchLayerInfo(serverID, name);
						if (lay!=null && lay.hidden==false) {
							lay.stateBuff=state;
							lay.opacityBuff = opacityBuff;
							lay.styleBuff = styleBuff; 
						}
					}
					
					// modifico statebuff con il valore imposto solo per le voci di tipo text
					if (this.tentry!=null) {
						for (var i=0; i<this.tentry.length; i++) {
							var tocTextEntry = this.tentry[i];
							var lay = api.TOCPanel.tocInfo.searchLayerInfoByDesc(tocTextEntry, 'text');
							if (lay!=null && lay.hidden==false) {
								lay.stateBuff=true;
							}
						}	
					}
					
					// modifico tutti gli stati che devono cambiare
					api.TOCPanel.tocInfo.onEachLayer(
						function(cat, lay, catIdx, layIdx) {
							if (lay.hidden==false && lay.itemType=='layer' ) {
								// Questo deve farlo comunque, perchè potrebbe avere gli antenati spenti pur essendo già acceso
								if (lay.stateBuff) { 
									api.TOCPanel.checkAllParentCat(lay.catTreeIdx, true);
									// Se la legenda lo consente apre tutte le categorie fino al layer acceso 
									if (api.TOCPanel.expandCategories) {
										api.TOCPanel.expandCategories(lay.catTreeIdx);
									}
								}
								// Questo lo deve fare solo se lo stato è diverso da quello voluto (altrimenti è inutile)
								if (lay.checked!=lay.stateBuff) {
									api.TOCPanel.setLayerStateChange(catIdx, layIdx, lay.stateBuff );
								}
								if (lay.opacityBuff!==undefined && lay.opacity!=lay.opacityBuff) {
									api.TOCPanel.layerOpacityChange(catIdx, layIdx, lay.opacityBuff*100 );
								}
								if (lay.styleBuff!==undefined){
									if(lay.styleCapable && lay.style!=lay.styleBuff && lay.definedStyles) {
										for(var i in lay.definedStyles){
											if(lay.definedStyles[i].name == lay.styleBuff){
												api.TOCPanel.setLayerStyle(catIdx, layIdx, lay.styleBuff );
												break;
											}
										}
										
									}
								}
								
							} else {
								if (lay.hidden==false && lay.itemType == 'text') {
									if (lay.stateBuff) { 
										api.TOCPanel.checkAllParentCat(lay.catTreeIdx, true);
										// Se la legenda lo consente apre tutte le categorie fino al layer acceso 
										if (api.TOCPanel.expandCategories) {
											api.TOCPanel.expandCategories(lay.catTreeIdx);
										}
									}	
								}
							}
						},
						this
					);
				}
			},
			this);			
			return true;
	},
	
	getTocEntry: function(index) {
		var basePos = index * this.getRecordLength();
		var retVal = { s: this.entry[basePos], l: this.entry[basePos+1], a: (this.mode=='listedOnly') ? this.entry[basePos+2] : undefined };
		if (this.ver && this.ver>=2) {
			if (this.mode=='listedOnly') retVal.o = this.entry[basePos+3];
			else retVal.o = this.entry[basePos+2];
		}
		
		if (this.ver && this.ver>=3) {
			if (this.mode=='listedOnly') retVal.style = this.entry[basePos+4];
			else retVal.style = this.entry[basePos+3];
		}
		
		return retVal; 
	},
	
	getTocEntryCount: function() {
		return Math.floor(this.entry.length / this.getRecordLength());
	},
	
	getRecordLength: function() {
		var extraFields = (this.ver && this.ver==2) ? 1 : 0;
		return (this.mode=='listedOnly') ? 3 + extraFields  : 2 + extraFields;
	},
	
	addTocEntry: function(serverID, layerName, state, opacity, style) {
		this.entry = this.entry || [];
		this.pool = this.pool || [];
		
		var serverIndex = this.pool.indexOf(serverID);
		
		if (serverIndex==-1) {
			serverIndex = this.pool.push(serverID) - 1;
		}
		
		this.entry.push(serverIndex, layerName);
		if (this.mode=='listedOnly') {
			this.entry.push(state);	
		}
		
		if ((this.ver && this.ver>=2)) {
			this.entry.push(opacity);
		}
		
		if (this.ver && this.ver>=3) {			
			this.entry.push(style ? style : "");			
		}
		
	},
	
	addTocTextEntry: function(descrizione) {
		this.tentry = this.tentry || [];
		this.tentry.push(descrizione);
	},
	
	encodeArgs: function() {
		
		var obj = { mode: this.mode, entry: this.entry, tentry: this.tentry, pool: this.pool, ver: this.ver };
		
		return Ext.JSON.encode(obj);
	}

});

Ext.define('TolomeoExt.ToloCommand.zoomTo', {

	extend: 'TolomeoExt.ToloCommand',
	alias: ['tolocommand.zoomTo'],
	
	commandCode: 'zoomTo',
	
	config: {
		/**
		 * @cfg {Number} Longitudine
		 */
		x: null,	
	
		/**
		* @cfg {Number} Latitudine
		*/
		y: null,
		
		/**
	 	* @cfg {Number} Scala
		*/
		s: null
	},
	
	constructor: function(config) {

		this.initConfig(config);
		this.callParent(arguments);
	},
	
	run: function(api) {
		
		api.viewer.on('onAfterPostInit',
			function() {
				api.viewer.pluginGotoPosition(this.x, this.y, this.s);		
			}, this,
			{single:true}
		);
		
		//api.viewer.pluginGotoPosition(this.x, this.y, this.s);
		return true;
	},
	
	encodeArgs: function() {
		var obj = { x: this.x, y: this.y, s: this.s};
		
		return Ext.JSON.encode(obj);
	}
	

});

Ext.define('TolomeoExt.ToloCommand.zoomToExtent', {

	extend: 'TolomeoExt.ToloCommand',
	alias: ['tolocommand.zoomToExtent'],
	
	commandCode: 'zoomToExtent',
	
	config: {
		/**
		 * 
		 * @type {Number} 
		 */
		left: null,
		
		/**
		 * 
		 * @type {Number} 
		 */
		bottom: null,
		
		/**
		 * 
		 * @type {Number} 
		 */
		right: null,
		
		/**
		 * 
		 * @type {Number} 
		 */
		top: null
		
	},
	
	constructor: function(config) {

		this.initConfig(config);
		this.callParent(arguments);
	},
	
	run: function(api) {
		
		api.viewer.on('onAfterPostInit',
			function() {
				var bbox = new BBox(this.left, this.bottom, this.right, this.top);
				this.zoomToExtent(bbox, 0);
			}, this,
			{single:true}
		);
		return true;
	},
	
	encodeArgs: function() {
		var obj = { left: this.left, bottom: this.bottom, right: this.right, left: this.left};
		
		return Ext.JSON.encode(obj);
	}
	

});


Ext.define('TolomeoExt.ToloCommand.addPopups', {

	extend: 'TolomeoExt.ToloCommand',
	alias: ['tolocommand.addPopup'],
	
	commandCode: 'addPopup',
	
	config: {
		
		/**
		 * 
		 * @type {Object[]} 
		 */
		conf: null
		
	},
	
	constructor: function(config) {

		this.initConfig(config);
		this.callParent(arguments);
	},
	
	run: function(api) {
		
		api.viewer.on('onAfterPostInit',
			function() {
				for (var i=0; i<this.conf.length; i++) {
					api.viewer.pluginAddPopup(this.conf[i].x, this.conf[i].y, this.conf[i].t, false, this.conf[i].e);	
				}		
			}, this,
			{single:true}
		);
		return true;
	},
	
	encodeArgs: function() {
		var obj = { conf: this.conf };
		
		return Ext.JSON.encode(obj);
	}

});


Ext.define('TolomeoExt.ToloCommand.identify', {

	extend: 'TolomeoExt.ToloCommand',
	alias: ['tolocommand.identify'],
	
	commandCode: 'identify',
	
	config: {
		/**
		 * 
		 * @type {Number} 
		 */
		x: null,
		
		/**
		 * 
		 * @type {Number} 
		 */
		y: null,
		
		/**
		 * 
		 * @type {String} 
		 */
		srid: null,
		
		/**
		 * 
		 * @type {String} 
		 */
		layerName: null
		
	},
	
	constructor: function(config) {

		this.initConfig(config);
		this.callParent(arguments);
	},
	
	run: function(api) {
		
		api.TOCPanel.on('tocGuiCreate',
			function() {			
				var point = api.reprojectToCurrentCrs(new Point(this.x,this.y),this.srid);
				var lonlat = {lon:point.x,lat:point.y};
				var pixel = api.viewer.pluginGetPixelFromCoordinate(lonlat);
				var lay = null;
				
				if(this.layerName){
					var lay = api.TOCPanel.tocInfo.searchLayerInfo(null,this.layerName);
				}
				
				api.onMappaSelect(new Point(lonlat.lon, lonlat.lat),'firstOnTop',false,true, pixel.x,pixel.y,(lay?lay.codTPN:lay));								
			}, this,
			{single:true}
		);
		
		return true;
	},
	
	encodeArgs: function() {
		var obj = { left: this.left, bottom: this.bottom, right: this.right, left: this.left};
		
		return Ext.JSON.encode(obj);
	}
	

});

Ext.define('TolomeoExt.ToloCommand.startPopup', {

	extend: 'TolomeoExt.ToloCommand',
	alias: ['tolocommand.startPopup'],
	
	commandCode: 'startPopup',
	
	config: {
		
		/**
		 * 
		 * @type {String} 
		 */
		msg: null
		
	},
	
	constructor: function(config) {

		this.initConfig(config);
		this.callParent(arguments);
	},
	
	run: function(api) {		
		
		Ext.create('Ext.window.Window', {
		    title: 'Avviso',
		    iconCls: 'iconPermalink',
		    modal: true,
		    height: 200,
		    width: 400,
		    layout: 'fit',
		    items: [{
					  xtype: 'box',
					  autoScroll: true,
					  autoEl: {
					     tag: 'div',
					     html: this.msg
					    }}
		            ],
		    buttons: [
			          { text: 'OK',
			        	listeners: {
			        		click: function() {
			        			this.up('.window').close(); 
				        	}
				         }
			          }
			    ]}).show();
		
		return true;
	},
	
	encodeArgs: function() {
		var obj = { msg: this.msg };
		
		return Ext.JSON.encode(obj);
	}

});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.define('TolomeoExt.ToloProcedure', {

	extend: 'Ext.util.Observable',

	commands: null,
	
	config: {
		cmdUrl: null, 
		commands: null
	},

	constructor: function(config) {
		this.initConfig(config);
		this.callParent(arguments);
		
		this.commands=this.commands||[];
		
		if (config.cmdUrlComp) {
			var decomp = LZString.decompressFromBase64(config.cmdUrlComp);
			this._parseUrl(decomp);
		}
		
		if (config.cmdUrl) {
			this._parseUrl(config.cmdUrl);		
		}
		
		
	},
	
	/**
	 * @private
	 * @param {String} cmdUrl Formato
	 * 		{ c: [	// Array di comandi
	 * 				{ c: 0    			// command code
	 * 				  a: [ { }, { } ]	// args
	 * 				},
	 * 				{ c: ...} 
	 * 				]
	 * 		}
	 */
	_parseUrl: function(cmdUrl) {
		var arg = Ext.JSON.decode(cmdUrl, true);
		
		if (arg) {
			var cmdConfigArr = arg.c;	// Array di comandi
			
			for (var i = 0; i < cmdConfigArr.length; i++) {
				var cConfig = cmdConfigArr[i];
		
				var c = TolomeoExt.ToloCommand.createCommand(cConfig.c, cConfig.a);
				/*
				var c = Ext.create('TolomeoExt.ToloCommand', {
							commandCode: cConfig.c,
							args: cConfig.a
					});
					*/
				this.commands.push(c);
				
			}
		} else {
			//TODO Errore nella stringa
			
		}
	},
	
	addCommand: function(cmd) {
		this.commands.push(cmd);
	},
	
	getCommandCount: function(){
		return this.commands.length;
	},

	getCommand: function(nLine) {
		return this.commands[nLine];
	},

	run: function(api) {
		var bOnOpenDrawMap=false;
		var buff;
		
		for (var i = 0; i < this.getCommandCount(); i++) {
			buff = this.getCommand(i).run(api);
			bOnOpenDrawMap = bOnOpenDrawMap || buff;
			//this.runLine(api, i);
		}
		return bOnOpenDrawMap;
	},

	/*
	runLine: function(api, nLine) {
		var cmd = this.getCommand(nLine);
		var args = cmd.getArgs();

		switch (cmd.getCommandCode()) {
		case TolomeoExt.ToloCommand.CMDZOOMTO:
					//api.viewer.pluginToolSelectZoomAll();
					//api.viewer.bOnOpenDrawMap=true;
		
					api.viewer.pluginGotoPosition(args[0].x, args[0].y, args[0].s);
					
					//api.zoomToScale(1000);
					//api.TOCPanel.on('tocGuiCreate',
					//	function() {
					//		api.zoomToScale(1000); 
					//	});	
					break;
		case TolomeoExt.ToloCommand.CMDTOCUNCHECKALL: 
					api.viewer.bOnOpenDrawMap=true;
					api.TOCPanel.on('tocGuiCreate',
						function() {
							api.TOCPanel.setCategoryStateChange(0,false);
							//api.TOCPanel.setLayerStateChange(0, 0, true); 
						});	
					break;
		case TolomeoExt.ToloCommand.CMDTOCCHECK:
					api.viewer.bOnOpenDrawMap=true;
					api.TOCPanel.on('tocGuiCreate',
						function() {
							var serverID = args[0].s; //"RTCASTORE";
							var name = args[0].l; //"r_toscan_cst_fogli";
							var state = args[0].a;
							var lay = api.TOCPanel.tocInfo.searchLayerInfo(serverID, name);
							if (lay!=null) {
								api.TOCPanel.setLayerStateChange(lay.catTreeIdx, lay.layTreeIdx, state );
							}
							//api.viewer.pluginToolSelectZoomAll();
							//api.viewer.pluginRefreshMap();
						});				
					break;
			default:
		
				break;
		}
		
	},*/ 
	
	toJSON: function() {
		return "{\"c\":" + Ext.JSON.encode(this.commands) + "}";
	}
		
});
	/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Plugin per il display di feature vettoriali 
 * in una griglia ExtJs.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.ToloFeatureGridPanel', {

	extend: 'Ext.Panel',

	alias: "widget.tolomeo_featurecomponent",
	
	id: "tolomeo_featuregrid",

	/**
	 * @property {Object} paramsJS
	 * Configurazioni specifiche del file di preset.
	 */
	paramsJS: null,

	/**
	 * @cfg {String} TOLOMEOServer
	 * URL di base del contesto di Tolomeo.
	 */
	TOLOMEOServer: null,

	/**
	 * @cfg {String} TOLOMEOContext
	 * Contesto di Tolomeo.
	 */
	TOLOMEOContext: null,

	/**
	 * @property {Object} schema
	 * Schema nome e tipo degli attributi usati per la configurazione delle griglia
	 */
	schema: null,

	/**
	 * @cfg {TolomeoExt.ToloFeatureManager} qbFeatureManager (required)
	 * Gestore di richieste e operazioni che coinvolgono le features.
	 */
	qbFeatureManager: null,

	/**
	 * @property {TolomeoExt.events.ToloQueryBuilderEvtManager} qbEventManager
	 * Gestore di eventi per il query builder.
	 */
	qbEventManager: null,
		
    /**
	 * @property {OpenLayers.Layer.Vector} featureLayer 
	 * Layer vettoriale contenente le features corrispondenti alla ricerca.
	 */
	featureLayer: null,
	
	/**
	 * @property {OpenLayers.Layer.Vector} highlightLayer 
	 * Layer vettoriale mostrante le singole feature corrispondenti alla ricerca.
	 */
	highlightLayer: null,
	
	highlightFeatureStyle: {		
		fillColor: "#ffff00",		
		fillOpacity: 0.4,
		strokeColor: "#ffff00",
		strokeOpacity: 0.4
	},
	
	layout: 'fit',
	
	/**
     * Inizializza un oggeto di tipo TolomeoExt.ToloFeatureGridPanel.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	initComponent: function(config) {		
		
		var me = this;
		
		if(!this.featureLayer){
			this.featureLayer = new OpenLayers.Layer.Vector("gridFeatureLayer", {
				displayInLayerSwitcher: false
			});
		}
		
		if(!this.highlightLayer){
			this.highlightLayer = new OpenLayers.Layer.Vector("gridHighlightLayer", {
				displayInLayerSwitcher: false
			});
		}
		
		if(!this.qbEventManager){
			this.qbEventManager = Ext.create('TolomeoExt.events.ToloQueryBuilderEvtManager');
		}
		
		this.qbFeatureManager.setProxy();
		this.qbFeatureManager.setFeatureStore(Ext.create('Ext.data.JsonStore', {
			fields: [],
			pageSize: this.qbFeatureManager.maxFeatures,
			proxy: this.qbFeatureManager.getProxy()
		}));
		
		var exportMenu = Ext.create('Ext.button.Split', {
            tooltip: ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.tooltip"),
            iconCls: "pageexport",
            menu : {
                items: [{
                    text: ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.SHP.text"),
                    menu: {
                        showSeparator: false,
                        items: [{
							text: ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.SHP.tuttepag.text"),
							handler: function(){
								this.qbFeatureManager.fireEvent("export", {format: "shp", items: "all"});
							}, 
							scope: this
						}, {
							text: ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.SHP.pagcorrente.text"),
							handler: function(){
								this.qbFeatureManager.fireEvent("export", {format: "shp", items: "single"});
							}, 
							scope: this
						}]
                    }
                }, {
                    text: ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.Spatialite.text"),
                    name: "spatialiteExporter",
                    menu: {
                      showSeparator: false,
                      items: [{
							text: ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.Spatialite.tuttepag.text"),							
							handler: function(){
								this.qbFeatureManager.fireEvent("export", {format: "spatialite", items: "all"});
							}, 
							scope: this
					   }, {
							text: ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.Spatialite.pagcorrente.text"),
							handler: function(){
								this.qbFeatureManager.fireEvent("export", {format: "spatialite", items: "single"});
							}, 
							scope: this
					   }]
                    }
                }, {
                    text: ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.CSV.text"),
                    menu: {
                      showSeparator: false,
                      items: [{
							text:  ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.CSV.tuttepag.text"),
							handler: function(){
								this.qbFeatureManager.fireEvent("export", {format: "csv", items: "all"});
							}, 
							scope: this
					   }, {
							text: ToloI18n.getMsg("ToloFeatureGridPanel.exportMenu.CSV.pagcorrente.text"),
							handler: function(){
								this.qbFeatureManager.fireEvent("export", {format: "csv", items: "single"});
							}, 
							scope: this
					   }]
                    }
                }]
            }
		});
        
		config = Ext.apply({
			xtype: "tolomeo_featuregrid",
			autoScroll: true,
			header: {
				hidden: true
			},
			//height: 250,			
			border: false,
			title: this.title,
			store: this.qbFeatureManager.featureStore,
			colums: [],
		    dockedItems: [{
		        xtype: 'pagingtoolbar',
		        store: this.qbFeatureManager.featureStore,   // same store GridPanel is using
		        dock: 'bottom',
		        displayInfo: true,
		        disabled: true,
		        items: [
					{
						tooltip:  ToloI18n.getMsg("ToloFeatureGridPanel.btnVisSuMappa.tooltip"),
						name: "viewOnMapButton",
						enableToggle: true,
						iconCls: "pageviewonmap",
						handler: function(button){
							this.updateLayerOnMap(this.qbFeatureManager.featureStore, button.pressed);
						},
						scope: this
					},
					{
						tooltip:  ToloI18n.getMsg("ToloFeatureGridPanel.btnZoomPag.tooltip"),
						name: "zoomOnPageButton",
					    iconCls: "pagezoomonmap",
						handler: function(){
							this.qbEventManager.fireEvent("zoomtomapextent", {dataExtent: this.pageBBOx});
						}, 
						scope: this
					}, "-", exportMenu
		        ]
		    }],
		    listeners:{
		    	scope: this,
		    	zoomtofeatureextent: function(evt){
		    		this.qbEventManager.fireEvent("zoomtomapextent", evt);
		    	},
		    	itemmouseenter: function(scope,record){
		    		me.highlightItemGeometry(record);
		    	},
		    	itemmouseleave: function(scope,record){
		    		me.dehighlightItemGeometry(record);
		    	}
		    }
		}, config || {});

		this.items = [config];
		this.callParent();
		
		this.on("afterrender", function(){
			this.waitMask = new Ext.LoadMask(this.id, {msg: ToloI18n.getMsg("ToloFeatureGridPanel.btnZoomPag.tooltip")});
		}, this);

		// /////////////////////////////////////
		// FeatureManager events's listeners
		// /////////////////////////////////////
		this.qbFeatureManager.on({
			scope: this,
			layerchange: function(results){				
				//	this.ownerCt.expand();								
				var featureGrid = this.query("tolomeo_featuregrid")[0];
				featureGrid.setStore(featureGrid.store, results);
			},
			loadfeatures: function(results, store){
				this.waitMask.hide();
				this.ownerCt.expand();
			},
			loadfeaturesfailure: function(){
				this.waitMask.hide();
			},
			resetfeaturelayer: function(){
				this.updateLayerOnMap(null, false);
				
				//
        		// Disable the paging toolbar
        		//
        		this.query("pagingtoolbar")[0].disable();
        		
        		// Collapse the Grid
//        		this.ownerCt.collapse();
			}
		});
		
    	this.qbFeatureManager.featureStore.on("beforeload", function(store, operation, eOpts){
    		if(operation){
    			//this.qbFeatureManager.maxFeatures = operation.limit;
    			//this.qbFeatureManager.startIndex = operation.start;
    		}
    	}, this);
    	
    	
    	this.qbFeatureManager.on("beforeloadfeatures", function(codTPN){this.queriedCodTPN = codTPN;},this);
    	
    	this.qbFeatureManager.featureStore.on("load", function(store, records, successful, eOpts){
    		if (successful) {
	    		if(records && records.length > 0){
	        		var proxy = store.getProxy();
	        		var reader = proxy.getReader();
	        		var metadata = reader.metaData;
	        		
	        		this.pageBBOx = OpenLayers.Geometry.fromWKT(metadata.pageBBOX).getBounds();
	        		
	        		//
	        		// Enable the paging toolbar
	        		//
	        		this.query("pagingtoolbar")[0].enable();
	        		
	        		//
	        		// Check if the vector layers on map should be updated
	        		//
	        		var button = this.query("[name=viewOnMapButton]")[0];
	        		if(button.pressed){
	        			this.updateLayerOnMap(store, button.pressed);
	        		}
	        		
	        		this.query("[name=spatialiteExporter]")[0].setDisabled(!this.paramsJS.withSpatialiteExporter(this.queriedCodTPN));
	        		
	    		}else{
	                Ext.Msg.show({
	                    title: ToloI18n.getMsg("ToloFeatureGridPanel.infoRisultato.title"),
	                    msg: ToloI18n.getMsg("ToloFeatureGridPanel.msgNessuno"),
	                    buttons: Ext.Msg.OK,
	                    icon: Ext.MessageBox.INFO
	                });
	    		}
    		} else {
    		    Ext.Msg.show({
    		    	title: ToloI18n.getMsg("ToloFeatureGridPanel.infoRisultato.title"),
                    msg: store.proxy.reader.rawData.msgErrore,
                    buttons: Ext.Msg.OK,
                    icon: Ext.MessageBox.INFO
                });
    		}
    	}, this);
	},
	
	/**
     * Aggiorna il layer vettoriale sulla mappa aggiornando le features in esso contenute.
     * @param {Ext.Data.Store} store Un oggetto che rappresenta lo store della griglia. 
	 * @param {Boolean} pressed Indica se il relativo pulsante nella paging toolbar è premuto.
     */
	updateLayerOnMap: function(store, pressed){
		if(pressed){
			var store = this.qbFeatureManager.featureStore;
			var count = store.getCount(); // the elements inside the current page
			var records = store.getRange(0, count-1);
			
			// Create the OL features vector
			var features = [];
			for(var i=0; i<records.length; i++){
				var record = records[i];
				
				var attributes = {};
				for(key in record.data){
					if(key != "geometry"){
						attributes[key] = record.data[key];
					}
				}
				
				var geometry = OpenLayers.Geometry.fromWKT(record.data.geometry);
				var feature = new OpenLayers.Feature.Vector(geometry, attributes, null);
				features.push(feature);						
			}
			
			this.featureLayer.removeAllFeatures();
			this.featureLayer.addFeatures(features);
			this.qbEventManager.fireEvent("addlayer", this.featureLayer);
		}else{
			this.qbEventManager.fireEvent("removelayer", this.featureLayer);
		}
	},
	
	/**
	 * Metodo che fa l'highlight della geometria relativa alla riga 
	 * @param {} record
	 */
	highlightItemGeometry: function(record){		
		var geometry = OpenLayers.Geometry.fromWKT(record.data.geometry);
		var feature = new OpenLayers.Feature.Vector(geometry, {}, this.highlightFeatureStyle);
		this.highlightLayer.addFeatures(feature);
		this.qbEventManager.fireEvent("addlayer", this.highlightLayer);
	},
	
	/**
	 * Metodo che fa l'dehighlight della geometria relativa alla riga 
	 * @param {} record
	 */
	dehighlightItemGeometry: function(){		
		this.highlightLayer.removeAllFeatures();
		this.qbEventManager.fireEvent("removelayer", this.highlightLayer);
	}

});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Plugin per la gestione di richieste e operazioni 
 * che coinvolgono le features.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.ToloFeatureManager', {
	
	extend: 'Ext.util.Observable',
	
	id: "qb_featuremanager",
	
	/**
	 * @cfg {String} TOLOMEOServer
	 * URL di base del contesto di Tolomeo.
	 */
	TOLOMEOServer: null,
	
	/**
	 * @cfg {String} TOLOMEOContext
	 * Contesto di Tolomeo.
	 */
	TOLOMEOContext: null,

	/**
	 * @property {Ext.Data.Store} featureStore
	 * Store delle features ritornate dal server a seguito di una richiesta.
	 */
	featureStore: null,
	
	/**
	 * @cfg {Number} maxFeatures [maxFeatures="10"]
	 * Massimo numero di elementi per pagina.
	 */
	maxFeatures: 10,
			
	/**
	 * @cfg {Number} startIndex [startIndex="0"]
	 * Indice di pagina per richieste paginate.
	 */
	startIndex: 0,
	
	/**
	 * @property {Ext.Data.Proxy} proxy
	 * Proxy Ext per le richieste Ajax cross-domain.
	 */
	proxy: null,
	
	/**
     * Crea un nuovo TolomeoExt.ToloFeatureManager.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	constructor: function(config) {
		this.callParent(arguments);
		
		Ext.apply(this, config);
		
		this.addEvents(
	        /**
			 * @event
			 * Lanciato alla selezione di un nuovo layer dalla form.
			 */
			"layerchange",
			
	        /**
			 * @event
			 * Lanciato se il caricamento delle features fallisce.
			 */
			"loadfeaturesfailure",
			
	        /**
			 * @event
			 * Lanciato quando le features sono state caricate.
			 */
			"loadfeatures",
			
	        /**
			 * @event
			 * Lanciato prima di caricare le features da servizio remoto.
			 */
			"beforeloadfeatures",
			
	        /**
			 * @event
			 * Lanciato per reimpostare i parametri della richiesta.
			 */
			"resetquery",
			
	        /**
			 * @event
			 * Lanciato prima che avvenga la selezione di un nuovo layer dalla form del query builder.
			 */
			"beforelayerchange",
			
	        /**
			 * @event
			 * Lanciato al termine delle operazioni di export.
			 */
			"export",
			
	        /**
			 * @event
			 * Lanciato prima dell'avvio di una operazione di export.
			 */
			"beforedataexport"
		);	
		
		this.on("resetquery", this.resetQuery);
		this.on("export", this.exportPage);		
		this.on("layerchange", this.onLayerChange);
		this.paginationSupported = false;
		this.dummyMaxFeature = 1000000;
	},
	
	/**
     * Esporta i dati in griglia secondo i formati supportati (SHP, CSV, Spatialite).
     * @param {Object} options Oggetto contenente le opzioni per lo scaricamento dei dati. 
     *
     */
	exportPage: function(options){
        this.fireEvent("beforedataexport");
		
		var params = {
			format: 'ext',
			filter: this.proxy.extraParams.filter,
			codTPN: this.proxy.extraParams.codTPN,
			format: options.format,
			startIndex: options.items == "all" ? -1 :  this.startIndex,
			maxFeatures: (options.items == "all" || !this.paginationSupported) ? -1 :  this.maxFeatures,
			ogcFilterVersion: this.proxy.extraParams.ogcFilterVersion
		};
		
    	var submitOpt = {
    		url: this.TOLOMEOServer + this.TOLOMEOContext + '/SearchExportServlet',
    		method: 'POST',
    		params: params,
    		waitMsg: ToloI18n.getMsg("ToloFeatureManager.attesa"),
    		success: function(results, store){
    			var result = results[0];
    			if(result){
    				location.href = this.TOLOMEOServer + this.TOLOMEOContext + '/SearchExportServlet?filename=' + result.data.Descrizione;
    			}
    		},
    		failure: this.doAjaxFailure(),
    		scope: this
    	};
    	
		new TolomeoExt.ToloCrossAjax().request(submitOpt);
	},
	
	/**
     * Imposta lo store delle features.
     * @param {Ext.Data.Store} store Oggetto rappresentante lo store dei dati. 
     *
     */
	setFeatureStore: function(store){
		this.featureStore = store
		
		this.featureStore.on("load", function(){
			this.featureStore.pageSize = this.paginationSupported ? this.maxFeatures : this.dummyMaxFeature;
			this.fireEvent("loadfeatures");
		}, this);
	},
	
    /**
     * Recupera lo schema degli attributi.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
	getSchema: function(fparams){
		
		fparams.format = 'ext';
		
        if (!this.schemaCache) {
            this.schemaCache = {};
        }
        
        this.fireEvent("beforelayerchange");
        
        var schema = this.schemaCache[fparams.codTPN];
        if(schema){
			this.fireEvent("layerchange", schema);
        }else{
        	var submitOpt = {
        		url: this.TOLOMEOServer + this.TOLOMEOContext + '/FilterBuilderMetadataServlet',
        		method: 'POST',
        		params: fparams,
        		waitMsg: ToloI18n.getMsg("ToloFeatureManager.attesa"),
        		success: function(results, store){
        			var schema = results;
        			this.schemaCache[fparams.codTPN] = schema;        			
        			this.fireEvent("layerchange", results);
        		},
        		failure: this.doAjaxFailure,
        		scope: this
        	};
        	
    		new TolomeoExt.ToloCrossAjax().request(submitOpt);
        }        
	},
	
	/**
	 * Gestisce il caricamento del nuovo schema andando a verificare se la paginazione è supportata.
	 * @param {} schema
	 */
	onLayerChange: function(schema){
		if(schema[0] && schema[0].data){
			this.paginationSupported = schema[0].data.paginationSupported;
		} else {
			this.paginationSupported = false;
		}
	},
	
	/**
     * Handler invocato in caso di fallimento della richiesta Ajax.
     * @param {Ext.Data.Store} store Oggetto rappresentante lo store dei dati. 
     *
     */
	doAjaxFailure: function (store) {
		this.fireEvent("loadfeaturesfailure", store);
    },
    
    /**
     * Metodo di caricamento dello store delle features.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    loadFeatures: function(fparams){       	
    	//
    	// Prepare the proxy params
    	//
		this.proxy.extraParams = this.proxy.extraParams || {};
		this.proxy.startParam = "startIndex";
		this.proxy.limitParam = "maxFeatures";
		this.proxy.actionMethods = "POST";
		
    	Ext.apply(this.proxy.extraParams, fparams); 
    	
    	this.fireEvent("beforeloadfeatures",fparams.codTPN);
    	
    	this.featureStore.loadPage(1, {
    	    params:{
    	    	startIndex: this.startIndex,
    	    	maxFeatures: (this.paginationSupported ? this.maxFeatures : -1)
    	    }
    	});
    },
    
	/**
     * Imposta il proxy per le richieste cross-domain. 
     *
     */
    setProxy: function(){
		this.proxy = TolomeoExt.ToloCrossAjaxUtil.getProxy(null, this.TOLOMEOServer + this.TOLOMEOContext + '/SearchExportServlet');
		var reader = this.proxy.getReader();
		reader.root = "rows";
		reader.totalProperty = "total";
    },
    
    /**
     * Recupera il proxy usato per le richiesta cross-domani. 
     *
     */
    getProxy: function(){
    	return this.proxy;
    },
    
	/**
     * Reimposta i parametri di richiesta per la raccolta delle features risultato della ricerca. 
     *
     */
    resetQuery: function(collapse){
    	if(this.featureStore){
    		this.featureStore.removeAll();
    	}
		
    	this.fireEvent("resetfeaturelayer");
    }
	
});/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Strumento di ricerca alfanumerica libera 
 * (Query Tool) general purpose (in grado cioè di 
 * lavorare su qualunque layer vettoriale configurato 
 * nel sistema) per Tolomeo.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.ToloQueryBuilderExt', {

	extend: 'Ext.Panel',

	/**
	 * @cfg {Object} paramsJS
	 * Configurazioni specifiche del file di preset.
	 */
	paramsJS: null,

	/**
	 * @cfg {String} TOLOMEOServer
	 * URL di base del contesto di Tolomeo.
	 */
	TOLOMEOServer: null,

	/**
	 * @cfg {String} TOLOMEOContext
	 * Contesto di Tolomeo.
	 */
	TOLOMEOContext: null,
	
	/**
	 * @cfg {String} filterFormat [filterFormat="OGC"]
	 * Formato dei filtro. Possibili valori: "OGC", "CQL".
	 */
	filterFormat: "OGC",
	
	/**
	 * @cfg {String} ogcFilterVersion [ogcFilterVersion="1.1.0"]
	 * Se filterFormat="OGC" indica il numero di versione del filtro.
	 */
	ogcFilterVersion: "1.1.0",

	/**
	 * @cfg {Boolean} caseInsensitiveMatch [caseInsensitiveMatch="false"]
	 * Indica se i valori degli attributi nel filtro devono essere case sensitive o meno.
	 */
	caseInsensitiveMatch: false,
	
	config: {
		/**
		 * @cfg {TolomeoExt.ToloFeatureManager} qbFeatureManager (required)
		 * Gestore di richieste e operazioni che coinvolgono le features.
		 */
		qbFeatureManager: null,
		
		/**
		 * @cfg {TolomeoExt.events.ToloQueryBuilderEvtManager} qbEventManager (required)
		 * Gestore di eventi per il query builder.
		 */
		qbEventManager: null,
		
		/**
		 * @cfg {TolomeoExt.widgets.ToloLayerSelector} layerSelector
		 * 
		 */
		layerSelector: null,
		
		/**
		 * @cfg {TolomeoExt.widgets.ToloSpatialSelector} spatialSelector
		 * 
		 */
		spatialSelector: null,
		
		/**
		 * @cfg {TolomeoExt.widgets.ToloAttributeFilter} queryfilter
		 * 
		 */
		queryfilter: null
	}, 
	
	/**
	 * @cfg {Object} autoCompleteCfg [autoCompleteCfg="{}"]
	 * Contiene la configurazione per il servizio di autocompletamento.
	 *
	 * @example
	 * autoCompleteCfg: {
	 *  	url: 'http://localhost:8080/tolomeobinj/UniqueValueServlet',
	 *		pageSize: 10
	 * }
	 */
	autoCompleteCfg: {},
	
	/**
	 * @cfg {Boolean} [autoComplete="fase"]
	 * Abilita la funzionalità di autocomplete .
	 */
	autoComplete: false,
	
	/**
	 * @cfg {String} noFilterSelectedMsgTitle
	 * 
	 */
    noFilterSelectedMsgTitle: null,
    
	/**
	 * @cfg {String} noFilterSelectedMsgText
	 * 
	 */
    noFilterSelectedMsgText: null,
    
	/**
	 * @cfg {String} invalidRegexFieldMsgTitle
	 * 
	 */
    invalidRegexFieldMsgTitle: null,
    
	/**
	 * @cfg {String} invalidRegexFieldMsgText
	 * 
	 */
    invalidRegexFieldMsgText: null,
    
	/**
	 * @cfg {String} notEnabledFieldMsgTitle
	 * 
	 */
    notEnabledFieldMsgTitle: null,
    
    /**
	 * @cfg {Boolean} notEnabledFieldMsgText
	 * 
	 */
    notEnabledFieldMsgText: null,    

	/**
     * Inizializza un nuovo TolomeoExt.ToloQueryBuilderExt.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	initComponent: function(config){	
		TolomeoExt.Vars.ApplyIfDefaults(this);

		this.noFilterSelectedMsgTitle=ToloI18n.getMsg("ToloQueryBuilderExt.noFilterSelectedMsgTitle");
		this.noFilterSelectedMsgText=ToloI18n.getMsg("ToloQueryBuilderExt.noFilterSelectedMsgText");
		this.invalidRegexFieldMsgTitle=ToloI18n.getMsg("ToloQueryBuilderExt.invalidRegexFieldMsgTitle");
		this.invalidRegexFieldMsgText=ToloI18n.getMsg("ToloQueryBuilderExt.invalidRegexFieldMsgText");
		this.notEnabledFieldMsgTitle=ToloI18n.getMsg("ToloQueryBuilderExt.notEnabledFieldMsgTitle");
		this.notEnabledFieldMsgText=ToloI18n.getMsg("ToloQueryBuilderExt.notEnabledFieldMsgText");  
		
		this.autoScroll = true;
		this.collapsed = false;
    	
		if(!this.qbEventManager){
			this.qbEventManager = Ext.create('TolomeoExt.events.ToloQueryBuilderEvtManager');
		}
		
		if(!this.qbFeatureManager){
			this.qbFeatureManager = Ext.create('TolomeoExt.ToloFeatureManager', {
				TOLOMEOServer: this.TOLOMEOServer,
				TOLOMEOContext: this.TOLOMEOContext
			});
		}
		
		this.qbFeatureManager.on({
			scope: this,
			layerchange: function(results/*, store*/){
				this.waitMask.hide();
				this.queryfilter.addFilterBuilder(results/*, store*/);
				this.spatialSelector.setGeomFieldName(this.queryfilter.getGeomFieldName());
			},
			loadfeatures: function(results, store){
				this.waitMask.hide();
			},
			beforeloadfeatures: function(){
				this.waitMask.show();
			},
			beforelayerchange: function(){
				this.waitMask.show();
			},
			loadfeaturesfailure: function(){
				this.waitMask.hide();
			}
		});
		
		// /////////////////////
		// Layer Selector
		// /////////////////////
		var layers = [];
		var evetLayerList = this.paramsJS.azioniEventi.eventiLayerList;
		for(var i=0; i<evetLayerList.length; i++){
			var layerEventConfig = evetLayerList[i];
			if(layerEventConfig.queryBuilder){
				layers.push({
					name: layerEventConfig.nomeLayer, 
					description: layerEventConfig.descrizioneLayer, 
					codTPN: layerEventConfig.codTPN
				});
			}
		}
		
		this.layerSelector = Ext.create('TolomeoExt.widgets.ToloLayerSelector', {
			layers: layers,
			listeners:{
				scope: this,
				layerselected: function(records){
					this.reset();
					
					// /////////////////////////////////////////////////
					// Enable the sub components after layer selection
					// in order to allow filter composition.
					// /////////////////////////////////////////////////
					this.spatialSelector.enable();
					this.filterView.enable();
					this.enableAttributeFilter(records[0]);
				}				
			}
		});
		
		// /////////////////////
		// Spatial Selector
		// /////////////////////
		this.spatialSelector = Ext.create('TolomeoExt.widgets.ToloSpatialSelector', {
			qbEventManager: this.qbEventManager,
			filterGeometryName: "geom",
			disabled: true
		});
		
		// /////////////////////
		// Attribute Filter
		// /////////////////////
		this.queryfilter = Ext.create('TolomeoExt.widgets.ToloAttributeFilter', {
			scroll: true,
			disabled: true,
			caseInsensitiveMatch: this.caseInsensitiveMatch,
			autoCompleteCfg: this.autoCompleteCfg,
			autoComplete: this.autoComplete,
			TOLOMEOServer: this.TOLOMEOServer,
			TOLOMEOContext: this.TOLOMEOContext
		});
		
		this.filterView = Ext.create('TolomeoExt.widgets.ToloFilterView', {
			scroll: true,
			disabled: true,
			listeners:{
				scope: this,
				typeselected: function(records){
					var filter = this.getFilter();
					if(filter){
						var record = records;
						
						if(records instanceof Array){
							record = records[0];
						}
						
						var serialized_filter = this.getFilterString(filter, record.get("name"));
						this.filterView.setFilter(serialized_filter);
					}
				}
			}
		});
			
		this.bbar = ["->", {
            text: "Cancella",
            iconCls: "querybuilder-icon-cancel",
            scope: this,
            handler: function() {
            	this.resetAllConditions();
            }
        }, {
            text: "Cerca",
            iconCls: "querybuilder-icon-find",
            handler: function() {
            	var filter = this.getFilter();
            	
            	if(filter){
            		var serialized_filter = this.getFilterString(filter, null);

                    var fparams = {
        				codTPN: this.codTPN,
        				SRID: this.paramsJS.mappe.SRID,
        				filter: serialized_filter,
        				ogcFilterVersion: this.ogcFilterVersion,
        				format: "ext"
        			}; 
            		
                    this.qbFeatureManager.loadFeatures(fparams);
            	}
            },
            scope: this
        }];
		
		this.callParent();
	
		this.add([this.layerSelector, this.spatialSelector, this.queryfilter, this.filterView]);
		
		this.spatialSelector.reset();
		
		// ////////////////////////////////////////////////////////
		// Disable the tool if any layer is configured to use it
		// ////////////////////////////////////////////////////////
		if(layers.length < 1){
			this.disabled = true;
		}
		
		this.on("afterrender", function(){
			this.waitMask = new Ext.LoadMask(this.id, {msg: "Ricerca in corso...."});
		}, this);
		
	},
	
	/**
     * Reimposta i componenti della form e il la griglia delle features
     *
     */
	reset: function(){
		
		// Attribute Form Reset
    	this.queryfilter.attributeFieldSet.removeAll();
		this.resetAllConditions();		    	
    	    	    	    
	},
	
	/**
     * Reimposta tutte le condizioni iniziali di ricerca per lo specifico livello
     *
     */
	resetAllConditions: function(){
    	// Spatial Selector Reset
    	this.spatialSelector.reset();
    	var spatialMethodCombo = this.spatialSelector.getSelectionMethodCombo();
    	spatialMethodCombo.reset();
    	
    	// Attribute Form Reset
    	if(this.queryfilter.filterBuilder){
    		this.queryfilter.filterBuilder.removeAllConditions();
    	}
    	
    	
    	// Attribute Filter Reset
    	this.filterView.resetView();
    	
    	// Feature Grid Reset
    	this.qbFeatureManager.fireEvent("resetquery");
	},
	
	/**
     * Abilita il filtro degli attributi.
     * @param {Ext.Data.Record} record corrispondente al layer selezionato. 
     *
     */
	enableAttributeFilter: function(record){
		// Adding a Filter Builder passing the feature type name
		this.codTPN = record.get('codTPN');
		
		var fparams = {
			codTPN: this.codTPN
		}; 
		
    	// Submit ajax della form
    	this.qbFeatureManager.getSchema(fparams);
	},
	
	/**
     * Recupera il filtro selezionato in formato stringa.
     * @param {OpenLayers.Filter} filter Il filtro selezionato. 
	 * @param {String} type Tipo del filtro.
     * @return {String} il filtro in formato stringa.
     */
	getFilterString: function(filter, type){
        var format = this.filterFormat;        
        if(type){
        	format = type;
        }
        
        var serialized_filter = "";
        if(format == "OGC" ){
            var node = new OpenLayers.Format.Filter({version: this.ogcFilterVersion}).write(filter);
            serialized_filter = new OpenLayers.Format.XML().write(node);
        }else{
        	serialized_filter = new OpenLayers.Format.CQL().write(filter);
        }
        
        return serialized_filter;
	},
	
    /**
     * Recupera il filtro selezionato.
     * @return {OpenLayers.Filter} il filtro selezionato.
     */
	getFilter: function(){
		var filter = null;
								
    	if(!this.queryfilter.disabled && !this.spatialSelector.disabled){
    		
    		if(this.queryfilter.attributeFieldSet.collapsed && this.spatialSelector.spatialFieldSet.collapsed){
    			
    			 Ext.Msg.show({
    				 title: this.noFilterSelectedMsgTitle,
                     msg: this.noFilterSelectedMsgText,
                     buttons: Ext.Msg.OK,
                     icon: Ext.MessageBox.ERROR
                 }); 
    			
    			return filter;
    		}
    		
    		// ////////////////////////////////////////////////
            // Check if there are some invalid field according 
    		// to validators regex config
            // ////////////////////////////////////////////////
    		var invalidItems = 0;
    		if(!this.queryfilter.attributeFieldSet.collapsed){
        		var filterFieldItem = this.query('tolomeo_tolofilterfield');            

                for(var x = 0; x<filterFieldItem.length; x++){
                	if(filterFieldItem[x].valueWidgets){
                    	var valueWidgets = filterFieldItem[x].valueWidgets.items.items;
                    	for(var y=0; y<valueWidgets.length; y++){
                    		var validateItem = valueWidgets[y];
                            if(!validateItem.isValid(true)){
                                invalidItems++;
                            }
                    	}
                	}
                }  
    		}
            
            if(invalidItems == 0){                    	
            	var filters = [];
            	
            	// ///////////////////////
            	// Compose the Filter
            	// ///////////////////////
            	if(!this.queryfilter.attributeFieldSet.collapsed){
                	var attributeFilter = this.queryfilter.filterBuilder.getFilter();
                	if(attributeFilter){
                		filters.push(attributeFilter);	
                	}
            	}

            	// /////////////////////////////////////////////
            	// If the spatial field set is collapdes then 
            	// use the current map extent. 
            	// /////////////////////////////////////////////
            	var currentMapExtent = this.spatialSelector.spatialFieldSet.collapsed;
            	if(!currentMapExtent){
	                var spatialFilter = this.spatialSelector.getQueryFilter(currentMapExtent);   
	                if (spatialFilter) {
	                	spatialFilter.projection = this.paramsJS.mappe.SRID;
	                	filters.push(spatialFilter);
	                }
            	}
            	
                if(filters.length > 0){
    				var filter = filters.length > 1 ?
                        new OpenLayers.Filter.Logical({
                            type: OpenLayers.Filter.Logical.AND,
                            filters: filters
                        }) :
                        filters[0];   
                        
                    return filter;
                    
                }else{
                    Ext.Msg.show({
                        title: this.noFilterSelectedMsgTitle,
                        msg: this.noFilterSelectedMsgText,
                        buttons: Ext.Msg.OK,
                        icon: Ext.MessageBox.ERROR
                    }); 
                }
            }else{
                Ext.Msg.show({
                    title: this.invalidRegexFieldMsgTitle,
                    msg: this.invalidRegexFieldMsgText,
                    buttons: Ext.Msg.OK,
                    icon: Ext.MessageBox.ERROR
                });
            }
            
    	}else {
            Ext.Msg.show({
                title: this.notEnabledFieldMsgTitle,
                msg: this.notEnabledFieldMsgText,
                buttons: Ext.Msg.OK,
                icon: Ext.MessageBox.ERROR
            });
    	}
    	
    	return filter;
	}
    
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets');

/**
 * Crea il pannello contenitore per il filtro degli attributi.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.ToloAttributeFilter', {

	extend: 'Ext.Panel',
	
	padding: '0 5 0 5',
	
	/**
     * @cfg {Boolean} caseInsensitiveMatch [caseInsensitiveMatch="false"]
	 * Il filtro di comparazione per i campi di tipo stringa deve essere case insensitive ?
     */
	caseInsensitiveMatch: false,
    
	/**
     * @cfg {Object} autoCompleteCfg [autoCompleteCfg="{}"]
	 * Stabilisce la configurazione da usare per la funzionalità di autocompletamento.
	 *
	 * @example
	 * autoCompleteCfg: {
	 *  	url: 'http://localhost:8080/tolomeobinj/UniqueValueServlet',
	 *		pageSize: 10
	 * }
     */
	autoCompleteCfg: {},
	
	/**
     * @cfg {Boolean} autoComplete [autoComplete="false"]
	 * Abilita la funzionalità di autocompletamento per i campi stringa.
     */
	autoComplete: false,
	
	filterBuilderStore: null,

	/**
     * Inizializza un nuovo TolomeoExt.widgets.ToloAttributeFilter.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	initComponent: function(config){				
		this.border = 0;
		
		this.attributeFieldSet = Ext.create('Ext.form.FieldSet', {
			title: ToloI18n.getMsg("ToloAttributeFilter.attributeFieldSet"),
			//anchor: "-1",
			autoWidth: true,
			autoHeight: true,
			collapsed : true,
			checkboxToggle: true
		});
		
		this.items = [this.attributeFieldSet];
        
		this.callParent();
    },
    
    getGeomFieldName: function() {
    	var fields = this.filterBuilderStore.model.getFields();
    	for (var i=0; i<fields.length; i++){
    		var field = fields[i];
    		if (field.name=="geomFieldName"){
    			geomFieldName = field.mapping;
    			break;
    		}
    	}
    	return this.filterBuilderStore.getAt(0).get("geomFieldName");
    },
    
	/**
     * Aggiunge alla form un nuovo nuovo costruttore per il filtro.
     * @param {Array} records corrispondenti allo schema della FeatureType da gestire.
     */
    addFilterBuilder: function(results){
    	
    	var schema = results;
    	this.filterBuilderStore = Ext.create('Ext.data.Store', {
   		    fields: [{
   		    	name: 'name',
   		    	mapping: 'name'
   		    },{
   		    	name: 'type', 
   		    	mapping: 'type'
   		    },{
   		    	name: 'restriction', 
   		    	mapping: 'restriction'
   		    },{
   		    	name: 'regex', 
   		    	mapping: 'regex'
   		    },{
   		    	name: 'dbname', 
   		    	mapping: 'dbname'
   		    },{
   		    	name: 'codTPN', 
   		    	mapping: 'codTPN'
   		    },{
   		    	name: 'geomFieldName', 
   		    	mapping: 'geomFieldName'
   		    },{
	   		    name: 'autocomplete', 
	   		    mapping: 'autocomplete'
	   		}],
   		    data: schema
   		});
    	
		this.filterBuilder = new TolomeoExt.widgets.ToloFilterBuilder({
			 caseInsensitiveMatch: this.caseInsensitiveMatch,
			 autoCompleteCfg: this.autoCompleteCfg,
			 autoComplete: this.autoComplete,
			 TOLOMEOServer: this.TOLOMEOServer,
			 TOLOMEOContext: this.TOLOMEOContext,
			 attributes: this.filterBuilderStore,
            allowBlank: true,
            allowGroups: false
        });
		
		this.attributeFieldSet.add(this.filterBuilder);
		
		this.enable();
    },
    
    reset: function(){
    	if(this.filterBuilder){
    		this.filterBuilder.removeAllConditions();
    	}
    	this.attributeFieldSet.removeAll();
    }
    
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets');

/**
 * Crea un panello per assembrale la form di composizione del filtro.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.ToloFilterBuilder', {
	
	extend: 'Ext.Container',

	alias: 'widget.tolomeo_tolofilterbuilder',

	padding: '0 5 0 5',
	
	/**
     * @cfg {Array} builderTypeNames [builderTypeNames=``["ognuno", "tutti", "nessuno", "non tutti"]``]
	 * La lista di etichette che corrispondono ai tipi di costanti del costruttore.
     */
	builderTypeNames: null,
    
	/**
     * @cfg {Array} allowedBuilderTypes 
	 * La lista delle costanti dei tipi di costruttore. Valori possibili sono ``[ANY_OF, ALL_OF, NONE_OF]``
     */
    allowedBuilderTypes: null,

	/**
     * @cfg {Boolean} allowBlank [allowBlank="false"]
	 * Impostare a true se si desidera consentire campi vuoti.
     */
    allowBlank: false,

	/**
     * @cfg {Boolean} caseInsensitiveMatch [caseInsensitiveMatch="false"]
	 * Il filtro di comparazione per i campi di tipo stringa deve essere case insensitive ?
     */
    caseInsensitiveMatch: false,

	/**
     * @cfg {String} preComboText
	 * Testo da mostrare prima della combo box per il tipo.
     */
    preComboText: null,

    /**
     * @cfg {String} postComboText
	 * Testo da mostrare dopo la combo box per il tipo.
     */
    postComboText: null,

	/**
     * @cfg {String} cls
	 * La classe di stile da usare per i pannelli di questo componente.
     */
    cls: "tolomeo-tolofilterbuilder",

	/**
     * @property {Object} builderType
	 * 
     */
    builderType: null,

	/**
     * @property {Object} childFilterContainer
	 * 
     */
    childFilterContainer: null,
    
	/**
     * @cfg {Object} customizeFilterOnInit [customizeFilterOnInit="true"]
	 * 
     */
    customizeFilterOnInit: true,
    
    /**
     * @cfg {String} addConditionText
	 * 
     */
    addConditionText: null,
	
	/**
     * @cfg {String} addGroupText
	 * 
     */
    addGroupText: null,
	
	/**
     * @cfg {String} removeConditionText
	 * 
     */
    removeConditionText: null,

	/**
     * @cfg {Boolean} allowGroups [customizeFilterOnInit="false"]
	 * Consente di agiungere gruppi di condizioni. Se "false" solo condizioni individuali saranno aggiunte al filtro.
     */
    allowGroups: false,
    
	/**
     * @cfg {Object} attributesComboConfig 
	 * Configurazione della combo box degli attributi.
     */
    attributesComboConfig: null,

	/**
     * @cfg {Boolean} autoComplete [autoComplete="false"]
	 * Abilita la funzionalità di autocompletamento per i campi stringa.
     */
    autoComplete: false,
    
	/**
     * @cfg {Object} autoCompleteCfg [autoCompleteCfg="{}"]
	 * Stabilisce la configurazione da usare per la funzionalità di autocompletamento.
	 *
	 * @example
	 * autoCompleteCfg: {
	 *  	url: 'http://localhost:8080/tolomeobinj/UniqueValueServlet',
	 *		pageSize: 10
	 * }
     */
    autoCompleteCfg: {},

	/**
     * Inizializza un nuovo TolomeoExt.widgets.ToloFilterBuilder.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent: function(config) {
        var defConfig = {
            defaultBuilderType: TolomeoExt.widgets.ToloFilterBuilder.ANY_OF
        };
        Ext.applyIf(this, defConfig);
        
        
        this.allowedBuilderTypes = [ToloI18n.getMsg("ToloFilterBuilder.builderTypeNames.unoquasiasi"), 
    		ToloI18n.getMsg("ToloFilterBuilder.builderTypeNames.tutti"),
    		ToloI18n.getMsg("ToloFilterBuilder.builderTypeNames.nessuno"),
    		ToloI18n.getMsg("ToloFilterBuilder.builderTypeNames.nontutti")];
        
        this.preComboText = ToloI18n.getMsg("ToloFilterBuilder.preComboText");
        this.postComboText = ToloI18n.getMsg("ToloFilterBuilder.postComboText");
        
        this.addConditionText = ToloI18n.getMsg("ToloFilterBuilder.addConditionText");
        this.addGroupText = ToloI18n.getMsg("ToloFilterBuilder.addGroupText");
        this.removeConditionText = ToloI18n.getMsg("ToloFilterBuilder.removeConditionText");
        
        if(this.customizeFilterOnInit) {
            this.filter = this.customizeFilter(this.filter);
        }
        
        this.builderType = this.getBuilderType();
        
        this.items = [{
            xtype: "container",
            layout: "form",
            ref: "form",
            defaults: {anchor: "100%"},
            hideLabels: true,
            items: [
                {
	                xtype: "fieldcontainer",
	                style: "padding-left: 2px",
	                items: [{
	                    xtype: "label",
	                    style: "padding-top: 0.3em",
	                    text: this.preComboText
	                }, this.createBuilderTypeCombo(), 
	                {
	                    xtype: "label",
	                    style: "padding-top: 0.3em",
	                    text: this.postComboText
	                }]
	            }, 
	            this.createChildFiltersPanel(), 
	            {
	                xtype: "toolbar",
	                items: this.createToolBar()
	            }
		    ]        
        }];
        
        this.addEvents(
			/**
			 * @event
			 * Lanciato quando il filtro subisce un cambiamento.
			 */
            "change"
        ); 

        this.callParent();
        
        this.on("added", function(scope){
        	scope.form = scope.query('container[ref=form]')[0];
        	scope.builderTypeCombo = scope.query('combo[ref=builderTypeCombo]')[0];
        });
    },

	/**
     * Crea la toolbar di comando.
     *
     */
    createToolBar: function() {
        var bar = [{
            text: this.addConditionText,
            iconCls: "add",
            handler: function() {
                this.addCondition();
            },
            scope: this
        }];
        if(this.allowGroups) {
            bar.push({
                text: this.addGroupText,
                iconCls: "add",
                handler: function() {
                    this.addCondition(true);
                },
                scope: this
            });
        }
        return bar;
    },
    
	/**
     * Restituisce il filtro che corrisponde al modello delle specifiche di Filter Encoding.
	 * Usare questo metodo invece che accedere direttamente alla proprietà ``filter`` della proprità.
	 * Il valore di ritorno sarà ``false`` se nessun figlio possiede una proprietà, un typo o un valore.
     * @return {OpenLayers.Filter} Il filtro corrente.
     */
    getFilter: function() {
        var filter;
        if(this.filter) {
            filter = this.filter.clone();
            if(filter instanceof OpenLayers.Filter.Logical) {
                filter = this.cleanFilter(filter);
            }
        }
        return filter;
    },
    
	/**
     * Assicura che i filtri binary logici abbiano più di un figlio.
     * @param {OpenLayers.Filter.Logical} filter Il filtro corrente.
	 * @return {OpenLayers.Filter} Un filtro che rispetta il modello usato da questo costruttore.
     */
    cleanFilter: function(filter) {
        if(filter instanceof OpenLayers.Filter.Logical) {
            if(filter.type !== OpenLayers.Filter.Logical.NOT &&
               filter.filters.length === 1) {
                filter = this.cleanFilter(filter.filters[0]);
            } else {
                var child;
                for(var i=0, len=filter.filters.length; i<len; ++i) {
                    child = filter.filters[i];
                    if(child instanceof OpenLayers.Filter.Logical) {
                        child = this.cleanFilter(child);
                        if(child) {
                            filter.filters[i] = child;
                        } else {
                            filter = child;
                            break;
                        }
                    } else if(!child || child.type === null || child[child.property] === null || child[child.type === OpenLayers.Filter.Comparison.BETWEEN ? "lowerBoundary" : "value"] === null || child[child.type === OpenLayers.Filter.Comparison.BETWEEN ? "upperBoundary" : "value"] === null ) {
                        filter = false;
                        break;
                    }
                }
            }
        } else {
            if(!filter || filter.type === null || filter.property === null || filter[filter.type === OpenLayers.Filter.Comparison.BETWEEN ? "lowerBoundary" : "value"] === null || filter[filter.type === OpenLayers.Filter.Comparison.BETWEEN ? "upperBoundary" : "value"] === null) {
                filter = false;
            }
        }
        return filter;
    },
    
	/**
     * Crea un filtro che corrisponde al modello del corrente costruttore.
	 * Questo filtro non rispetterà necessariamente e specifiche di Filter Encoding.
	 * In particolare, i filtri che rappresentano operatori logici binary possono non avere due filtri figlio.
	 * Usare il metodo ''getFilter'' per ottenere un filtro che rispetta le specifiche di Filter Encoding
     * @param {OpenLayers.Filter} filter Il filtro corrente.
	 * @return {OpenLayers.Filter} Un filtro che rispetta il modello usato da questo costruttore.
     */
    customizeFilter: function(filter) {
        if(!filter) {
            filter = this.wrapFilter(this.createDefaultFilter());
        } else {
            filter = this.cleanFilter(filter);
            var child, i, len;
            switch(filter.type) {
                case OpenLayers.Filter.Logical.AND:
                case OpenLayers.Filter.Logical.OR:
                    if(!filter.filters || filter.filters.length === 0) {
                        // give the filter children if it has none
                        filter.filters = [this.createDefaultFilter()];
                    } else {
                        for(i=0, len=filter.filters.length; i<len; ++i) {
                            child = filter.filters[i];
                            if(child instanceof OpenLayers.Filter.Logical) {
                                filter.filters[i] = this.customizeFilter(child);
                            }
                        }
                    }
                    // wrap in a logical OR
                    filter = new OpenLayers.Filter.Logical({
                        type: OpenLayers.Filter.Logical.OR,
                        filters: [filter]
                    });
                    break;
                case OpenLayers.Filter.Logical.NOT:
                    if(!filter.filters || filter.filters.length === 0) {
                        filter.filters = [
                            new OpenLayers.Filter.Logical({
                                type: OpenLayers.Filter.Logical.OR,
                                filters: [this.createDefaultFilter()]
                            })
                        ];
                    } else {
                        // NOT filters should have one child only
                        child = filter.filters[0];
                        if(child instanceof OpenLayers.Filter.Logical) {
                            if(child.type !== OpenLayers.Filter.Logical.NOT) {
                                // check children of AND and OR
                                var grandchild;
                                for(i=0, len=child.filters.length; i<len; ++i) {
                                    grandchild = child.filters[i];
                                    if(grandchild instanceof OpenLayers.Filter.Logical) {
                                        child.filters[i] = this.customizeFilter(grandchild);
                                    }
                                }
                            } else {
                                // silly double negative
                                if(child.filters && child.filters.length > 0) {
                                    filter = this.customizeFilter(child.filters[0]);
                                } else {
                                    filter = this.wrapFilter(this.createDefaultFilter());
                                }
                            }
                        } else {
                            // non-logical child of NOT should be wrapped
                            var type;
                            if(this.defaultBuilderType === TolomeoExt.widgets.ToloFilterBuilder.NOT_ALL_OF) {
                                type = OpenLayers.Filter.Logical.AND;
                            } else {
                                type = OpenLayers.Filter.Logical.OR;
                            }
                            filter.filters = [
                                new OpenLayers.Filter.Logical({
                                    type: type,
                                    filters: [child]
                                })
                            ];
                        }
                    }
                    break;
                default:
                    // non-logical filters get wrapped
                    filter = this.wrapFilter(filter);
                    break;
            }
        }
        return filter;
    },
	
	/**
     * Inizializza il filtro predefinito.
	 * @return {OpenLayers.Filter} Un filtro che rispetta il modello usato da questo costruttore.
     */
    createDefaultFilter: function() {
        return new OpenLayers.Filter.Comparison({
                            matchCase: !this.caseInsensitiveMatch});
    },
    
	/**
     * Prende un filtro non logico per creare un parent che dipende da ``defaultBuilderType``.
	 * @param {OpenLayers.Filter} filter Un filtro non logico.
	 * @return {OpenLayers.Filter} Una versione wrapped del filtro passato come argomento.
     */
    wrapFilter: function(filter) {
        var type;
        if(this.defaultBuilderType === TolomeoExt.widgets.ToloFilterBuilder.ALL_OF) {
            type = OpenLayers.Filter.Logical.AND;
        } else {
            type = OpenLayers.Filter.Logical.OR;
        }
        return new OpenLayers.Filter.Logical({
            type: OpenLayers.Filter.Logical.OR,
            filters: [
                new OpenLayers.Filter.Logical({
                    type: type, filters: [filter]
                })
            ]
        });
    },
    
	/**
     * Aggiunge una nuova condizione o gruppo di condizion al costruttore del filtro.
	 * Qusto modifica il filtro a aggiunge un pannello che rappresenta la nuova condizione 
	 * o grupo di condizioni.
	 * @param {Object} group Un nuovo gruppo di condizioni.
     */
    addCondition: function(group) {
        var filter, type;
        if(group) {
            type = "tolomeo_tolofilterbuilder";
            filter = this.wrapFilter(this.createDefaultFilter());
        } else {
            type = "tolomeo_tolofilterfield";
            filter = this.createDefaultFilter();
        }
        var newChild = this.newRow({
            xtype: type,
            filter: filter,
            columnWidth: 1,
            attributes: this.attributes,
            autoComplete: this.autoComplete,
            autoCompleteCfg: this.autoCompleteCfg,
			TOLOMEOServer: this.TOLOMEOServer,
			TOLOMEOContext: this.TOLOMEOContext,
            allowBlank: group ? undefined : this.allowBlank,
            customizeFilterOnInit: group && false,
            caseInsensitiveMatch: this.caseInsensitiveMatch,
            listeners: {
                change: function() {
                    this.fireEvent("change", this);
                },
                scope: this
            }
        });
        this.childFilterContainer.add(newChild);
        this.filter.filters[0].filters.push(filter);
        this.childFilterContainer.doLayout();
    },
    
	/**
     * Rimuove una condizione o gruppo di condizioni da costruttore. 
	 * Questo modificha il filtro e rimuove il pannello che rappresenta la nuova condizione 
	 * o grupo di condizioni.
	 * @param {Object} item elemento da rimuovere.
	 * @param {OpenLayers.Filter} filter Il filtro corrente .
     */
    removeCondition: function(item, filter) {
		var parent = this.filter.filters[0].filters;
		if(parent.length > 1) {
			var a = parent.indexOf(filter);
			if(a!=-1){
				parent.splice(a,1)
			}
			
			this.childFilterContainer.remove(item, true);
		}else{
			var items = item.query("tolomeo_tolofilterfield");
			
			var i = 0;
			while(items[i]){
				for(var k = 0; k<items.length; k++){
					items[k].items.each(function(f) {
					    if (Ext.isFunction(f.reset)) {
					        f.reset();
					    }
					});
				}
				
                for(var c = 1;c<items[i].items.items.length;c++){
                	var cmp = items[i].items.get(c);
                	if(cmp.xtype == "container"){
                		cmp.removeAll();
                	}else{
                		cmp.disable();
                	}
                }

				filter.value = null;
                filter.lowerBoundary = null;
                filter.upperBoundary = null;
				i++;
			}
		}
		
		this.fireEvent("change", this);
    },
    
	/**
     * Rimuove dal pannello tutte le condizioni presenti.
     * 
     */
    removeAllConditions: function(){
    	var containers = this.query("container[name=filtercondition_container]");
    	for(var i=0; i<containers.length; i++){
    		var container = containers[i];
    		var filter = container.items.items[1].filter;
    		this.removeCondition(container, filter);
    	}    	
    },
    
    /**
     * Crea la combo box corrispondente ai tipi di costruttore possibili per il filtro.
	 *
     */
    createBuilderTypeCombo: function() {
        var types = this.allowedBuilderTypes || [
            TolomeoExt.widgets.ToloFilterBuilder.ANY_OF, 
            TolomeoExt.widgets.ToloFilterBuilder.ALL_OF,
            TolomeoExt.widgets.ToloFilterBuilder.NONE_OF
        ];
        var numTypes = types.length;
        var data = new Array(numTypes);
        var type;
        for(var i=0; i<numTypes; ++i) {
            type = types[i];
            data[i] = [type, this.builderTypeNames[type]];
        }
        return {
            xtype: "combo",
            store: new Ext.data.SimpleStore({
                data: data,
                fields: ["value", "name"]
            }),
            value: this.builderType,
            ref: "builderTypeCombo",
            displayField: "name",
            valueField: "value",
            triggerAction: "all",
            queryMode: "local",
            listeners: {
                select: function(combo, records) {
                	var record = records;
                	if(records instanceof Array){
                		record = records[0];
                	}
                    this.changeBuilderType(record.get("value"));
                    this.fireEvent("change", this);
                },
                scope: this
            },
            width: 100
        };
    },

	/**
     * Altera i tipi di filtroquando la combo dei tipi di filtro cambia di valore.
	 * @param {Integer} type elemento da rimuovere.
     */
    changeBuilderType: function(type) {
        if(type !== this.builderType) {
            this.builderType = type;
            var child = this.filter.filters[0];
            switch(type) {
                case TolomeoExt.widgets.ToloFilterBuilder.ANY_OF:
                    this.filter.type = OpenLayers.Filter.Logical.OR;
                    child.type = OpenLayers.Filter.Logical.OR;
                    break;
                case TolomeoExt.widgets.ToloFilterBuilder.ALL_OF:
                    this.filter.type = OpenLayers.Filter.Logical.OR;
                    child.type = OpenLayers.Filter.Logical.AND;
                    break;
                case TolomeoExt.widgets.ToloFilterBuilder.NONE_OF:
                    this.filter.type = OpenLayers.Filter.Logical.NOT;
                    child.type = OpenLayers.Filter.Logical.OR;
                    break;
                case TolomeoExt.widgets.ToloFilterBuilder.NOT_ALL_OF:
                    this.filter.type = OpenLayers.Filter.Logical.NOT;
                    child.type = OpenLayers.Filter.Logical.AND;
                    break;
            }
        }
    },

	/**
     * Crea il pannello che ospita tutte le condizioni e i gruppi di condizioni.
	 * Dato che questo è chiamato dopo che il filtro è stato personalizzato, noi abbiamo sempre 
     * un filtro logico con un filtro figlio che è un filtro logico. 	 
	 * @param {Integer} type elemento da rimuovere.
	 * @return {Ext.Container} il pannello contenitore.
     */
    createChildFiltersPanel: function() {
        this.childFilterContainer = new Ext.Container();
        var grandchildren = this.filter.filters[0].filters;
        var grandchild;
        for(var i=0, len=grandchildren.length; i<len; ++i) {
            grandchild = grandchildren[i];
            var fieldCfg = {
                xtype: "tolomeo_tolofilterfield",
                allowBlank: this.allowBlank,
                columnWidth: 1,
                filter: grandchild,
                attributes: this.attributes,
                autoComplete: this.autoComplete,
                autoCompleteCfg: this.autoCompleteCfg,
				TOLOMEOServer: this.TOLOMEOServer,
				TOLOMEOContext: this.TOLOMEOContext,
                caseInsensitiveMatch: this.caseInsensitiveMatch,
                listeners: {
                    change: function() {
                        this.fireEvent("change", this);
                    },
                    scope: this
                }
            };
            var containerCfg = Ext.applyIf(
                grandchild instanceof OpenLayers.Filter.Logical ?
                    {
                        xtype: "tolomeo_tolofilterbuilder"
                    } : {
                        xtype: "container",
                        layout: "form",
                        hideLabels: true,
                        items: fieldCfg
                    }, fieldCfg
            );
                
            this.childFilterContainer.add(this.newRow(containerCfg));
        }
        
        return this.childFilterContainer;
    },

	/**
     * Genera una nuova condizione per il filtro figlio del pannello. Questo accoppia 
	 * un altro pannello filtro o costruttore di filtro con un componente che consente la rimozione.	 
	 * @param {Ext.Container} filterContainer Il pannello contenitore degli elementi del filtro.
	 * @return {Ext.Container} il pannello contenitore della nuova condizione del filtro.
     */
    newRow: function(filterContainer) {
        var ct = Ext.create('Ext.Container', {
            layout: "column",
            name: "filtercondition_container",
            items: [
                {
	                xtype: "container",
	                width: 28,
	                height: 26,
	                style: "padding-left: 2px",
	                items: [{
	                    xtype: "button",
	                    style: {
	                    	marginTop: '3px'
	                    },
	                    tooltip: this.removeConditionText,
	                    iconCls: "delete",
	                    handler: function(btn){
	                        this.removeCondition(ct, filterContainer.filter);
	                    },
	                    scope: this
	                }]
	            }, filterContainer
            ]
        });
        return ct;
    },

	/**
     * Determina il tipo di costruttore basato sul filtro corrente.	 
	 * @return {Object} il tipo di costruttore.
     */
    getBuilderType: function() {
        var type = this.defaultBuilderType;
        if(this.filter) {
            var child = this.filter.filters[0];
            if(this.filter.type === OpenLayers.Filter.Logical.NOT) {
                switch(child.type) {
                    case OpenLayers.Filter.Logical.OR:
                        type = TolomeoExt.widgets.ToloFilterBuilder.NONE_OF;
                        break;
                    case OpenLayers.Filter.Logical.AND:
                        type = TolomeoExt.widgets.ToloFilterBuilder.NOT_ALL_OF;
                        break;
                }
            } else {
                switch(child.type) {
                    case OpenLayers.Filter.Logical.OR:
                        type = TolomeoExt.widgets.ToloFilterBuilder.ANY_OF;
                        break;
                    case OpenLayers.Filter.Logical.AND:
                        type = TolomeoExt.widgets.ToloFilterBuilder.ALL_OF;
                        break;
                }
            }
        }
        
        return type;
    },

	/**
     * Cambia il filtro associato a questa istanza del costruttore.	 
	 * @param {OpenLayers.Filter} filter Un filtro da impostare.
     */
    setFilter: function(filter) {
        this.filter = this.customizeFilter(filter);
        this.changeBuilderType(this.getBuilderType());
        this.builderTypeCombo.setValue(this.builderType);
        this.form.remove(this.childFilterContainer);
        this.form.insert(1, this.createChildFiltersPanel());
        this.form.doLayout();
        this.fireEvent("change", this);
    }

});

// //////////////////////////////////////////
// Tipi di costruttore
// //////////////////////////////////////////
TolomeoExt.widgets.ToloFilterBuilder.ANY_OF = 0;
TolomeoExt.widgets.ToloFilterBuilder.ALL_OF = 1;
TolomeoExt.widgets.ToloFilterBuilder.NONE_OF = 2;
TolomeoExt.widgets.ToloFilterBuilder.NOT_ALL_OF = 3;
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets');

/**
 * Widget per la visualizzazione del filtro impostato dall'utente nella form.
 * Le modifiche apportate dall'untente al filtro sono formattate in formato stringa.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.ToloFilterView', {

	extend: 'Ext.Panel',
	
	padding: '0 5 0 5',

	/**
     * Inizializza un componente di tipo TolomeoExt.widgets.ToloFilterView.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	initComponent: function(config){	
		this.border = 0;
			
		this.filterTypeStore = Ext.create('Ext.data.Store', {
		    fields: [{
		    	name: 'type', 
		    	mapping: 'type'
		    },{
		    	name: 'name', 
		    	mapping: 'name'
		    }],
		    data: [
	           {type: 1, name: "OGC"},
	           {type: 2, name: "CQL"}
		    ]
		});

		this.filterTypeCombo = Ext.create('Ext.form.ComboBox', {
			typeAhead: true,
			forceSelection: true, 
			width: 200,
			queryMode: 'local',
			triggerAction: 'all',
			selectOnFocus: true,
			editable: false,
			fieldLabel: ToloI18n.getMsg("ToloFilterView.filterTypeCombo"),
			name: 'name',
			value: 1,
			valueField: 'type',
			displayField: 'name',
			store: this.filterTypeStore,
		    listeners:{
		         scope: this,
		         select: function(combo, records, eOpts){
		        	 this.fireEvent("typeselected", records);
		         }
		    }
		});	
		
		this.filterView = Ext.create('Ext.form.TextArea', {
			flex: 1,
	        xtype: 'textareafield',
	        grow: true,
	        name: 'filterView',
	        fieldLabel: ToloI18n.getMsg("ToloFilterView.filterView"),
	        anchor: '100%',
	        labelAlign: "top",
	        height: 100
	    });
		
		var viewContainer = Ext.create('Ext.Panel', {
			border: false,
			height: 120,
		    layout: {
		        type: 'hbox',
		        align: 'middle'
		    },
		    items: [
	            this.filterView,
	            {
			        xtype: 'button',
			        iconCls: "filterviewupdate",
//			        style: "padding-top: 20px;",
			        tooltip: ToloI18n.getMsg("ToloFilterView.btnAggiorna"),
			        handler: function(button){
			        	var selectedRecord = this.filterTypeCombo.findRecordByValue(this.filterTypeCombo.getValue());
			        	this.fireEvent("typeselected", selectedRecord);
			        },
			        scope: this
			    }
	        ]
		});
				
		this.filterViewFieldSet = Ext.create('Ext.form.FieldSet',{
			title: ToloI18n.getMsg("ToloFilterView.filterViewFieldSet"),
			anchor: "-1",
			autoWidth: true,
			autoHeight: true,
			collapsed : true,
			checkboxToggle: true,
			items:[this.filterTypeCombo, viewContainer]
		});
		
		this.callParent();
		
		this.add(this.filterViewFieldSet);
    },
    
	/**
     * Imposta all'interno della TextArea definita la stringa corispondente il filtro selezionato.
     * @param {String} filterString Il filtro in formato stringa.
     */
    setFilter: function(filterString){
    	this.filterView.setRawValue(filterString);
    },
    
	/**
     * Reimposta la TextArea contenente il filro in formato stringa.
     *
     */
    resetView: function(){
    	this.filterView.setRawValue("");
    	this.filterTypeCombo.reset();
    }
    
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets');

/**
 * Widget per la selezione dei layers per cui il plugin query form è abilitato all'uso.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.ToloLayerSelector', {

	extend: 'Ext.Panel',
	
	/**
	 * @cfg {Array} layers
	 * Array dei layers rappresentante lo store.
	 */
	layers: null,
	
	padding: '0 5 0 5',

	/**
     * Inizializza un componente di tipo TolomeoExt.widgets.ToloLayerSelector.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	initComponent: function(config){	
		this.border = 0;
		
		this.layerStore = Ext.create('Ext.data.Store', {
		    fields: [{
		    	name: 'descrizione',
		    	mapping: 'description'
		    },{
		    	name: 'name', 
		    	mapping: 'name'
		    },{
		    	name: 'codTPN', 
		    	mapping: 'codTPN'
		    }],
		    data: this.layers
		});

		this.layerSelectorCombo = Ext.create('Ext.form.ComboBox',{
			typeAhead: true,
			forceSelection: true, 
			anchor: "-1",
			padding: '0 0 5 0',
			queryMode: 'local',
			triggerAction: 'all',
			emptyText: ToloI18n.getMsg("ToloLayerSelector.emptyText"),
			selectOnFocus: true,
			editable:false,
			fieldLabel: ToloI18n.getMsg("ToloLayerSelector.fieldLabel"),
			name: 'name',
			valueField: 'name',
			displayField: 'descrizione',
			store: this.layerStore,
		    listeners:{
		         scope: this,
		         select: function(combo, records, eOpts){
		        	 this.fireEvent("layerselected", records);
		         }
		    }
		});	
		
		this.layerFieldSet = Ext.create('Ext.form.FieldSet', {
			title: ToloI18n.getMsg("ToloLayerSelector.layerFieldSet"),
			anchor: "-1",
			autoWidth: true,
			autoHeight: true,
			items:[this.layerSelectorCombo]
		});
		
		this.callParent();
		
		this.add(this.layerFieldSet);
    }   
	
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets');

/**
 * Widget per la selezione della regione di interesse. Collega 
 * le specifiche funzionalità di di selezione spaziale e le 
 * relative caratteristiche in un'unica form dinamica.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.ToloSpatialSelector', {

	extend: 'Ext.Panel',
	
	padding: '0 5 0 5',
	
	/**
	 * @cfg {Object} spatialSelectorsConfig
     * Configurazione dei metodi di selezione che si vuole rendere disponibili.
     */
    spatialSelectorsConfig:{
	    /**
		 * @cfg {Object} bbox
		 * Metodo di selezione per bounding box.
		 */
        bbox:{
            xtype : 'widget.tolomeo_spatial_bbox_selector'
        },
	    /**
		 * @cfg {Object} buffer
		 * Metodo di selezione pre buffer.
		 */
        buffer:{
            xtype : 'widget.tolomeo_spatial_buffer_selector'
        },
	    /**
		 * @cfg {Object} circle
		 * Metodo di selezione per cerchio.
		 */
        circle:{
            xtype : 'widget.tolomeo_spatial_circle_selector',
            zoomToCurrentExtent : true
        },
	    /**
		 * @cfg {Object} polygon
		 * Metodo di selezione per poligono.
		 */
        polygon:{
            xtype : 'widget.tolomeo_spatial_polygon_selector'
        }
    },

	/**
	 * @cfg {String} defaultSelectionMethod
	 * (xtype) Metodo di selezione da usare come predefinito.
	 */
	defaultSelectionMethod: null,

	/**
	 * @cfg {String} filterGeometryName
	 * None del campo geometrico usato per la preparazione del filtro.
	 */
	filterGeometryName: "the_geom",

	/**
	 * @cfg {String} titleText
	 * Titolo usato per il fielset di contenimento.
	 */
	titleText: null,

	/**
	 * @cfg {String} comboEmptyText
	 * Testo predefinito per la combo box del metodo di selezione spaziale.
	 */
	comboEmptyText : null,

	/**
	 * @cfg {String} comboSelectionMethodLabel
	 * Testo per l'etichetta della combo box di selezione del metodo.
	 */
	comboSelectionMethodLabel : null,
	
	/**
	 * @property {TolomeoExt.events.ToloQueryBuilderEvtManager} qbEventManager
	 * Gestore di eventi per il query builder.
	 */
	qbEventManager: null,
    
	/**
     * Crea un nuovo TolomeoExt.widgets.ToloSpatialSelector.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	constructor : function(config) {
		this.layoutConfig = {
            xtype: 'container',
            defaults:{
                layout: "form"
            }
        };

		// Apply config
		Ext.apply(this, config);

		
		
		// initialize spatial selectors
		this.spatialSelectors = {};
		this.spatialSelectorsItems = [];
		if(this.spatialSelectorsConfig){
			for (var key in this.spatialSelectorsConfig){
				var spConfig = this.spatialSelectorsConfig[key];
				var plugin = Ext.create(spConfig.xtype, {
					qbEventManager: this.qbEventManager
				});
				this.spatialSelectors[key] = plugin;
				var selectorItem = plugin.getSelectionMethodItem();
				selectorItem.value = key;
				this.spatialSelectorsItems.push(selectorItem);
			}	
		}
		
		this.callParent(arguments);
	},

	/**
     * Inizializza un oggetto di tipo TolomeoExt.widgets.ToloSpatialSelector.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	initComponent: function(config){	
		this.border = 0;
		
		this.titleText = ToloI18n.getMsg("ToloSpatialSelector.titleText");
		this.comboEmptyText = ToloI18n.getMsg("ToloSpatialSelector.comboEmptyText");
		this.comboSelectionMethodLabel = ToloI18n.getMsg("ToloSpatialSelector.comboSelectionMethodLabel");

		
    	// prepare layout
    	var layout = {};
		Ext.apply(layout, this.layoutConfig);
		if(!layout.title){
			layout.title = this.titleText;
		}

		var selectionMethodCombo = {
			xtype : 'combo',
			// anchor : '100%',
			id : this.id + '_selectionMethod_id',
			fieldLabel : this.comboSelectionMethodLabel,
			typeAhead : true,
			triggerAction : 'all',
			lazyRender : false,
			queryMode : 'local',
			name : 'roiSelectionMethod',
			forceSelection : true,
			emptyText : this.comboEmptyText,
			allowBlank : false,
			//autoLoad : true,
			displayField : 'label',
			valueField : 'value',
			editable : false,
			readOnly : false,
			store : Ext.create('Ext.data.JsonStore', {
				autoLoad : true,
				fields : [{
					name : 'name',
					dataIndex : 'name'
				}, {
					name : 'label',
					dataIndex : 'label'
				}, {
					name : 'value',
					dataIndex : 'value'
				}],
				data : this.spatialSelectorsItems
			}),
			listeners : {
				select : function(c, record, index) {
					this._updating = true;
					this.reset();
					this._updating = false;
					var method = this.spatialSelectors[c.getValue()];//record.json.method;
					method.activate();
					this.activeMethod = method;
                    setTimeout(function(){
                        //TODO: ??? c.refOwner.doLayout();
                    }, 500);
				},
				scope : this
			}
		};

		var selItems = [];
		selItems.push(selectionMethodCombo);
		
		if(this.spatialSelectors){
	    	for (var key in this.spatialSelectors){
	    		var output = this.spatialSelectors[key];
	    		if(output){
					selItems.push(output);
	    		}
	    	}
	    }

		this.spatialFieldSet = Ext.create('Ext.form.FieldSet', {
			collapsed : true,
			checkboxToggle: true,
			title: this.titleText,
			items: selItems
		});
		
	    // initialize layout
		layout.items = [];		
		layout.items.push(this.spatialFieldSet);

    	this.items = [layout];
    	
    	this.callParent();    	
    	
    	//
    	// Update the current map extent
    	//
    	this.qbEventManager.on("mapmoved", function(extent){
    		this.currentMapExtent = extent;
    	}, this);
    },
 
	/**
     * Reimposta lo stato del selettore spaziale.
	 *
     */
    reset: function(){
    	if(this.spatialSelectors){
	    	for (var key in this.spatialSelectors){
	    		this.spatialSelectors[key].deactivate();
	    		this.activeMethod = null;
	    	}
	    	if(!this._updating 
	    		&& this.defaultSelectionMethod
	    		&& this.spatialSelectors[this.defaultSelectionMethod]){
	    		this.spatialSelectors[this.defaultSelectionMethod].activate();
				this.activeMethod = this.spatialSelectors[this.defaultSelectionMethod];
	    	}   	
    	}
    },

    setGeomFieldName: function(geomFieldName) {
    	this.filterGeometryName = geomFieldName;
    },
    
	/**
     * Genera un filtro per il metodo di selezione scelto.
     * @param {Boolean} currentExtent Stabilisce se ritornare un filtro semplice basato sull'estensione corente della mappa.
     */
	getQueryFilter: function(currentExtent){
		var currentExtentFilter = new OpenLayers.Filter.Spatial({
			type: OpenLayers.Filter.Spatial.BBOX,
			property: this.filterGeometryName,
			value: this.currentMapExtent 
		});
		
		if(currentExtent === true){
			return currentExtentFilter;
		}
		
		if(this.activeMethod && this.activeMethod.currentGeometry){
			this.activeMethod.filterGeometryName = this.filterGeometryName;
			return this.activeMethod.getQueryFilter();
		}else{
			// tornare questo se si vuole almeno il filtro sulla finestra corrente
			// return currentExtentFilter;
			return null;
		}
	},

	/**
     * Restituisce la geometria selezionata.
     * 
     */
	getGeometry: function(){
		if(this.activeMethod){
			return this.activeMethod.currentGeometry;
		}else{
			return null;
		}
	},
	
	/**
     * Restituisce l'elemento ext relativo alla combo box del metodo di selezione spaziale.
     * 
     */
	getSelectionMethodCombo: function(){		
    	var selectionMethodCombo = this.queryById(this.id + '_selectionMethod_id');
    	return  selectionMethodCombo;
	},
	
	setDecimalPrecision: function(decimalPrecision){
		for (var key in this.spatialSelectors){			
			this.spatialSelectors[key].setDecimalPrecision(decimalPrecision);			
		}	
	}
	
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets.grid');

/**
 * Griglia Ext dinamica per gestire la visualizzazione dei risultati 
 * di una ricerca sulla base del filtro composto.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.grid.ToloFeatureGrid', {
	
	extend: 'Ext.grid.GridPanel',
	
    alias: "widget.tolomeo_featuregrid",

	/**
     * @property {Object} schema
	 * Lo schema per la griglia.
     */
	 
	/**
     * @property {Ext.form.DateField.prototype.format} dateFormat
	 * Lo schema per la griglia.
     */
	 
	/**
     * @property {Ext.form.TimeField.prototype.format} timeFormat
	 * Lo schema per la griglia.
     */

	/**
     * @cfg {String} actionTooltip
     *
     */
	actionTooltip: null,
    
	/**
     * Inizializza un nuovo TolomeoExt.widgets.grid.ToloFeatureGrid.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent: function(config){
    	
    	this.actionTooltip = ToloI18n.getMsg("ToloFeatureGrid.actionTooltip");
    	
        if (!this.dateFormat) {
            this.dateFormat = Ext.form.DateField.prototype.format;
        }
        if (!this.timeFormat) {
            this.timeFormat = Ext.form.TimeField.prototype.format;
        }
        
        if(this.store) {
            this.cm = this.createColumnModel(this.store);
        }
        
        this.callParent();
    },

	/**
     * Cancella ogni cosa creata prima di chiamare il distruttore della classe padre.
     * 
     */
    onDestroy: function() {
        TolomeoExt.widgets.grid.FeatureGrid.superclass.onDestroy.apply(this, arguments);
    },
    
	/**
     * Imposta lo store per questa griglia, riconfigurando il modello delle colonne.
     * @param {Ext.Data.Store} store Lo store da impostare.
	 * @param {Array} schema Schema opzionale per determinare i campi appropriati da mostrare per la griglia.
     */
    setStore: function(store, schema) {
        if (schema) {
            this.schema = schema;
        }
        
        if (store) {
            this.reconfigure(store, this.createColumnModel(store));
        }
    },

	/**
     * Restituisce la configurazione per il modello delle colonne.
     * @param {Ext.Data.Store} store Lo store corrente da usare.
	 * @return {Array} Il modello delle colonne
     */
    getColumns: function(store) {
//        function getRenderer(format) {
//            return function(value) {
//                //TODO When http://trac.osgeo.org/openlayers/ticket/3131
//                // is resolved, change the 5 lines below to
//                // return value.format(format);
//                var date = value;
//                if (typeof value == "string") {
//                	date = Date.parseDate(value.replace(/Z$/, ""), "c");
//                }
//                return date = date ? date.format(format) : value;
//            };
//        }
		
        var columns = [{
			xtype: 'actioncolumn',
			header: "", 
			width: 30,
			hidden: false,
			scope: this,
			items: [{
				iconCls: 'zoomaction',
				tooltip: this.actionTooltip,
				scope: this,
				handler: function(grid, rowIndex, colIndex){
					var store = grid.getStore();
					var row = store.getAt(rowIndex);
					var feature = row.data;
					if(feature){
						
						var geometry = OpenLayers.Geometry.fromWKT(feature.geometry);
						var bounds = geometry.getBounds();
						if(bounds){
							
							this.fireEvent("zoomtofeatureextent", {dataExtent: bounds});
// RESTORE THIS
//							grid.getSelectionModel().selectRow(rowIndex);					
						}
					}
				}
			}]
		}];
				
		var name, dbname, type, xtype, format, renderer;	
		
		if(this.schema){			
			var fields = this.store.model.prototype.fields;
			
			for(var i=0; i<this.schema.length; i++){
				var item = this.schema[i];
				
	            if (item) {
	                name = item.get("name");
	                dbname = item.get("dbname");
	                type = item.get("type");
	                format = null;
	                switch (type) {
	                    case "java.util.Date":
	                        format = this.dateFormat;
	                    case "java.util.Calendar":
	                        format = format ? format : this.dateFormat + " " + this.timeFormat;
	                        xtype = undefined;
	                        renderer = Ext.util.Format.dateRenderer(format) //getRenderer(format);
	                        break;
	                    case "java.lang.Boolean":
	                        xtype = "booleancolumn";
	                        break;
	                    case "java.lang.String":
	                        xtype = "gridcolumn";
	                        break;
	                    default:
	                        xtype = "numbercolumn";
	                }
	            } 
	            
                columns.push({
                    dataIndex: dbname,
                    header: name,
                    sortable: true,
                    xtype: xtype,
                    format: format,
                    renderer: xtype ? undefined : renderer
                });
			}
		}
        
        return columns;
    },

	/**
     * Invocare questo metodo per creare il modello delle colonne per la griglia.
     * @param {Ext.Data.Store} store Lo store corrente su cui creare il modello.
	 * 
     */
    createColumnModel: function(store) {
    	 this.columns = this.getColumns(store);
    	 return this.columns;
    }
    
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns("TolomeoExt.widgets.form");

/**
 * Widget per la gestione delle funzionalità relative alla modalità di selezione
 * per BBOX 
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.ToloBBOXFieldset', {
	
	extend: 'Ext.form.FieldSet',
	
    alias: "widget.tolomeo_bboxfieldset",
 
    id: "bboxFieldSet",
  
 	/**
     * @property {String} layerName.
     * Nome da usare per il layer vettoriale che rappresenta il BBOX disegnato.
     */
    layerName: "BBOX",

    /**
     * @cfg {Integer} decimalPrecision.
     * Massimo numero possibile di cifre decimali per i campi di coordinate.
     */
    decimalPrecision: 5,
    
 	/**
     * @property {String} outputSRS.
     * Codice EPSG per la trasformazione delle coordinate in 
     * visualizzazione all'interno della form.
     */
    outputSRS: 'EPSG:4326',

 	/**
     * @cfg {Object} spatialFilterOptions.
     * Opzioni di configurazione per i campi di coordinata (valori massimi e minini consentiti per i campi).
     * 
     * @example
	 *    spatialFilterOptions: {
	 *	        lonMax: 180,   
	 *	        lonMin: -180,
	 *	        latMax: 90,   
	 *	        latMin: -90  
	 *	  }
     */
    spatialFilterOptions: {
        lonMax: null,   
        lonMin: null,
        latMax: null,   
        latMin: null  
    },

    /**
     * @cfg {Boolean} displayBBOXInLayerSwitcher.
     * Usato per determinare se il layer vettoriale deve apparire all'interno del LayerSwitcher OpenLayers.
     */
    displayBBOXInLayerSwitcher: false,

    /**
     * @property {Object} defaultStyle.
     * Configurazione del OpenLayer.Style predefinito usato come stile del BBOX su mappa.
     */
	defaultStyle : {
		"strokeColor" : "#ee9900",
		"fillColor" : "#ee9900",
		"fillOpacity" : 0.4,
		"strokeWidth" : 1
	},

    /**
     * @property {Object} selectStyle.
     * Configurazione del OpenLayer.Style di selezione usato come stile del BBOX su mappa.
     */
	selectStyle : {
		"strokeColor" : "#ee9900",
		"fillColor" : "#ee9900",
		"fillOpacity" : 0.4,
		"strokeWidth" : 1
	},

    /**
     * @property {Object} temporaryStyle.
     * Configurazione del OpenLayer.Style temporaneo usato come stile del BBOX su mappa.
     */
	temporaryStyle : {
		"pointRadius" : 6,
		"fillColor" : "#FF00FF",
		"strokeColor" : "#FF00FF",
		"label" : "Select",
		"graphicZIndex" : 2
	},

 	/**
     * @property {String} northLabel.
     * Testo dell'etichetta per la coordinata Nord.
     */
    northLabel: null,
    
 	/**
     * @property {String} westLabel.
     * Testo dell'etichetta per la coordinata Ovest.
     */
    westLabel: null,
    
 	/**
     * @property {String} eastLabel.
     * Testo dell'etichetta per la coordinata Est.
     */
    eastLabel: null,
    
 	/**
     * @property {String} southLabel.
     * Testo dell'etichetta per la coordinata Sud.
     */
    southLabel: null,
    
 	/**
     * @property {String} setAoiText.
     * Testo di etichetta per il pulsante di attivazione del controllo di disegno del BOX.
     */
    setAoiText: null,
    
 	/**
     * @property {String} setAoiTooltip.
     * Testo per il tooltip del pulsante di attivazione del controllo di disegno del BOX.
     */
    setAoiTooltip: null,
    
    /**
     * @property {String} currentExtentLabel.
     * Labe del checkbox per la selezione dell'estenzione corrente della mappa
     */
    currentExtentLabel: null,
    
 	/**
     * @property {String} title.
     * Titolo del field set di contenimento.
     */
    title: null,

	/**
     * Inizializza un nuovo TolomeoExt.widgets.form.ToloBBOXFieldset.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent: function(config) {       
    	
        this.northLabel = ToloI18n.getMsg("ToloBBOXFieldset.northLabel");
        this.westLabel = ToloI18n.getMsg("ToloBBOXFieldset.westLabel");      
        this.eastLabel = ToloI18n.getMsg("ToloBBOXFieldset.eastLabel");
        this.southLabel = ToloI18n.getMsg("ToloBBOXFieldset.southLabel");
        this.setAoiText = ToloI18n.getMsg("ToloBBOXFieldset.setAoiText");
        this.setAoiTooltip = ToloI18n.getMsg("ToloBBOXFieldset.setAoiTooltip");
        this.currentExtentLabel = ToloI18n.getMsg("ToloBBOXFieldset.currentExtentLabel");
        this.title = ToloI18n.getMsg("ToloBBOXFieldset.title");
    	
    	this.addEvents(
		        /**
				 * @event
				 * Lanciato quando è stata attivata la modalità per prendere le estensioni della mappa
				 */
				"mapExtentActive",
				/**
				 * @event
				 * Lanciato quando è stata disattivata la modalità per prendere le estensioni della mappa
				 */
				"mapExtentDeactive"
		);	
    	
        this.autoHeight = true;
        this.layout = {
            type: 'table',
            // The total column count must be specified here
            columns: 3
        },
		
        this.defaults = {
            // applied to each contained panel
            bodyStyle:'padding:3px'
        };
		
        this.bodyCssClass = 'aoi-fields';
        
        // Define handlar box style
        Ext.util.CSS.createStyleSheet(".olHandlerBoxZoomBox_"+this.id+" {\n"
            +" border-width:" + 5 + "px; \n"
            +" border-style:solid; \n"
            +" border-color: " + "#66cccc" + ";"
            +" position: absolute; \n"
            +" background-color: " + "#66cccc" + "; \n"
            +" opacity: "+0.5+"; \n"
            +" font-size: 1px; \n"
            +" filter: alpha(opacity="+0.5 * 100+"); \n"
            +"}",
            "olHandlerBoxZoomBox_"+this.id);   
        
        var me = this;

        this.northField = Ext.create('Ext.form.NumberField', {
            fieldLabel: me.northLabel,
            labelAlign: "top",
            id: me.id+"_NorthBBOX",
            width: 100,
            allowBlank: false,
            decimalPrecision: me.decimalPrecision,
            allowDecimals: true,
            hideLabel : false                    
        });
        
        this.westField = Ext.create('Ext.form.NumberField', {
            fieldLabel: this.westLabel,
            labelAlign: "top",
            id: me.id+"_WestBBOX",
            width: 100,
            allowBlank: false,
            decimalPrecision: this.decimalPrecision,
            allowDecimals: true,
            hideLabel : false                    
        });
        
        this.eastField = Ext.create('Ext.form.NumberField', {
            fieldLabel: this.eastLabel,
            labelAlign: "top",
            id: me.id+"_EastBBOX",
            width: 100,
            allowBlank: false,
            decimalPrecision: this.decimalPrecision,
            allowDecimals: true,
            hideLabel : false                    
        });
              
        this.southField = Ext.create('Ext.form.NumberField', {
            fieldLabel: this.southLabel,
            labelAlign: "top",
            id: me.id+"_SouthBBOX",
            width: 100,
            allowBlank: false,
            decimalPrecision: this.decimalPrecision,
            allowDecimals: true,
            hideLabel : false                    
        });
        
        if(this.spatialFilterOptions.lonMin && this.spatialFilterOptions.lonMax){
            this.southField.minValue=this.spatialFilterOptions.lonMin;
            this.southField.maxValue=this.spatialFilterOptions.lonMax;
            this.northField.minValue=this.spatialFilterOptions.lonMin;
            this.northField.maxValue=this.spatialFilterOptions.lonMax;
        }
        
        if(this.spatialFilterOptions.latMin && this.spatialFilterOptions.latMax){
            this.eastField.minValue=this.spatialFilterOptions.latMin;
            this.eastField.maxValue=this.spatialFilterOptions.latMax;
            this.westField.minValue=this.spatialFilterOptions.latMin;
            this.westField.maxValue=this.spatialFilterOptions.latMax;
        }
        
        this.bboxButton = Ext.create('Ext.Button', {
            text: this.setAoiText,
            tooltip: this.setAoiTooltip,
            enableToggle: true,
            toggleGroup: this.toggleGroup,
            iconCls: 'aoi-button',
            height: 50,
            width: 50,
            listeners: {
                scope: this, 
                toggle: function(button, pressed) {
                    if(pressed){      
                        //
                        // Activating the new control
                        //   
                        this.selectBBOX.activate();
                    }else{
                        this.selectBBOX.deactivate();
                    }
                }
            }
        }); 
        
        this.mapExtentChkBox = Ext.create('Ext.form.Checkbox', {
        	fieldLabel: this.currentExtentLabel,
        	handler : function(cmp,checked){
        		if(checked){
        			me.fireEvent('mapExtentActive',me);
        		} else {            			
        			me.fireEvent('mapExtentDeactive',me);            			
        		}
        	}
        });
                     
        this.items = [{
        	layout: "form",
            cellCls: 'spatial-cell',            
            border: false,
            colspan: 3,
            items: [this.mapExtentChkBox,{
            	xtype: 'menuseparator'
            }]
        },{
            layout: "form",
            cellCls: 'spatial-cell',
            cls: 'center-align',
            width: 100,
            border: false,
            colspan: 3,
            items: [this.northField]
        },{
            layout: "form",
            cellCls: 'spatial-cell',
            cls: 'center-align',
            width: 100,
            border: false,
            items: [this.westField]
        },{
            layout: "form",
            cellCls: 'spatial-cell',
            cls: 'center-align',
            border: false,
            items: [this.bboxButton]                
        },{
            layout: "form",
            cellCls: 'spatial-cell',
            cls: 'center-align',
            width: 100,
            border: false,
            items: [this.eastField]
        },{
            layout: "form",
            cellCls: 'spatial-cell',
            cls: 'center-align',
            width: 100,
            border: false,
            colspan: 3,
            items: [this.southField]
        }];
            
        this.listeners = {
           "afterlayout": function(){
				if(this.ownerCt.qbEventManager){
					this.ownerCt.qbEventManager.fireEvent("afterboxlayout", {scope: me});
				}
            },
            beforecollapse : function(p) {
				if(this.ownerCt.qbEventManager){
					this.ownerCt.qbEventManager.fireEvent("removelayer", this.layerName);
				}
            }
          
        };

        this.callParent();
    },
    
	/**
     * Reimposta il pannello.
     * 
     */
    reset: function(){
		if(this.ownerCt.qbEventManager){
			this.ownerCt.qbEventManager.fireEvent("removebboxlayer", {scope: this});
		}
        this.northField.reset();
        this.southField.reset();
        this.eastField.reset();
        this.westField.reset(); 
        this.mapExtentChkBox.reset();
		this.fireEvent('unselect', this);
    },

	/**
     * Controlla la validità dei valori inserito.
     * 
     * @return {Boolean} Restituisce true se valido e false se non lo è.
     */
    isValid: function(){
        return(this.westField.isValid() &&
            this.southField.isValid() && 
            this.eastField.isValid() && 
            this.northField.isValid());
    },
    
	/**
     * Controlla se i campi della form sono sttai modificati.
     * 
     * @return {Boolean} Restituisce true se modificati e false altrimenti.
     */
    isDirty: function(){
        return(this.westField.isDirty() &&
            this.southField.isDirty() && 
            this.eastField.isDirty() && 
            this.northField.isDirty());
    },

	/**
     * Restituisce i BBOX selezionato nella proiezione configurata.
     * 
     * @return {OpenLayers.Bounds} Il Bounding Box selezionato.
     */
    getBBOXBounds: function(){
        return new OpenLayers.Bounds(
            this.westField.getValue(), 
            this.southField.getValue(), 
            this.eastField.getValue(), 
            this.northField.getValue()
        );
    },

	setDecimalPrecision: function(decimalPrecision){
		this.northField.decimalPrecision = decimalPrecision;
        this.southField.decimalPrecision = decimalPrecision;
        this.eastField.decimalPrecision = decimalPrecision;
        this.westField.decimalPrecision = decimalPrecision; 
	}
    
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets.form');

/**
 * Widget per la gestione delle funzionalità relative alla modalità di selezione
 * per Buffer 
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.ToloBufferFieldset', {
	
	extend: 'Ext.form.FieldSet',
	
    alias: "widget.tolomeo_bufferfieldset",
    
    id: "bufferFieldSet",   
    
 	/**
     * @property {String} buttonIconCls.
     * Classe di stile che rappresenta l'iconaper il pulsante di selezione.
     */
    buttonIconCls:'gx-buffer',
    
    /**
     * @cfg {Number} labelWidth.
     * Larghezza delle label
     */
    labelWidth: 70,
    
    /**
     * @cfg {Number} fieldWidth.
     * Larghezza dei campi
     */
    fieldWidth : 100,

    /**
     * @cfg {String} bufferFieldLabel.
     * Testo per la label del campo numerico del buffer.
     */
	bufferFieldLabel: null,
	
    /**
     * @cfg {String} bufferFieldSetTitle.
     * Testo per il titolo del field set del buffer.
     */
	bufferFieldSetTitle: null,

    /**
     * @cfg {String} coordinatePickerLabel.
     * Testo per l'etichetta del coordinate picker.
     */
	coordinatePickerLabel: null,
	
    /**
     * @cfg {String} draweBufferTooltip.
     * Testo del il tooltip per il bottone di disegno del buffer.
     */
	draweBufferTooltip: null,
	
 	/**
     * @property {String} outputSRS.
     * Codice EPSG per la trasformazione delle coordinate in 
     * visualizzazione all'interno della form.
     */
	outputSRS: "EPSG:4326",
	
    /**
     * @cfg {String} selectLayerName.
     * Nome del layer vettoriale che rappresenta il buffer su mappa.
     */
	selectLayerName: "buffer-layer",
	
    /**
     * @cfg {Boolean} displayInLayerSwitcher.
     * Usato per determinare se il layer vettoriale deve apparire all'interno del LayerSwitcher OpenLayers.
     */
	displayInLayerSwitcher: false,
	
    /**
     * @property {Object} selectStyle.
     * Configurazione del OpenLayer.Style usato come stile del punto su mappa.
     */
	selectStyle: {
		strokeColor: "#FF0000",
		handlerFillColor: "#FFFFFF",
		fillColor: "#FFFFFF",
		fillOpacity: 0,
		strokeWidth: 2
	},
	
    /**
     * @cfg {Integer} minValue.
     * valore minimo per il raggio del buffer.
     */
	minValue: 1,

    /**
     * @cfg {Integer} maxValue.
     * valore massimo per il raggio del buffer.
     */
	maxValue: 5000,
	
    /**
     * @cfg {Integer} decimalPrecision.
     * Massimo numero possibile di cifre decimali per i campi di coordinate.
     */
	decimalPrecision: 0,
	
    /**
     * @cfg {Boolean} geodesic.
     * 
     */
	geodesic: false,
	
    /**
     * @cfg {Object} config.
     * 
     */
	config: {
	    /**
	     * @cfg {OpenLayer.Layer.Vector} bufferLayer.
	     * 
	     */
        bufferLayer: null
	},
	
	/**
     * Inizializza un nuovo TolomeoExt.widgets.form.ToloBufferFieldset.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent: function(config) {
    	
    	this.bufferFieldLabel = ToloI18n.getMsg("ToloBufferFieldset.bufferFieldLabel");
    	this.bufferFieldSetTitle = ToloI18n.getMsg("ToloBufferFieldset.bufferFieldSetTitle");
    	this.coordinatePickerLabel = ToloI18n.getMsg("ToloBufferFieldset.coordinatePickerLabel");
    	this.draweBufferTooltip = ToloI18n.getMsg("ToloBufferFieldset.draweBufferTooltip");

    	// per adesso lo impongo a prescindere, poi vediamo.
    	this.distanceUnit = 'm';
    	this.bufferFieldLabel += " [" + this.distanceUnit + "]"; 
    	
		this.coordinatePicker = Ext.create('TolomeoExt.widgets.form.ToloCoordinatePicker', {
			labelWidth: this.labelWidth,
			fieldWidth: this.fieldWidth,
			fieldLabel: this.coordinatePickerLabel,
			//fieldBodyCls : 'prova',
			latitudeEmptyText: this.latitudeEmptyText,
			longitudeEmptyText: this.longitudeEmptyText,
			outputSRS: this.outputSRS,
			toggleGroup: this.toggleGroup,
			listeners: {
				scope: this,

                afterrender: function(me) {
                    me.labelCell.applyStyles('vertical-align: middle');
                },

				updatebuffer: function(lonlat, scope){
				    var cv = this.coordinatePicker.isValid();
				    var bv = this.bufferField.isValid();
					if(cv && bv ){                                 
                        var coords = this.coordinatePicker.getCoordinate();
                        var lonlat = new OpenLayers.LonLat(coords[0], coords[1]);
                        var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
                        
        				if(this.qbEventManager){
        					this.qbEventManager.fireEvent("drawbuffer", 
        							point, 
        							this.geodesic, 
        							this.bufferField.getValue(), 
        							this.selectStyle,
        							this.selectLayerName,
        							this.displayInLayerSwitcher,
        							this.setBufferLayer, 
        							this);
        				}
                    }
				},
				reset: function(selectLayerName){
					if(this.qbEventManager){
						this.qbEventManager.fireEvent("removelayer", selectLayerName);
					}
				},
				update: function(lonlat, scope){
					if(this.qbEventManager){
						this.qbEventManager.fireEvent("updatemappoint", lonlat, scope);
					}
				}
			}			
		});
		
		this.bufferField = Ext.create('Ext.form.NumberField', {
			name: 'buffer',
			ref: 'bufferField',
			padding: '10 0 10 0',
			fieldLabel: this.bufferFieldLabel,
			labelWidth: this.labelWidth,
			allowBlank: false,
			disabled: false,
			width: (this.labelWidth + this.fieldWidth + 5), // 5 is default labelPad
			minValue: this.minValue,
            maxValue: this.maxValue,
			enableKeyEvents: true,
		    decimalPrecision: this.decimalPrecision,
			allowDecimals: true,
			hideLabel : false,
			validationDelay: 1500
		});
		
		this.bufferField.addListener("change", function(){   
			if(this.coordinatePicker.isValid() && this.bufferField.isValid()){						
				var coords = this.coordinatePicker.getCoordinate();
				var lonlat = new OpenLayers.LonLat(coords[0], coords[1]);
				var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);								
				
				if(this.qbEventManager){
					this.qbEventManager.fireEvent("drawbuffer", 
							point, 
							this.geodesic, 
							this.bufferField.getValue(), 
							this.selectStyle,
							this.selectLayerName,
							this.displayInLayerSwitcher,
							this.setBufferLayer,
							this);
				}
			}else{
				this.resetBuffer();
			}
		}, this, {delay: 500});
		
		this.items = [
			this.coordinatePicker,
			{
			    xtype: 'menuseparator'
			  },
			this.bufferField
		];
        
		this.title = this.bufferFieldSetTitle;		
		this.callParent();			
    },
	
	/**
     * Reimposta il buffer tramite un evento gestito dal Manager.
     * 
     */
	resetBuffer: function(){
		if(this.qbEventManager){
			this.qbEventManager.fireEvent("removelayer", this.selectLayerName);
		}
	},
	
	/**
     * Controlla la validità del valore inserito.
     * 
     * @return {Boolean} Restituisce true se valido e false se non lo è.
     */
	isValid: function(){
		return(this.coordinatePicker.isValid() &&
			this.bufferField.isValid());
	},
	
	/**
     * Avvia la procedura di reimpostazione del buffer.
     * 
     */	
	resetPointSelection: function(){
		this.coordinatePicker.resetPoint();
        this.bufferField.reset();
		this.resetBuffer();
	},
	
	/**
     * Imposta il layer OpenLayers per il buffer.
     * @param {OpenLayers.Layer.Vector} bufferLayer Il layer vettoriale OpenLayers che rappresenta il buffer.
     * @param {OpenLayers.Feature.Vector} bufferFeature La feature vettoriale da disegnare du mappa.
     */
	setBufferLayer: function(bufferLayer, bufferFeature){
		this.bufferLayer = bufferLayer;
		this.fireEvent('bufferadded', this, bufferFeature);
	},
	
	setDecimalPrecision: function(decimalPrecision){
		this.coordinatePicker.setDecimalPrecision(decimalPrecision);
	}
	
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets.form');

/**
 * Una combo box Ext per selezionare gli operatori di comparazione disponibili 
 * per i filtri OGC.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.ToloComparisonComboBox', {
	
	extend: 'Ext.form.ComboBox',
	
	alias: 'widget.tolomeo_comparisoncombo',
    
	displayTpl:'<tpl for=".">{[values.text]}</tpl>',
	
	listConfig: {
		 
        // Custom rendering template for each item
        getInnerTpl: function() {                	        
        	// return '{[(values.tipTitle || values.tipText)  ? values.text + " <div data-qtitle=\\"" + values.tipTitle + "\\"  data-qtip=\\"" + values.tipText + "\\" style=\\"color:gray; border-bottom: dashed 1px gray; border-top: dashed 1px gray; \\">" + values.tipText + "</div>" : values.text]}';
        	return '{[(values.tipTitle || values.tipText)  ? " <div data-qtitle=\\"" + values.tipTitle + "\\"  data-qtip=\\"" + values.tipText + "\\" >" + values.text + "</div>" : values.text]}';
        }
	},
	
 	/**
     * @property {Array} allowedTypes.
     * Tipi di operatori disponibili.
     */
    allowedTypes: null,

 	/**
     * @cfg {Boolean} allowBlank.
     * Stabilisce se consentire testo vuoto all'interno della combo.
     */
    allowBlank: false,

 	/**
     * @cfg {String} mode.
     * Stabilisce il metodo di caricamento dello store.
     */
    mode: "local",

 	/**
     * @cfg {Boolean} typeAhead.
     * 
     */
    typeAhead: true,

 	/**
     * @cfg {Boolean} forceSelection.
     * 
     */
    forceSelection: true,

 	/**
     * @cfg {String} triggerAction.
     * 
     */
    triggerAction: "all",

 	/**
     * @cfg {Integer} width.
     * 
     */
    width: 80,

 	/**
     * @cfg {Boolean} editable.
     * 
     */
    editable: true,
    
    /**
     * @cfg {String} editable.
     * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used don't change
     */
    lastQuery: '',
  
	/**
     * Inizializza un nuovo TolomeoExt.widgets.form.ToloComparisonComboBox.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent: function(config) {
    	
    	this.allowedTypes = [
            [OpenLayers.Filter.Comparison.EQUAL_TO, "=", ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.uguale.tiptitle"), ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.uguale.tiptext")],
            [OpenLayers.Filter.Comparison.NOT_EQUAL_TO, "<>", ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.diverso.tiptitle"), ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.diverso.tiptext")],
            [OpenLayers.Filter.Comparison.LESS_THAN, "<", ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.minore.tiptitle"), ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.minore.tiptext")],
            [OpenLayers.Filter.Comparison.GREATER_THAN, ">", ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.maggiore.tiptitle"), ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.maggiore.tiptext")],
            [OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO, "<=", ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.minug.tiptitle"), ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.minug.tiptext")],
            [OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO, ">=", ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.magug.tiptitle"), ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.magug.tiptext")],
            [OpenLayers.Filter.Comparison.LIKE, "like", ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.like.tiptitle"), ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.like.tiptext")],
            // simulate ilike operator (not match case)
            ["ilike", "ilike", ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.ilike.tiptitle"), ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.ilike.tiptext")],
            [OpenLayers.Filter.Comparison.BETWEEN, "between", ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.between.tiptitle"), ToloI18n.getMsg("ToloComparisonComboBox.allowedTypes.between.tiptext")]
        ];

    	
        var defConfig = {
            displayField: "text",
            valueField: "value",
            store: new Ext.data.SimpleStore({
                data: this.allowedTypes,
                fields: ["value", "text", "tipTitle", "tipText"]
            }),
            value: (this.value === undefined) ? this.allowedTypes[0][0] : this.value,
            listeners: {
                // workaround for select event not being fired when tab is hit
                // after field was autocompleted with forceSelection
                "blur": function() {
                    var index = this.store.findExact("value", this.getValue());
                    if (index != -1) {
                        this.fireEvent("select", this, this.store.getAt(index));
                    } else if (this.startValue != null) {
                        this.setValue(this.startValue);
                    }
                }
            }
        };
        
        Ext.applyIf(this, defConfig);
        
        this.callParent();
        
    }

});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets.form');

/**
 * Widget relativa al Form Container per la gestione del campo di selezione 
 * delle coordinate puntuali su mappa. 
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.ToloCoordinatePicker', {
	
	extend: 'Ext.form.FieldContainer',

    alias: 'tolomeo_coordinate_picker',
	
	/**
     * @property {String} fieldLabel.
     *
     */
    fieldLabel: null,
	
    /**
     * @cfg {Number} fieldWidth.
     * Larghezza dei campi
     */
    fieldWidth : 100,
    
	/**
     * @property {String} pointSelectionButtionTip.
     *
     */
    pointSelectionButtionTip: null,
	
	/**
     * @property {String} latitudeEmptyText.
     * Stringa da mostrare per componente non valorizzato (latitudine)
     */
    latitudeEmptyText: null,
	 
 	/**
      * @property {String} longitudeEmptyText.
      * Stringa da mostrare per componente non valorizzato (longitudine)
      */
    longitudeEmptyText: null,
	 
 	/**
     * @property {String} outputSRS.
     * Codice EPSG per la trasformazione delle coordinate in 
     * visualizzazione all'interno della form.
     */
    outputSRS: 'EPSG:4326',
    
 	/**
     * @property {String} buttonIconCls.
     * Classe di stile usata per l'icona del pulsante di selezione.
     */
    buttonIconCls:'gx-cursor',

    /**
     * @property {Object} selectStyle.
     * Configurazione del OpenLayer.Style usato come stile del punto su mappa.
     */
    selectStyle:{
        pointRadius: 4,
        graphicName: "cross",
        fillColor: "#FFFFFF",
        strokeColor: "#FF0000",
        fillOpacity:0.5,
        strokeWidth:2
    },

    /**
     * @property {Object} defaultSelectStyle.
     * Configurazione del OpenLayer.Style usato per riempire i campi mancati di "selectStyle".
     */
    defaultSelectStyle:{
        pointRadius: 4, // sized according to type attribute
        graphicName: "cross",
        fillColor: "#0000FF",
        strokeColor: "#0000FF",
        fillOpacity:0.5,
        strokeWidth:2        
    },
	
    /**
     * @cfg {Integer} decimalPrecision.
     * Massimo numero possibile di cifre decimali per i campi di coordinate.
     */
    decimalPrecision: 10,
    
    /**
     * @cfg {String} selectLayerName.
     * Nome del layer vettoriale che rappresenta il punto su mappa.
     */
    selectLayerName: "select_marker_position_layer",
    
    /**
     * @cfg {Boolean} displayInLayerSwitcher.
     * Usato per determinare se il layer vettoriale deve apparire all'interno del LayerSwitcher OpenLayers.
     */
    displayInLayerSwitcher: false,
    
	/**
     * Inizializza un nuovo TolomeoExt.widgets.form.ToloCoordinatePicker.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent:function(config){
    	/*
        this.layout = {
            type: 'hbox'
        };
        */
    	
    	this.fieldLabel = ToloI18n.getMsg("ToloCoordinatePicker.fieldLabel");
        this.pointSelectionButtionTip = ToloI18n.getMsg("ToloCoordinatePicker.pointSelectionButtionTip");
        this.latitudeEmptyText = ToloI18n.getMsg("ToloCoordinatePicker.latitudeEmptyText");
        this.longitudeEmptyText = ToloI18n.getMsg("ToloCoordinatePicker.longitudeEmptyText");

    	
        this.layout = {
                type: 'table',
                // The total column count must be specified here
                columns: 2
            };
        
        this.defaults = {
            // applied to each contained panel
            bodyStyle:'padding:0px;'
        };
		
        var compositeField = this;
		
		Ext.applyIf(this.selectStyle, this.defaultSelectStyle);
	
		this.items= [{
            layout: "form",
            cellCls: 'spatial-cell',
            cls: 'center-align',
            width: this.fieldWidth,
            border: false,            
            items: [{
                xtype     : 'numberfield',
                emptyText : this.longitudeEmptyText,	
                ref:'longitudeField',
                decimalPrecision:this.decimalPrecision,
                allowBlank: false,
                name: 'lon',
				listeners: {
					scope:this,
					change: this.updatePoint
				}
            }]
        },{
            layout: "form",
            cellCls: 'spatial-cell',
            cls: 'center-align',            
            rowspan: 2,
            border: false,
            bodyStyle:'padding:10px;',
            items: [{
                layout: "form",
                cellCls: 'spatial-cell',
                cls: 'center-align',
                border: false,
                items: [{
                    xtype: 'button',
    				ref:'clickToggle',
                    tooltip: this.pointSelectionButtionTip,
                    iconCls: this.buttonIconCls,
                    enableToggle: true,
                    toggleGroup: this.toggleGroup,
                    scale: 'large',
                    width: 40,
                    height: 40,
                    listeners: {
                      scope: this, 
                      toggle: function(button, pressed) {  
                         if(pressed){
                              this.selectLonLat.activate();
                          }else{
                              this.selectLonLat.deactivate();
                          }
                      }
                    }
                }]                
            }]
        },{
            layout: "form",
            cellCls: 'spatial-cell',
            cls: 'center-align',
            width: this.fieldWidth,
            border: false,
            items: [ {
                xtype     : 'numberfield',
                emptyText : this.latitudeEmptyText,
                ref: 'latitudeField',
                decimalPrecision: this.decimalPrecision,
                allowBlank:false,
                name: 'lat',
				listeners: {
					scope:this,
					change: this.updatePoint
				}
            }]
        }];
        this.callParent(arguments);
        
        this.on("added", function(scope){
        	scope.latitudeField = scope.query('numberfield[ref=latitudeField]')[0];
        	scope.longitudeField = scope.query('numberfield[ref=longitudeField]')[0];
        	scope.clickToggle = scope.query('button[ref=clickToggle]')[0];
        	scope.updateStep();
        });                
    },
    
	/**
     * Controlla la validità dei valori inseriti.
     * 
     * @return {Boolean} Restituisce true se valido e false se non lo è.
     */
    isValid: function(){
    	if(this.latitudeField.isValid() &&
    	    	this.longitudeField.isValid()){
    		return true;
    	}else{
    		return false;
    	}
    },
	
	/**
     * Prende i valori dai campi e avvia la procedura di disegno sulla mappa.
     * 
     */
    updatePoint: function(){
        var lat = this.latitudeField.getValue();
		var lon = this.longitudeField.getValue();
		if( lon && lat ){
			//add point
			var lonlat = new OpenLayers.LonLat(lon,lat);
			
			 /*  DECOMMENTARE SE SI VUOLE visualizzare le coordinate nel sistema di riferimento impostato per il buffer selector
            
	            var tPoint = new Point(lonlat.lon, lonlat.lat);
	            tPoint.transform(this.outputSRS,this.projectionObject.getCode());
	            
	            lonlat.lon = tPoint.x;
	            lonlat.lat = tPoint.y;
	        */						
			
			this.updateMapPoint(lonlat);
		}
    },
	
	/**
     * Lancia l'evento per la rimozione del layer vettoriale 
     * che rappresente la selezione del punto sulla mappa.
     * 
     */
    resetMapPoint:function(){
    	this.fireEvent("reset", this.selectLayerName);
    },

	/**
     * Reimposta i campi della form e rimuove il layer dalla mappa.
     *
     */
    resetPoint:function(){
		this.latitudeField.reset();
        this.longitudeField.reset();
		
        this.resetMapPoint();
	},

	/**
     * Usato per impostare la pressione del pulsante di attivazione 
     * del controllo di disegno.
     * @param {Boolean} toggle valore del toggle da impostare per il componente Ext.
     */
	toggleButton: function(toggle){
		this.clickToggle.toggle(toggle);
	},
	
	/**
     * Aggiorna il punto sulla mappa tramite eventi gestiti dal Manager a livello superiore.
     * del controllo di disegno.
     * @param {OpenLayers.LonLat} lonlat valore delle coordinate da usare per il punto su mappa.
     */
    updateMapPoint:function(lonlat){
        if(this.selectStyle){
        	this.resetMapPoint();
        	this.fireEvent("update", lonlat, this);
        	this.fireEvent("updatebuffer", lonlat, this);
        }    
    },
	 
	/**
     * Recupera le coordinate dai campi della form.
     * del controllo di disegno.
     * @return {Array} Array delle coordinate.
     */
	getCoordinate: function(){
		return [this.longitudeField.getValue(), this.latitudeField.getValue()];
	},
	
	setDecimalPrecision: function(decimalPrecision){
		this.decimalPrecision = decimalPrecision;
		this.latitudeField.decimalPrecision = decimalPrecision;
    	this.longitudeField.decimalPrecision = decimalPrecision;
    	this.updateStep();
	},
	
	updateStep: function(){
		var step = 1/Math.pow(10,this.decimalPrecision-2);
		this.latitudeField.step  = step;
    	this.longitudeField.step = step;
	}
    
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets.form');

/**
 * Un campo Form che rappresenta un filtro di comparazione
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.ToloFilterField', {
	
	extend: 'Ext.form.FieldContainer',
	
	alias: 'widget.tolomeo_tolofilterfield',
	    
	/**
     * @cfg {String} lowerBoundaryTip
     * Tooltip per il campo inferiore di valorizzazione.
     *
     */
    lowerBoundaryTip: null,
     
	/**
     * @cfg {String} upperBoundaryTip
     * Tooltip per il campo superiore di valorizzazione.
     *
     */
    upperBoundaryTip: null,
    
	/**
     * @cfg {String} invalidRegExText
     * Testo mostrato in caso di valore campo non valido.
     *
     */
    invalidRegExText: null,
     
	/**
     * @cfg {Boolean} caseInsensitiveMatch [caseInsensitiveMatch="false"]
	 * Il filtro di comparazione per i campi di tipo stringa deve essere case insensitive ?
     */
    caseInsensitiveMatch: false,

	/**
     * @property {OpenLayers.Filter} filter
	 * Filtro non logico opzionale messo a disposizione nella configurazione iniziale. Per 
	 * recuperare il filtro usare il metodo ''getFilter'' invece di accedere direttamente questa 
	 * proprietà.
     */
    filter: null,
    
	/**
     * @property {Ext.DataStore} attributes
	 * Rappresenta lo store configurato degli attributi del layer 
	 * da usare all'interno della combo box di filtraggio delle proprietà.
     */
    attributes: null,

	/**
     * @property {Object} comparisonComboConfig
	 * Oggetto di configurazione per la combo box di comparazione.
     */

	/**
     * @property {Object} attributesComboConfig
	 * Oggetto di configurazione per la combo box degli attributi.
     */
    attributesComboConfig: null,
    
	/**
     * @cfg {Boolean} autoComplete [autoComplete="false"]
	 * Abilita la funzionalità di autocompletamento per i campi stringa.
     */
    autoComplete: false,
    
	/**
     * @cfg {Object} autoCompleteCfg [autoCompleteCfg="{}"]
	 * Stabilisce la configurazione da usare per la funzionalità di autocompletamento.
	 *
	 * @example
	 * autoCompleteCfg: {
	 *  	url: 'http://localhost:8080/tolomeobinj/UniqueValueServlet',
	 *		pageSize: 10
	 * }
     */
    autoCompleteCfg: {},
    
	/**
     * @cfg {Integer} pageSize [autoComplete="5"]
	 * Configura il numero massimo predefinito di elementi per pagina per la 
	 * combo box di autocompletamento.
     */
    pageSize: 5,
    
	/**
     * Inizializza un nuovo TolomeoExt.widgets.form.ToloFilterField.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent: function(config) {
    	
        this.lowerBoundaryTip = ToloI18n.getMsg("ToloFilterField.lowerBoundaryTip");
        this.upperBoundaryTip = ToloI18n.getMsg("ToloFilterField.upperBoundaryTip");
        this.invalidRegExText = ToloI18n.getMsg("ToloFilterField.invalidRegExText");
    	
        var me = this;
        
        this.combineErrors = false;
        
        if (!this.dateFormat) {
            this.dateFormat = Ext.form.DateField.prototype.format;
        }
        if (!this.timeFormat) {
            this.timeFormat = Ext.form.TimeField.prototype.format;
        }        
        if(!this.filter) {
            this.filter = this.createDefaultFilter();
        }
        
        var mode = "local";
        var attributes = this.attributes;
        
        this.createDefaultConfigs();
        
        var defAttributesComboConfig = {
            xtype: "combo",
            store: attributes,
            editable: false,
            typeAhead: true,
            forceSelection: true,
            queryMode: mode,
            triggerAction: "all",
            ref: "property",
            allowBlank: this.allowBlank,
            displayField: "name",
            valueField: "dbname",
            value: this.filter.property,
            listeners: {
                select: function(combo, records) {
                	var record = records;
                	if(records instanceof Array){
                		record = records[0];
                	}
                	
                    this.filter.property = record.get("dbname");
                    this.fieldType = record.get("type");
                    this.fieldRegEx = record.get("regex");
                    this.layerCodeTPN = record.get("codTPN");
                    this.attributeLogicalName = record.get("attributeLogicalName");
                    
                    // ////////////////////////////////////////////////////////////////
                    // Check if the autocomplete must be disabled for the single field
                    // ////////////////////////////////////////////////////////////////
                    var autocomplete = record.get("autocomplete");
                    this.autoCompleteFieldCheck = autocomplete.active != undefined ? 
                    		autocomplete.active && this.autoComplete : this.autoComplete;
                    
                    if(this.autoCompleteFieldCheck){
                    	var mode = autocomplete.mode || "remote";
                    	Ext.copyTo(this.autoCompleteCfg, {
                    		 autoCompleteMode: mode,
                    		 minChars: mode == "remote" ? autocomplete.minChars || 1 : 1
                    	}, ["autoCompleteMode", "minChars"]);
                    	
                        //
                        // Update the this.autoCompleteDefault config with minChars retrieved by the server
                        //
                        this.autoCompleteDefault["single"].minChars = this.autoCompleteCfg.minChars;
                        this.autoCompleteDefault["lower"].minChars = this.autoCompleteCfg.minChars;
                        this.autoCompleteDefault["upper"].minChars = this.autoCompleteCfg.minChars;
                        
                        this.autoCompleteDefault["single"].pageSize = mode == "remote" ? 
                        		this.autoCompleteCfg.pageSize || this.pageSize : 0;
                        this.autoCompleteDefault["lower"].pageSize = mode == "remote" ? 
                        		this.autoCompleteCfg.pageSize || this.pageSize : 0; 
                        this.autoCompleteDefault["upper"].pageSize = mode == "remote" ? 
                        		this.autoCompleteCfg.pageSize || this.pageSize : 0;
                    }
                    
                    if(!this.comparisonCombo) {
                        this.comparisonCombo = this.items.get(1);
                    }
                    
                    this.comparisonCombo.enable();
                    this.comparisonCombo.reset();
                    
                    if(!this.valueWidgets) {
                        this.valueWidgets = this.items.get(2);
                    }
                    this.valueWidgets.removeAll();
                    
                    this.setFilterType(null);
        
                    this.doLayout();
                    this.fireEvent("change", this.filter, this);
                },
                // workaround for select event not being fired when tab is hit
                // after field was autocompleted with forceSelection
                "blur": function(combo) {
                    var index = combo.store.findExact("dbname", combo.getValue());
                    if (index != -1) {
                        combo.fireEvent("select", combo, combo.store.getAt(index));
                    } else if (combo.startValue != null) {
                        combo.setValue(combo.startValue);
                    }
                },
                scope: this
            },            
            width: 200
        };
        
        var defComparisonComboConfig = {
            xtype: "tolomeo_comparisoncombo",
            ref: "type",
            disabled: true,
            editable: false,
            allowBlank: this.allowBlank,
            value: this.filter.type,
            listeners: {
                select: function(combo, records) {        
                	var record = records;
                	if(records instanceof Array){
                		record = records[0];
                	}
                    this.createValueWidgets(record.get("value"));
                },
                expand: function(combo) {
                    var store = combo.getStore();
                    store.clearFilter();
                    if(this.fieldType === "java.util.Date" || 
                    		this.fieldType === "java.util.Calendar" || 
                    		this.fieldType === "'java.math.BigInteger" || 
                    		this.fieldType === "java.lang.Double" || 
                    		this.fieldType === "java.math.BigDecimal" || 
                    		this.fieldType === "java.lang.Integer" || 
                    		this.fieldType === "java.lang.Long" || 
                    		this.fieldType === "java.lang.Float" || 
                    		this.fieldType === "java.lang.Short" ||
                    		this.fieldType === "java.lang.Number"){
                        store.filter([
                          {
                            fn   : function(record) {
                                return (record.get('text') != "like") && (record.get('text') != "ilike");
                            },
                            scope: this
                          }                      
                        ]);
                    }else if(this.fieldType === "java.lang.Boolean"){
                        store.filter([
                          {
                            fn   : function(record) {
                                return (record.get('name') == "=");
                            },
                            scope: this
                          }                      
                        ]);
                    }else if(this.fieldType === "java.lang.String"){
                        store.filter([
                          {
                            fn   : function(record) {
                            	return (record.get('text') != "between");
                            },
                            scope: this
                          }                      
                        ]);
                    }  
                },
                scope: this
            },            
            width: 200        
        };
        
        this.attributesComboConfig = this.attributesComboConfig || {};
        Ext.applyIf(this.attributesComboConfig, defAttributesComboConfig);
        
        this.comparisonComboConfig = this.comparisonComboConfig || {};        
        Ext.applyIf(this.comparisonComboConfig, defComparisonComboConfig);

        this.items = [this.attributesComboConfig, this.comparisonComboConfig, {
            xtype: 'container',
            isFormField: true,
            isValid: function() { return true; },
            reset: function() {
                 this.eachItem(function(a) {
                    a.reset()
                });
            },
            eachItem: function(b, a) {
                if (this.items && this.items.each) {
                    this.items.each(b, a || this)
                }
            },
            layout  : 'hbox',            
            defaultMargins: '0 3 0 0',
            width: 200
        }];
        
        this.addEvents(
			/**
			 * @event 
			 * Lanciato a seguito di un cambiamento del valore del componente.
			 */
            "change"
        ); 

        this.callParent();
    },
    
	/**
     * Crea e aggiunge alla form una combo box di auto completamento.
     * @param {Object} config Un opzionale oggetto di configurazione per il componente ExtJs.
     * @return {Object} Ritorna l'oggetto relativo alla combobox di auto completamento.
     */
    addAutocompleteStore: function(config) {
        var uniqueValuesStore = new TolomeoExt.data.ToloUniqueValuesStore({
            pageSize: this.autoCompleteCfg.autoCompleteMode == "remote" ? this.autoCompleteCfg.pageSize || this.pageSize : 0,
			TOLOMEOServer: this.TOLOMEOServer,
			TOLOMEOContext: this.TOLOMEOContext
        });
        
        this.initUniqueValuesStore(uniqueValuesStore, this.layerCodeTPN, this.filter.property,this.attributeLogicalName);
        
        return Ext.apply(Ext.apply({}, config), {store: uniqueValuesStore});
    },
    
	/**
     * Se lo store degli attributi contiene anche una RegEx di validazione, applica 
     * il validatore al componente Ext che rappresenta il valore.
     * 
     * @param {String} type Tipo dell'attributo relativo per configurare il componente.
     * @return {Object} Ritorna la configurazione relativa alla campo valore della proprietà.
     */
    createValueWidget: function(type) {
        if(this.autoComplete && this.autoCompleteFieldCheck && this.fieldType === 'java.lang.String') {
            return Ext.apply({}, this.addAutocompleteStore(this.autoCompleteDefault[type]));
        } else {
        	var config = {};
        	if(this.fieldRegEx){
        		var me = this;
        		var valueTest = new RegExp(this.fieldRegEx);
        		config = Ext.apply(config, {
                    validator: function(value) {
                        return valueTest.test(value) ? true : me.invalidRegExText;
                    }
        		});
        	}
        	
            return Ext.apply(config, this.fieldDefault[type][this.fieldType]);
        }
    },
    
	/**
     * Crea il componente Ext destinato a contenere il valore delle proprietà.
     * @param {String} type Tipo dell'attributo relativo per configurare il componente.
     * 
     */
    createValueWidgets: function(type) {
        if((type !== this.filter.type) || (this.filter.type == OpenLayers.Filter.Comparison.LIKE)) {
        	
            this.setFilterType(type);
            
            if(!this.valueWidgets) {
                this.valueWidgets = this.items.get(2);
            }
            this.valueWidgets.removeAll();
            if (type === OpenLayers.Filter.Comparison.BETWEEN) {
                this.valueWidgets.add(this.createValueWidget('lower'));
                this.valueWidgets.add(this.createValueWidget('upper'));
            } else {
            	var vw = this.createValueWidget('single');
            	vw.width = vw.maxWidth;    
            	/*
            	if (type === OpenLayers.Filter.Comparison.LIKE || type === 'ilike') {
            		vw.emptyText = this.wildCardText
            	} 
            	*/           	               
                this.valueWidgets.add(vw);
            }
            
            this.doLayout();
            
            this.fireEvent("change", this.filter, this);
        }
    },
    
	/**
     * Imposta la configurazione predefinita per la gestione dinamica dei componenti.
     * 
     */
    createDefaultConfigs: function() {
        this.defaultItemsProp = {
            'single': {
                validateOnBlur: false,
                ref: "value",
                // grow: true,
                // growMin: 80,
                width: 80,
                anchor: "100%",
                allowBlank: this.allowBlank,
                listeners: {
                    "change": function(field, value) {
                        this.filter.value = value;
                        this.fireEvent("change", this.filter, this);
                    },
                    "blur": function(field){
                    
                    },
                    scope: this
                }   
            },
            
           'lower': {
                // grow: true,
                // growMin: 80,
                width: 80,
                ref: "lowerBoundary",
                anchor: "100%",
                allowBlank: this.allowBlank,
                listeners: {
                    "change": function(field, value) {
                        this.filter.lowerBoundary = value;
                        this.fireEvent("change", this.filter, this);
                    },
                    "autosize": function(field, width) {
                        field.setWidth(width);
                        field.ownerCt.doLayout();
                    },
                    scope: this
                }
            },
            
            'upper': {
                // grow: true,
                // growMin: 80,
                width: 80,
                ref: "upperBoundary",
                allowBlank: this.allowBlank,
                listeners: {
                    "change": function(field, value) {
                        this.filter.upperBoundary = value;
                        this.fireEvent("change", this.filter, this);
                    },

                    scope: this
                }
            }
        };
        
        this.fieldDefault = {};
        
        var maxFieldWidth = 200; // 70 - 80
        var numberFieldWidth = maxFieldWidth/2; 
        for(key in this.defaultItemsProp) {
            this.fieldDefault[key] = {
                'java.lang.String': Ext.applyIf({
                    xtype: "textfield",
                    width: maxFieldWidth,
                    maxWidth: maxFieldWidth
                }, this.defaultItemsProp[key]),
                'java.lang.Double': Ext.applyIf({
                    xtype: "numberfield",
                    allowDecimals:true,
                    decimalPrecision: 10,
                    width: numberFieldWidth,
                    maxWidth: maxFieldWidth
                },this.defaultItemsProp[key]),
                'java.lang.Float': Ext.applyIf({
                    xtype: "numberfield",
                    allowDecimals:true,
                    decimalPrecision: 10,
                    width: numberFieldWidth,
                    maxWidth: maxFieldWidth
                },this.defaultItemsProp[key]),
                'java.math.BigDecimal': Ext.applyIf({
                    xtype: "numberfield",
                    allowDecimals:true,
                    decimalPrecision: 10,
                    width: numberFieldWidth,
                    maxWidth: maxFieldWidth
                },this.defaultItemsProp[key]),
                'java.math.BigInteger': Ext.applyIf({
                    xtype: "numberfield",
                    allowDecimals:false,
                    width: numberFieldWidth,
                    maxWidth: maxFieldWidth
                },this.defaultItemsProp[key]),
                'java.lang.Integer': Ext.applyIf({
                    xtype: "numberfield",
                    allowDecimals:false,
                    width: numberFieldWidth,
                    maxWidth: maxFieldWidth
                },this.defaultItemsProp[key]),
                'java.lang.Long': Ext.applyIf({
                    xtype: "numberfield",
                    allowDecimals:false,
                    width: numberFieldWidth
                },this.defaultItemsProp[key]),
                'java.lang.Short': Ext.applyIf({
                    xtype: "numberfield",
                    allowDecimals:false,
                    width: numberFieldWidth,
                    maxWidth: maxFieldWidth
                },this.defaultItemsProp[key]),
                'java.util.Date': Ext.applyIf({
                    xtype: "datefield",
                    width: numberFieldWidth,
                    maxWidth: maxFieldWidth,
                    allowBlank: false,
                    format: this.dateFormat
                },this.defaultItemsProp[key]),
                'java.util.Calendar': Ext.applyIf({
                    xtype: "datefield",
                    width: numberFieldWidth,
                    maxWidth: maxFieldWidth,
                    allowBlank: false,
                    format: this.dateFormat
                },this.defaultItemsProp[key])
            };
        }
        
        this.autoCompleteDefault = {        
            'single': Ext.applyIf({
                xtype: "tolomeo_uniquevaluescb",
                queryMode: "remote", // required as the combo store shouldn't be loaded before a field name is selected
                pageSize: this.autoCompleteCfg.pageSize || this.pageSize,
//                pageSize: this.autoCompleteCfg.autoCompleteMode == "remote" ? this.autoCompleteCfg.pageSize || this.pageSize : 0,
                typeAhead: true,
                forceSelection: false,
                remoteSort: true,
                triggerAction: "all",
                allowBlank: this.allowBlank,
                displayField: "value",
                valueField: "value",
                matchFieldWidth: false,
                listConfig:{
                	width: 200
                },
                minChars: this.autoCompleteCfg.minChars || 1,

//                resizable: true,
                listeners: {
                    select: function(combo, record) {
                        this.filter.value = combo.getValue();
                        this.fireEvent("change", this.filter);
                    },
                    blur: function(combo) {
                        this.filter.value = combo.getValue();
                        this.fireEvent("change", this.filter);
                    },
                    beforequery: function(evt) {
                        evt.combo.store.baseParams.start = 0;
                        //                        evt.combo.store.baseParams.query = evt.combo.getValue();
                        evt.combo.store.baseParams.query = this.autoCompleteCfg.autoCompleteMode == "remote" ? evt.combo.getValue() : "*";

                    },
                    scope: this
                },
                matchFieldWidth: false,
                anchor: "100%",
                flex:1
            },this.defaultItemsProp['single']),
            'lower': Ext.applyIf({
                xtype: "tolomeo_uniquevaluescb",
                queryMode: "remote", // required as the combo store shouldn't be loaded before a field name is selected
                pageSize: this.autoCompleteCfg.pageSize || this.pageSize,
//              pageSize: this.autoCompleteCfg.autoCompleteMode == "remote" ? this.autoCompleteCfg.pageSize || this.pageSize : 0,
                typeAhead: true,
                forceSelection: false,
                remoteSort: true,
                triggerAction: "all",
                allowBlank: this.allowBlank,
                displayField: "value",
                valueField: "value",
                minChars: this.autoCompleteCfg.minChars || 1,
//                resizable: true,
                listeners: {
                    select: function(combo, record) {
                        this.filter.lowerBoundary = combo.getValue();
                        this.fireEvent("change", this.filter);
                    },
                    blur: function(combo) {
                        this.filter.lowerBoundary = combo.getValue();
                        this.fireEvent("change", this.filter);
                    },
                    beforequery: function(evt) {
                        evt.combo.store.baseParams.start = 0;
//                        evt.combo.store.baseParams.query =  evt.combo.getValue();
                        evt.combo.store.baseParams.query = this.autoCompleteCfg.autoCompleteMode == "remote" ? evt.combo.getValue() : "*";
                    },
                    scope: this
                },
                matchFieldWidth: false,                
                anchor: "100%",
                flex:1
            },this.defaultItemsProp['lower']),
            'upper': Ext.applyIf({
                xtype: "tolomeo_uniquevaluescb",
                queryMode: "remote", // required as the combo store shouldn't be loaded before a field name is selected
        		pageSize: this.autoCompleteCfg.pageSize || this.pageSize,
//              pageSize: this.autoCompleteCfg.autoCompleteMode == "remote" ? this.autoCompleteCfg.pageSize || this.pageSize : 0,
                typeAhead: true,
                forceSelection: false,
                remoteSort: true,
                triggerAction: "all",
                allowBlank: this.allowBlank,
                displayField: "value",
                valueField: "value",
                minChars: this.autoCompleteCfg.minChars || 1,
//                resizable: true,
                listeners: {
                    select: function(combo, record) {
                        this.filter.upperBoundary = combo.getValue();
                        this.fireEvent("change", this.filter);
                    },
                    blur: function(combo) {
                        this.filter.upperBoundary = combo.getValue();
                        this.fireEvent("change", this.filter);
                    },
                    beforequery: function(evt) {
                        evt.combo.store.baseParams.start = 0;
//                        evt.combo.store.baseParams.query =  evt.combo.getValue();
                        evt.combo.store.baseParams.query = this.autoCompleteCfg.autoCompleteMode == "remote" ? evt.combo.getValue() : "*";

                    },
                    scope: this
                },
                matchFieldWidth: false,                
                anchor: "100%",
                flex: 1
            },this.defaultItemsProp['upper'])        
        };      
    },
    
	/**
     * Crea il filtro predefinito di comparazione. Questo metodo può essere sovrascritto per cambiare 
     * il filtro predefinito.
     * @return {OpenLayers.Filter} Di default ritorna un filtro di comparazione.
     * 
     */
    createDefaultFilter: function() {
        return new OpenLayers.Filter.Comparison({matchCase: !this.caseInsensitiveMatch});
    },
    
	/**
     * Crea il componente Ext destinato a contenere il valore delle proprietà.
     * @param {TolomeoExt.data.ToloUniqueValuesStore} store Store della combo box di auto completamento.
     * @param {String} layerName codTPN da usare com eparametro della richiesta.
     * @param {String} fieldName Nome della proprietà di cui ritornare i suggerimenti.
     * 
     */
    initUniqueValuesStore: function(store, layerName, fieldName, attributeLogicalName) {
        var params = {
            url: this.autoCompleteCfg.url,
            inputs: {
            	featureTypeName: layerName,
                fieldName: fieldName,
                attributeLogicalName: attributeLogicalName
            }
        };
        
        if(this.autoCompleteCfg.autoCompleteMode == "remote"){
        	Ext.applyIf(params, {
        		start: 0,
                limit: this.autoCompleteCfg.pageSize || this.pageSize
        	});
        }
        
        store.setParams(params);
    },
    
	/**
     * Imposta il tipo di filtro che si desidera.
     * @param {String} type Tipo del filtro da impostare.
     * 
     */
    setFilterType: function(type) {
        this.filter.type = type;
        
        // Ilike (ignore case)
        if(this.filter.type == "ilike"){
            this.filter.type = OpenLayers.Filter.Comparison.LIKE;
            this.filter.matchCase = false;
        }else{
            // default matches case. See OpenLayers.Filter.Comparison#matchCase
            this.filter.matchCase = !this.caseInsensitiveMatch; //true;
        }
    },

	/**
     * Cambia l'oggetto del filtro con uno nuovo che si desidera utilizzare.
     * @param {OpenLayers.Filter} filter Il filtro da impostare
     * 
     */
    setFilter: function(filter) {
        var previousType = this.filter.type;
        this.filter = filter;
        if (previousType !== filter.type) {
            this.setFilterType(filter.type);
        }
        this['property'].setValue(filter.property);
        this['type'].setValue(filter.type);
        if (filter.type === OpenLayers.Filter.Comparison.BETWEEN) {
            this['lowerBoundary'].setValue(filter.lowerBoundary);
            this['upperBoundary'].setValue(filter.upperBoundary);
        } else {
            this['value'].setValue(filter.value);
        }
        this.fireEvent("change", this.filter, this);
    }

});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets.form');

/**
 * Una combo box utilizzata per visualizzare valori unici per un dato campo del layer.
 * Questo componente accetta tutte le configurazioni previste per una Ext.ComboBox
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.ToloUniqueValuesCombo', {
	
	extend: 'Ext.form.ComboBox',
	
	alias: 'widget.tolomeo_uniquevaluescb',
	
	/**
     * Restituisce i parametri usati nella richiesta ajax per il popolamento dello store.
     * @param {Object} q I parametri delo store per la richiesta.
	 * @return {Object} I patametri per la richiesta di popolamento dello store
     */
    getParams : function(q){
    	var superclass = this.superclass;
    	
        var params = superclass.getParams.call(this, q);
        Ext.apply(params, this.store.baseParams);
        return params;
    },
    
	/**
     * Inizializza la lista dei valori della paging toolbar
	 * 
     */
    initList : function() { 
    	// warning: overriding ComboBox private method
        this.superclass.initList.call(this);
        if (this.pageTb && this.pageTb instanceof Ext.PagingToolbar) {
            this.pageTb.afterPageText = "";
            this.pageTb.beforePageText = "";
            this.pageTb.displayMsg = "";
        }
    }
});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

// //////////////////////////////////////////////////////////////////////////
//  include widgets/form/spatialselector/BBOXToloSpatialSelectorMethod.js
//  include widgets/form/spatialselector/BufferToloSpatialSelectorMethod.js
//  include widgets/form/spatialselector/CircleToloSpatialSelectorMethod.js
//  include widgets/form/spatialselector/PolygonToloSpatialSelectorMethod.js
/////////////////////////////////////////////////////////////////////////////

Ext.ns('TolomeoExt.widgets.form.spatialselector');

/**
 * Plugin comune per la selezione spaziale.
 * Widgets conosciute: 
 *    <ul>
 *       <li>BBOXToloSpatialSelectorMethod: `widget.tolomeo_spatial_bbox_selector` ptype</li>
 *       <li>BufferToloSpatialSelectorMethod: `widget.tolomeo_spatial_buffer_selectorr` ptype</li>
 *       <li>CircleToloSpatialSelectorMethod: `widget.tolomeo_spatial_circle_selector` ptype</li>
 *       <li>PolygonToloSpatialSelectorMethod: `widget.tolomeo_spatial_polygon_selector` ptype</li>
 * 	  </ul>
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod', {
	
	extend: 'Ext.Container',

    /**
     * @cfg {String} name.
     * Nome da mostrare nella combo box di selezione spaziale.
     */

    /**
     * @cfg {String} label.
     * Etichetta da mostrare nella combo box di selezione spaziale.
     */

    /**
     * @cfg {Object} output.
     * Configurazione di output per questo plugin.
     */

    /**
     * @cfg {OpenLayers.Geometry} currentGeometry.
     * Rappresenta la geometria selezionata.
     */

    /**
     * @cfg {OpenLayers.Geometry} filterGeometryName.
     * Rappresenta il nome del campo geometrico da usare nel filtro.
     */
   
 	/**
     * @property {Boolean} zoomToCurrentExtent [zoomToCurrentExtent="false"]
     * Flag per stabilire lo zoom all'extent della geometria selezionata.
     */
	zoomToCurrentExtent: false,

    /**
     * @property {Object} defaultStyle.
     * Configurazione del OpenLayer.Style predefinito usato come stile del BBOX su mappa.
     */
	defaultStyle : {
        "fillColor"   : "#FFFFFF",
        "strokeColor" : "#FF0000",
        "fillOpacity" : 0.5,
        "strokeWidth" : 1
	},

    /**
     * @property {Object} selectStyle.
     * Configurazione del OpenLayer.Style di selezione usato come stile del BBOX su mappa.
     */
	selectStyle : {
        "fillColor"   : "#FFFFFF",
        "strokeColor" : "#FF0000",
        "fillOpacity" : 0.5,
        "strokeWidth" : 1
	},

    /**
     * @property {Object} temporaryStyle.
     * Configurazione del OpenLayer.Style temporaneo usato come stile del BBOX su mappa.
     */
	temporaryStyle : {
		"strokeColor": "#ee9900",
		"fillColor": "#ee9900",
		"fillOpacity": 0.4,
		"strokeWidth": 1
	},
	
 	/**
     * @property {Boolean} addGeometryOperation [addGeometryOperation="true"]
     * Flag per stabilire se aggiungere o meno il tool di selezione dell'operazione geometrica per la selezione spaziale scelta.
     */
	addGeometryOperation: true,

 	/**
     * @cfg {Object} geometryOperations.
     * Lista delle operazioni geometriche consentite.
     * 
     * @example
	 *	geometryOperations:[{
	 *		name: "INTERSECTS",
	 *		label: "INTERSECTS",
	 *		value: OpenLayers.Filter.Spatial.INTERSECTS
	 *	},{
	 *		name: "BBOX",
	 *		label: "BBOX",
	 *		value: OpenLayers.Filter.Spatial.BBOX
	 *	},{
 	 *		name: "CONTAINS",
	 *		label: "CONTAINS",
	 *		value: OpenLayers.Filter.Spatial.CONTAINS
	 *	},{
	 *		name: "DWITHIN",
	 *		label: "DWITHIN",
	 *		value: OpenLayers.Filter.Spatial.DWITHIN
	 *	},{
	 *		name: "WITHIN",
	 *		label: "WITHIN",
	 *		value: OpenLayers.Filter.Spatial.WITHIN
	 *	}]
     */
	geometryOperations:null,

    /**
     * @property {OpenLayers.Filter.Spatial} defaultGeometryOperation.
     * Operazione geometrica selezionata di default.
     */
	defaultGeometryOperation: OpenLayers.Filter.Spatial.INTERSECTS,

    /**
     * @property {String} geometryOperationText.
     * Testo da mostrare per l'etichetta della operazione geometrica.
     */
	geometryOperationText: null,

    /**
     * @property {String} geometryOperationEmptyText.
     * Testo da mostrare per la combo box della operazione geometrica se non valorizzata.
     */
	geometryOperationEmptyText: null,

    /**
     * @property {String} distanceTitleText.
     * Testo da mostrare per il fieldset della distanza.
     */
	distanceTitleText: null,
	
	/**
     * @property {String} distanceLabelText.
     * Etichetta da mostrare per il valore della distanza.
     */
	distanceLabelText: null,

    /**
     * @property {String} distanceUnitsTitleText.
     * Testo da mostrare per il campo delle unità relative al campo distanza.
     */
	distanceUnitsTitleText: null,

    /**
     * @property {String} noOperationTitleText.
     * Messaggio di operazione non valida.
     */
	noOperationTitleText: null,

    /**
     * @property {String} noOperationMsgText.
     * Messaggio per operazione non selezionata.
     */
	noOperationMsgText: null,

    /**
     * @property {String} noCompleteMsgText.
     * Messaggio per form non completa.
     */
	noCompleteMsgText: null,
	
	/**
	 * @cfg {TolomeoExt.events.ToloQueryBuilderEvtManager} qbEventManager (required)
	 * Gestore di eventi per il query builder.
	 */
	qbEventManager: null,

	/**
     * Crea un nuovo TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	constructor : function(config) {
		Ext.apply(this, config);		
		this.callParent(arguments);
	},

	/**
     * Inizializza un nuovo TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent: function(config) {   
		Ext.apply(this, config);
		
		
		this.geometryOperations = [{
			name: "INTERSECTS",
			label: ToloI18n.getMsg("ToloSpatialSelectorMethod.geoOp.INTERSECTS"),
			tipTitle: ToloI18n.getMsg("ToloSpatialSelectorMethod.geoOp.INTERSECTS.title"),
			tipText: ToloI18n.getMsg("ToloSpatialSelectorMethod.geoOp.INTERSECTS.text"),
			value: OpenLayers.Filter.Spatial.INTERSECTS
		}/*
		,{
			name: "BBOX",
			label: "Bounding Box",
			value: OpenLayers.Filter.Spatial.BBOX
		}
		*/
		,{
			name: "DWITHIN",
			label: ToloI18n.getMsg("ToloSpatialSelectorMethod.geoOp.DWITHIN"),
			tipTitle: ToloI18n.getMsg("ToloSpatialSelectorMethod.geoOp.DWITHIN.title"),
			tipText: ToloI18n.getMsg("ToloSpatialSelectorMethod.geoOp.DWITHIN.text"),
			value: OpenLayers.Filter.Spatial.DWITHIN
		},{
			name: "WITHIN",
			label: ToloI18n.getMsg("ToloSpatialSelectorMethod.geoOp.WITHIN"),
			tipTitle: ToloI18n.getMsg("ToloSpatialSelectorMethod.geoOp.WITHIN.title"),
			tipText: ToloI18n.getMsg("ToloSpatialSelectorMethod.geoOp.WITHIN.text"),
			value: OpenLayers.Filter.Spatial.WITHIN
		}];
		
		this.geometryOperationText = ToloI18n.getMsg("ToloSpatialSelectorMethod.geometryOperationText");
		this.geometryOperationEmptyText = ToloI18n.getMsg("ToloSpatialSelectorMethod.geometryOperationEmptyText");
		this.distanceTitleText = ToloI18n.getMsg("ToloSpatialSelectorMethod.distanceTitleText");
		this.distanceLabelText = ToloI18n.getMsg("ToloSpatialSelectorMethod.distanceLabelText");
		this.distanceUnitsTitleText = ToloI18n.getMsg("ToloSpatialSelectorMethod.distanceUnitsTitleText");
		this.noOperationTitleText = ToloI18n.getMsg("ToloSpatialSelectorMethod.noOperationTitleText");
		this.noOperationMsgText = ToloI18n.getMsg("ToloSpatialSelectorMethod.noOperationMsgText");
		this.noCompleteMsgText = ToloI18n.getMsg("ToloSpatialSelectorMethod.noCompleteMsgText");
		
		

		if(!this.output){
			this.output = this;
		}

		if(this.addGeometryOperation){
			if (!this.items){
				this.items = [];
			}

			this.items.push({
				xtype: 'fieldset',
				ref: "geometryOperationFieldset",
				title: this.geometryOperationText,
                checkboxToggle: true,
                collapsed : true,
				items: [this.getGeometryOperationCombo()]
			});
			
			this.items.push(this.getDistanceFieldset());
		}

		this.output.addEvents(
				"geometrySelect"
		);	
		
		this.callParent();
		
        this.on("added", function(scope){
        	scope.geometryOperationFieldset = scope.query('fieldset[ref=geometryOperationFieldset]')[0];
        	scope.geometryOperation = scope.query('combo[ref=geometryOperation]')[0];
        	scope.distanceFieldset = scope.query('fieldset[ref=distanceFieldset]')[0];
        	scope.distance = scope.query('numberfield[ref="distance"]')[0];
        	//scope.dunits = scope.query('textfield[ref=dunits]')[0];
        });
    },

	/**
     * Genera un oggetto per la combo di selezione.
     * @return {Object} l'oggetto selezionato dalla combo.
     */
	getSelectionMethodItem: function(){
        return {
        	label: this.label, 
        	name: this.name
        };
	},

	/**
     * Genera un filtro per il metodo si selezione.
     * @return {OpenLayers.Filter} Il filtro impostato dall'utente.
     */
	getQueryFilter: function(){
		var operation = null;
		
		if(this.addGeometryOperation && !this.geometryOperationFieldset.collapsed){
			if(this.geometryOperation.isValid() ){
				operation = this.geometryOperation.getValue();
			}else{
                Ext.Msg.show({
                    title: this.noOperationTitleText,
                    msg: this.noOperationMsgText,
                    buttons: Ext.Msg.OK,
                    icon: Ext.MessageBox.ERROR
                });
				return null;
			}
		}else{
			operation = OpenLayers.Filter.Spatial.INTERSECTS;
		}
		
		if(this.currentGeometry){
			switch (operation){
				case OpenLayers.Filter.Spatial.CONTAINS:
				case OpenLayers.Filter.Spatial.INTERSECTS:
					this.currentFilter = new OpenLayers.Filter.Spatial({
						type: operation,
						property:  this.filterGeometryName,
						value: this.currentGeometry,
						bounds: this.currentGeometry.getBounds()
					});
					break;
				case OpenLayers.Filter.Spatial.WITHIN:
					this.currentFilter = new OpenLayers.Filter.Spatial({
						type: operation,
						property:  this.filterGeometryName,
						value: this.currentGeometry
					});
					break;
				case OpenLayers.Filter.Spatial.DWITHIN:
					if(this.distance.isValid()
						/*&& this.dunits.isValid()*/){
						this.currentFilter = new OpenLayers.Filter.Spatial({
							type: operation,
							property:  this.filterGeometryName,
					        distanceUnits: "m", //this.dunits.getValue(),
					        distance: this.distance.getValue(),
							value: this.currentGeometry
						});
					}else{
		                Ext.Msg.show({
		                    title: this.noOperationTitleText,
		                    msg: this.noCompleteMsgText,
		                    buttons: Ext.Msg.OK,
		                    icon: Ext.MessageBox.ERROR
		                });
		                return null;
					}
					break;
				case OpenLayers.Filter.Spatial.BBOX:
				default: 
					this.currentFilter = new OpenLayers.Filter.Spatial({
						type: OpenLayers.Filter.Spatial.BBOX,
						property:  this.filterGeometryName,
						value: this.currentGeometry.getBounds()
					});
			}
			
		}else{
	        this.currentFilter = null;
		}
		
		return this.currentFilter;
	},

	/**
     * Attiva il plugin.
     * 
     */
	activate: function(){
		this.reset();
		this.doLayout();
		this.show();
	},

	/**
     * Disattiva il plugin.
     * 
     */
	deactivate: function(){
		this.reset();
		this.hide();
	},

	/**
     * Reimposta il componente (filtro e geometria).
     * 
     */
    reset: function(){
    	this.currentGeometry = null;
    	this.currentFilter = null;
    },

	/** api: method[setCurrentGeometry]
     *  :arg geometry: ``Object`` The geometry to be setted as current geometry.
     *  Set current geometry
	 */
    setCurrentGeometry: function(geometry){
		this.currentGeometry = geometry;
		
    	if (geometry) {
			if (this.zoomToCurrentExtent && geometry && geometry.getBounds) {
				var dataExtent = geometry.getBounds();
				
				// create an event to manage the zoom to extent
				this.qbEventManager.fireEvents("zoomtomapextent", {dataExtent: dataExtent});
			}

			this.output.fireEvent("geometrySelect", geometry);
		} 
    },

	/**
     * Genera la combo box Ext per la selezione dell'operazione geometrica.
     * @return {Ext.form.ComboBox} La combo box di selezione dell'operazione geometrica.
     */
	getGeometryOperationCombo: function() {
		var geometryOperationMethodCombo = Ext.create('Ext.form.ComboBox', {
			ref : 'geometryOperation',
			width: 220,
			labelStyle: 'width: 70px;',
			typeAhead : true,
			forceSelection: true, 
			queryMode: 'local',
			triggerAction: 'all',
			emptyText : this.geometryOperationEmptyText,
			selectOnFocus: true,
			editable:false,
			fieldLabel : this.geometryOperationText,
			name : 'geometryOperation',
			displayField : 'label',
			valueField : 'value',
			readOnly : false,
			lazyRender : false,
			value: this.defaultGeometryOperation,
			allowBlank : false,
			listConfig: {
		 
		        // Custom rendering template for each item
		        getInnerTpl: function() {                	        
		        	// return '{[(values.tipTitle || values.tipText)  ? values.text + " <div data-qtitle=\\"" + values.tipTitle + "\\"  data-qtip=\\"" + values.tipText + "\\" style=\\"color:gray; border-bottom: dashed 1px gray; border-top: dashed 1px gray; \\">" + values.tipText + "</div>" : values.text]}';
		        	return '{[(values.tipTitle || values.tipText)  ? " <div data-qtitle=\\"" + values.tipTitle + "\\"  data-qtip=\\"" + values.tipText + "\\" >" + values.label + "</div>" : values.label]}';
		        }
			},
			
			store : Ext.create('Ext.data.JsonStore', {
				autoLoad : true,
				fields : [{
					name : 'name',
					dataIndex : 'name'
				}, {
					name : 'label',
					dataIndex : 'label'
				}, {
					name : 'value',
					dataIndex : 'value'
				}, {
					name : 'tipTitle',
					dataIndex : 'tipTitle'
				}, {
					name : 'tipText',
					dataIndex : 'tipText'
				}],
				data : this.geometryOperations
			}),
			listeners : {
				// Show/Hide distance units for DWITHIN
				select : function(c, record, index) {
					if(c.getValue() == OpenLayers.Filter.Spatial.DWITHIN){
						this.distanceFieldset.show();
					}else if(this.distanceFieldset.isVisible()){
						this.distanceFieldset.hide();
					}
				},
				scope : this
			}
		});
		 
		return geometryOperationMethodCombo;
	},

	/**
     * Genera la combo box Ext per la selezione della distanza (specifica selezione geometrica vedi "DWITHIN").
     * @return {Ext.form.FieldSet} il FieldSet per l'impostazione della distanza.
     */
	getDistanceFieldset: function(){
		return {
			xtype: 'fieldset',
			title: this.distanceTitleText,
			ref: "distanceFieldset",
			hidden: true,
			items: [/*{
				xtype: "textfield",
				fieldLabel: this.distanceUnitsTitleText,
				name: "dunits",
				ref: "dunits",
				labelStyle: 'width: 90px;',
				width: 200,
				allowBlank: false
			},*/{
				xtype: "numberfield",
				fieldLabel: this.distanceLabelText,
				name: "distance",
				ref: "distance",
				labelStyle: 'width: 90px;',
				width: 200,
				allowBlank: false
			}]/*,
			listeners:{
				scope: this,
			    afterlayout: function(fieldset, options){
					var dunits = this.query('textfield[ref=dunits]')[0];
					if(this.qbEventManager){
						this.qbEventManager.fireEvent("setmapunitsvaluefield", this.dunits);
					}
				}
			}*/
		}
	},
	
	setDecimalPrecision: function(decimalPrecision){
		return;
	}
	
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets.form.spatialselector');

/**
 * Plugin per la selezione poligonale
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.spatialselector.ToloPolygonSpatialSelectorMethod', {
	
	extend: 'TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod',
	
	alias: 'widget.tolomeo_spatial_polygon_selector',
	
	requires: [
       'TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod'
	],

    /**
     * @cfg {String} name.
     * Nome da mostrare nella combo box di selezione spaziale.
     */
	name  : null,
	
	buttonCls : 'draw-polygon-button',

    /**
     * @cfg {String} label.
     * Etichetta da mostrare nella combo box di selezione spaziale.
     */
	label : null,
	
	initComponent: function(config) {   
				
    	this.name = ToloI18n.getMsg("ToloPolygonSpatialSelectorMethod.name");
    	this.label = ToloI18n.getMsg("ToloPolygonSpatialSelectorMethod.label");
    	
		var displayProjection = this.displayProjection;
		var me = this;
		
    	this.output = Ext.create('Ext.Button', {    		
            text: ToloI18n.getMsg("ToloPolygonSpatialSelectorMethod.output"),
            tooltip: ToloI18n.getMsg("ToloPolygonSpatialSelectorMethod.tooltip"),
            enableToggle: true,            
            iconCls: this.buttonCls,            
            height: 40,
            width: 100,
            scale: 'large',
            listeners: {
                scope: this, 
                toggle: function(button, pressed) {
                    if(pressed){      
                        me.unfreeze();
                    }else{
                        me.freeze();
                    }
                }
            }
        }); 

    	this.items = [{
    		type: 'fieldcontainer',       		
    		margin: 10,
    		border: false,
    		layout: {
    		        type: 'hbox',
    		        pack: 'center'
    		        
    		},
	    	items: [this.output]
    	}];
    	
    	this.active = false;

    	this.callParent(arguments);
    },


	/**
     * Attiva il controllo.
     * 
     */
	activate: function(){
		TolomeoExt.widgets.form.spatialselector.ToloPolygonSpatialSelectorMethod.superclass.activate.call(this);
		this.output.enable();		
	},

	/**
     * Restituisce il controllo di disegno di questo componente.
     * 
     */
	getDrawControl: function(){
		return new OpenLayers.Control.DrawFeature(
            this.drawings,
            OpenLayers.Handler.Polygon
        );
	},

	/**
     * Disattiva il controllo.
     * 
     */
	deactivate: function(){
		TolomeoExt.widgets.form.spatialselector.ToloPolygonSpatialSelectorMethod.superclass.deactivate.call(this);
		if(this.draw){
			this.draw.deactivate();	
		}
		
		if(this.qbEventManager){
			this.qbEventManager.fireEvent("polygonSpatialSelectorDeactive", this);
		}
		
		this.output.disable();
		
		this.active = false;
		
		/*
		if (this.drawings) {
			if(this.qbEventManager){
				this.qbEventManager.fireEvent("removelayer", this.drawings);
			}
			
			this.drawings = null;
		}
		*/
	},

	/**
     * Reimposta il controllo di sidegno poligonale.
     * 
     */
    reset: function(){    	
    	TolomeoExt.widgets.form.spatialselector.ToloPolygonSpatialSelectorMethod.superclass.reset.call(this);
    	
		if(this.drawings){
			this.drawings.removeAllFeatures();
		}
		
		if(this.persistance){
			this.persistance.removeAllFeatures();
		}
    },
	
	freeze: function(){
		if(this.draw){
			this.draw.deactivate();	
		}		
		
		if(this.output.pressed){
			this.output.toggle();
		}
	},
	
	unfreeze: function(){
		
		if(!this.active) {
			
			if(this.qbEventManager){
				this.qbEventManager.fireEvent("polygonSpatialSelectorActive", this);
			}			
			this.active = true;
			
		} else {
			
			if(this.persistance){
				this.persistance.removeAllFeatures();
			}
			
			if(this.draw){
				this.draw.activate();	
			}
			
		}
						
	}
	
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.namespace('TolomeoExt.widgets.form.spatialselector');

/**
 * Plugin per la selezione di una area di interesse a Bounding Box (BBOX).
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.spatialselector.ToloBBOXSpatialSelectorMethod', {
		
	extend: 'TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod',

	alias : 'widget.tolomeo_spatial_bbox_selector',
	
	requires: [
       'TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod'
	],

    /**
     * @cfg {String} latitudeEmptyText [displayProjection="null"]
     * La proiezione per il display delle coordinate (se null si usa la proiezione dell mappa).
     */
    displayProjection: null,
    
    /**
     * @cfg {String} name.
     * Nome da mostrare nella combo box di selezione spaziale.
     */
	name  : null,

    /**
     * @cfg {String} label.
     * Etichetta da mostrare nella combo box di selezione spaziale.
     */
	label : null,

 	/**
     * @cfg {Object} spatialFilterOptions.
     * Opzioni di configurazione per i campi di coordinata (valori massimi e minini consentiti per i campi).
     * 
     * @example
	 *    spatialFilterOptions: {
	 *	        lonMax: 180,   
	 *	        lonMin: -180,
	 *	        latMax: 90,   
	 *	        latMin: -90  
	 *	  }
     */
	spatialFilterOptions : {
		lonMax : 20037508.34, //90,
		lonMin : -20037508.34, //-90,
		latMax : 20037508.34, //180,
		latMin : -20037508.34 //-180
	},

 	/**
     * @property {String} northLabel.
     * Testo dell'etichetta per la coordinata Nord.
     */
    northLabel: null,
    
 	/**
     * @property {String} westLabel.
     * Testo dell'etichetta per la coordinata Ovest.
     */
    westLabel: null,
    
 	/**
     * @property {String} eastLabel.
     * Testo dell'etichetta per la coordinata Est.
     */
    eastLabel: null,
    
 	/**
     * @property {String} southLabel.
     * Testo dell'etichetta per la coordinata Sud.
     */
    southLabel: null,

 	/**
     * @property {String} setAoiTitle.
     * Titolo del field set di contenimento.
     */
	setAoiTitle : null,

 	/**
     * @property {String} setAoiText.
     * Testo di etichetta per il pulsante di attivazione del controllo di disegno del BOX.
     */
    setAoiText: null,

 	/**
     * @property {String} setAoiTooltip.
     * Testo per il tooltip del pulsante di attivazione del controllo di disegno del BOX.
     */
    setAoiTooltip: null,

	/**
     * Inizializza un nuovo TolomeoExt.widgets.form.spatialselector.ToloBBOXSpatialSelectorMethod.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent: function(config) {
    	
    	this.name  = ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.name");
    	this.label = ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.label");
    	
        this.northLabel = ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.northLabel");
        this.westLabel = ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.westLabel");
        this.eastLabel = ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.eastLabel");
        this.southLabel = ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.southLabel");
        this.setAoiTitle = ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.setAoiTitle");
        this.setAoiText = ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.setAoiText");
        this.setAoiTooltip = ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.setAoiTooltip");
    	
    	var displayProjection = this.displayProjection;
		
    	// ///////////////////////////////////////////
		// Spatial AOI Selector FieldSet
		// ///////////////////////////////////////////
		var confbbox = {
            map: null, 
            padding: '5 0 5 5',
            outputSRS : this.displayProjection  || null, 
            spatialFilterOptions: this.spatialFilterOptions,
            ref: "spatialFieldset",
            id: this.id + "_bbox",
            defaultStyle: this.defaultStyle,
            selectStyle: this.selectStyle,
            temporaryStyle: this.temporaryStyle,
            anchor: "100%",
            title: this.setAoiTitle,
		    northLabel:this.northLabel,
		    westLabel:this.westLabel,
		    eastLabel:this.eastLabel,
		    southLabel:this.southLabel,
		    setAoiText: this.setAoiText,
		    setAoiTooltip: this.setAoiTooltip,
		    waitEPSGMsg: ToloI18n.getMsg("ToloBBOXSpatialSelectorMethod.waitEPSGMsg")
        };

    	this.output = Ext.create('TolomeoExt.widgets.form.ToloBBOXFieldset', confbbox);

    	this.items = [this.output];

    	this.callParent();
    },

	/**
     * Attiva il controllo.
     * 
     */
	activate: function(){
		TolomeoExt.widgets.form.spatialselector.ToloBBOXSpatialSelectorMethod.superclass.activate.call(this);
		if(this.output){
			this.output.enable();
		}
	},

	/**
     * Disattiva il controllo.
     * 
     */
	deactivate: function(){
		TolomeoExt.widgets.form.spatialselector.ToloBBOXSpatialSelectorMethod.superclass.deactivate.call(this);
		if(this.output){
			if(this.qbEventManager){
				this.qbEventManager.fireEvent("removelayer", this.output.layerName);
			}
			this.output.disable();
		}
	},

	/**
     * Reimposta il controllo di disegno del BBOX.
     * 
     */
    reset: function(){
    	TolomeoExt.widgets.form.spatialselector.ToloBBOXSpatialSelectorMethod.superclass.reset.call(this);
    	
		if(this.qbEventManager){
			this.qbEventManager.fireEvent("removelayer", this.output.layerName);
		}
    	this.output.reset();
    },

	setDecimalPrecision: function(decimalPrecision){
		this.output.setDecimalPrecision(decimalPrecision);
	}

});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets.form.spatialselector');

/**
 * Plugin per la selezione di una area di interesse a buffer.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.spatialselector.ToloBufferSpatialSelectorMethod',  {
	
	extend: 'TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod',

	alias: 'widget.tolomeo_spatial_buffer_selector',
	
	requires: [
       'TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod'
	],

    /**
     * @cfg {String} name.
     * Nome da mostrare nella combo box di selezione spaziale.
     */
	name: null,

    /**
     * @cfg {String} label.
     * Etichetta da mostrare nella combo box di selezione spaziale.
     */
	label: null,

    /**
     * @cfg {String} latitudeEmptyText.
     * Testo da mostrare se ili campo Y (latitude) non è valorizzato.
     */
    latitudeEmptyText: null,

    /**
     * @cfg {String} longitudeEmptyText.
     * Testo da mostrare se ili campo X (longitude) non è valorizzato.
     */
    longitudeEmptyText: null,

	/**
	 * @cfg {Object} bufferOptions 
	 * Opzioni di configurazione per la selezione del buffer.
	 *
	 * @example
	 *	bufferOptions : {
	 *		"minValue": 1,
	 *		"maxValue": 1000, 
	 *			"decimalPrecision": 2,
	 *		"distanceUnits": "m"
	 *	}
	 */
	bufferOptions : {
		"minValue": 1,
		"maxValue": 5000,
		"decimalPrecision": 2,
		"distanceUnits": "m",
		"outputSRS": "EPSG:3003"
	},
	
    /**
     * @cfg {Boolean} geodesic.
     * 
     */
	geodesic: true,

	/**
     * Inizializza un nuovo TolomeoExt.widgets.form.spatialselector.ToloBufferSpatialSelectorMethod.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    initComponent: function(config) {
    	
    	this.name = ToloI18n.getMsg("ToloBufferSpatialSelectorMethod.name");
    	this.label = ToloI18n.getMsg("ToloBufferSpatialSelectorMethod.label");
    	this.latitudeEmptyText = ToloI18n.getMsg("ToloBufferSpatialSelectorMethod.latitudeEmptyText");
    	this.longitudeEmptyText = ToloI18n.getMsg("ToloBufferSpatialSelectorMethod.longitudeEmptyText");

		// ///////////////////////////////////////////
		// Spatial Buffer Selector FieldSet
		// ///////////////////////////////////////////
		this.bufferFieldset = new TolomeoExt.widgets.form.ToloBufferFieldset({
			id: this.id + "bufferFieldset",
			map: null,
			minValue: this.bufferOptions.minValue,
            maxValue: this.bufferOptions.maxValue,
		    decimalPrecision: this.bufferOptions.decimalPrecision,
		    outputSRS : this.bufferOptions.outputSRS, 
			selectStyle: this.selectStyle,
			geodesic: this.geodesic != undefined ?  this.geodesic : false,
			latitudeEmptyText: this.latitudeEmptyText,
			longitudeEmptyText: this.longitudeEmptyText,
			qbEventManager: this.qbEventManager			
		});
		
		this.bufferFieldset.on("bufferadded", function(evt, feature){
			this.setCurrentGeometry(feature.geometry);
		}, this);

	    this.bufferFieldset.on("bufferremoved", function(evt, feature){
			this.setCurrentGeometry(null);
		}, this);
	    
	    this.bufferFieldset.on("afterlayout", function(evt){
	    	this.qbEventManager.fireEvent("addcoordinatepickercontrol", this.bufferFieldset.coordinatePicker);
		}, this);

    	this.output = this;

    	this.items = [this.bufferFieldset];

    	this.callParent();
    },

	/**
     * Attiva il controllo.
     * 
     */
	activate: function(){
		TolomeoExt.widgets.form.spatialselector.ToloBufferSpatialSelectorMethod.superclass.activate.call(this);
		if(this.output){
			this.output.enable();
			if(Ext.isIE){
				this.output.doLayout();
			}
		}
	},

	/**
     * Disattiva il controllo.
     * 
     */
	deactivate: function(){
		TolomeoExt.widgets.form.spatialselector.ToloBufferSpatialSelectorMethod.superclass.deactivate.call(this);
		if(this.output){
			this.bufferFieldset.resetPointSelection();
			this.bufferFieldset.coordinatePicker.toggleButton(false);
			this.output.hide();
			this.output.disable();
		}
	},

	/**
     * Reimposta il controllo di disegno del buffer.
     * 
     */
    reset: function(){
    	TolomeoExt.widgets.form.spatialselector.ToloBufferSpatialSelectorMethod.superclass.reset.call(this);
		if(this.output){
			this.bufferFieldset.resetPointSelection();
			this.bufferFieldset.coordinatePicker.toggleButton(false);
		}
    },

	setDecimalPrecision: function(decimalPrecision){
		this.bufferFieldset.setDecimalPrecision(decimalPrecision);
	}

});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.widgets.form.spatialselector');

/**
 * Plugin per la selezione di una area di interesse circolare.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.widgets.form.spatialselector.ToloCircleSpatialSelectorMethod', {
	
	extend: 'TolomeoExt.widgets.form.spatialselector.ToloPolygonSpatialSelectorMethod',

	alias: 'widget.tolomeo_spatial_circle_selector',
	
	requires: [
       'TolomeoExt.widgets.form.spatialselector.ToloSpatialSelectorMethod'
	],
  
    /**
     * @cfg {String} name.
     * Nome da mostrare nella combo box di selezione spaziale.
     */
	name  : null,
	
	buttonCls : 'draw-circle-button',

    /**
     * @cfg {String} label.
     * Etichetta da mostrare nella combo box di selezione spaziale.
     */
	label : null,

	initComponent: function(config) {
		this.callParent();
		this.name = ToloI18n.getMsg("ToloCircleSpatialSelectorMethod.name");
		this.label = ToloI18n.getMsg("ToloCircleSpatialSelectorMethod.label");
	},
	
	/**
     * Restituisce il controllo di disegno di questo componente.
     * 
     */
	getDrawControl: function(){
        var polyOptions = {sides: 100};
        
        return new OpenLayers.Control.DrawFeature(
            this.drawings,
            OpenLayers.Handler.RegularPolygon,
            {
                handlerOptions: polyOptions
            }
        );
	},

	/**
     * Reimposta il controllo di disegno poligonale.
     * 
     */
    reset: function(){
    	TolomeoExt.widgets.form.spatialselector.ToloCircleSpatialSelectorMethod.superclass.reset.call(this);
		if(this.circleCentroidLayer){
			this.circleCentroidLayer.removeAllFeatures();
		}
    }

});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Crea un poligono regolar dato il raggio. Utile per la creazione di cerchi gedetici.
 * @param {Object} origin Corrisponde al centro del poligono.
 * @param {Number} radius Rappresenta il raggio del poligono regolare.
 * @param {Number} sides Rappresenta il numero di lati per determinare il livello di aprossimazione 
                   del poligono regolare 20 approssima un cerchio. 
 * @param {Number} rotation angolo di rotazione in gradi.
 * @param {OpenLayers.Projection} projection Sistema di proiezione delle coordinate.
 */
OpenLayers.Geometry.Polygon.createGeodesicPolygon = function(origin, radius, sides, rotation, projection){
	if (projection.getCode() !== "EPSG:4326") {
		origin.transform(projection, new OpenLayers.Projection("EPSG:4326"));
	}
	var latlon = new OpenLayers.LonLat(origin.x, origin.y);
	
	var angle;
	var new_lonlat, geom_point;
	var points = [];
	
	for (var i = 0; i < sides; i++) {
		angle = (i * 360 / sides) + rotation;
		new_lonlat = OpenLayers.Util.destinationVincenty(latlon, angle, radius);
		new_lonlat.transform(new OpenLayers.Projection("EPSG:4326"), projection);
		geom_point = new OpenLayers.Geometry.Point(new_lonlat.lon, new_lonlat.lat);
		points.push(geom_point);
	}
	var ring = new OpenLayers.Geometry.LinearRing(points);
	return new OpenLayers.Geometry.Polygon([ring]);
};

Ext.ns('TolomeoExt.events');

/**
 * Plugin per la gesitione degli eventi che coinvolgono il query builder.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.events.ToloQueryBuilderEvtManager', {
	
	extend: 'Ext.util.Observable',
	
	id: "qb_event_manager",
	
	/**
     * Crea un nuovo TolomeoExt.events.ToloQueryBuilderEvtManager.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	constructor: function(config) {
		this.callParent(arguments);
		
		Ext.apply(this, config);
		
		this.addEvents(
		        /**
				 * @event
				 * Lanciato successivamente al disegno del box.
				 */
				"afterboxlayout",
				/**
				 * @event
				 * Lanciato dai componenti che lo necessitano per lo zoom all'extent specificato.
				 */
				"zoomtomapextent",
				/**
				 * @event
				 * Lanciato dai componenti che lo necessitano per la rimozione di un layer della mappa.
				 */
				"removelayer",
				/**
				 * @event
				 * Lanciato per l'aggiunta del controllo di selezione delle coordiante su mappa.
				 */
				"addcoordinatepickercontrol",
				/**
				 * @event
				 * Lanciato per l'aggiornemento del punto sulla mappa.
				 */
				"updatemappoint",
				/**
				 * @event
				 * Lanciato successivamente al disegno del box.
				 */
				"drawbuffer",
				/**
				 * @event
				 * Lanciato per l'attivazione del controllo di selezione spaziale.
				 */
				"polygonSpatialSelectorActive",
				/**
				 * @event
				 * Lanciato per l'impostazione delle unit di misura.
				 */
				"setmapvaluefield",
				/**
				 * @event
				 * Lanciato a seguito di una operazione di spostamento della mappa.
				 */
				"mapmoved",
				/**
				 * @event
				 * Lanciato per l'agginta di un layer vetoriale.
				 */
				"addvectorlayer",
				/**
				 * @event
				 * Lanciato per impostare le unità della mappa nei tool che lo richiedono.
				 */
				"setmapunitsvaluefield"
		);	
		
		this.on("afterboxlayout", this.onAfterBoxLayout);
		this.on("zoomtomapextent", this.zoomToMapExtent);
		this.on("removelayer", this.removeLayer);
		this.on("addlayer", this.addLayer);
		this.on("addcoordinatepickercontrol", this.addCoordinatePickerControl);
		this.on("updatemappoint", this.updateMapPoint);
		this.on("drawbuffer", this.drawBuffer);
		this.on("polygonspatialselectoractive", this.polygonSpatialSelectorActive);
		this.on("polygonspatialselectorDeactive", this.polygonSpatialSelectorDeactive);
		this.on("setmapunitsvaluefield", this.setMapUnitsValueField);
	},
	
	/**
     * Imposta la mappa per le operaioni interne di gestione.
     * @param {OpenLayers.Map} map La mappa di TolomeoExt.
     */
	setMap: function(map){
		this.map = map;		
		this.map.events.register("moveend", this, function(){
			this.fireEvent("mapmoved", this.map.getExtent());
		});
	},
	
	/**
     * Aggiunge un dato layer alla mappa.
     * @param {OpenLayers.Layer} layer Layer OpenLayers WMS o Vector.
     */
	addLayer: function(layer){
		if(layer && this.map){
			//
			// check if already exists if yes remove it
			//
			var lay = this.map.getLayersByName(layer.name)[0];    	        
	        if(lay){
	            this.map.removeLayer(lay);
	        }	        
	        
			this.map.addLayer(layer);
		}
	},
	
	/**
     * Rimuove un dato layer dalla mappa.
     * @param {OpenLayers.Layer} layer Layer OpenLayers WMS o Vector.
     */
	removeLayer: function(layer){
		
		var lay = this.getLayer(layer);				
		
		if(!lay || (lay instanceof OpenLayers.Layer.Vector && lay.features.length < 1)){
			return;
		}
				        
	    this.map.removeLayer(lay);	       
		
		/*
		if(layer && this.map){
			var lay;
			if(typeof layer === 'string'){
				lay = this.map.getLayersByName(layer)[0];
			}else{
				lay = layer;
			}        
	        
			var remove = lay ? true : false;
			if(lay && lay instanceof OpenLayers.Layer.Vector && lay.features.length < 1){
				remove = false;
			}
			
	        if(remove){
	            this.map.removeLayer(lay);
	        }
		}
		*/
	}, 
	
	/**
     * Rimuove un dato layer dalla mappa.
     * @param {OpenLayers.Layer} layer Layer OpenLayers WMS o Vector.
     */
	getLayer: function(layer){
		if(layer && this.map){
			var lay;
			if(typeof layer === 'string'){
				lay = this.map.getLayersByName(layer)[0];
			}else{
				lay = layer;
			}
			return lay;
		}
		return null;
	},
	
	/**
     * Rimuove le features da un layer della mappa se vettoriale.
     * @param {OpenLayers.Layer} layer Layer OpenLayers WMS o Vector.
     */
	cleanLayer: function(layer){
		var lay = this.getLayer(layer);
		if(lay && lay instanceof OpenLayers.Layer.Vector && lay.features.length > 0){
			lay.destroyFeatures();
		}			
	},
	
	/**
     * Esegue una operazione di zoom usando l'extent fornito come argomento.
     * @param {Object} evt Oggetto contenente le proprietà di zoom.
	 * @param {OpenLayers.Bounds} [evt.dataExtent] Extent a cui eseguire lo zoom.
     */
	zoomToMapExtent: function(evt){
		if(evt.dataExtent){
			this.map.zoomToExtent(evt.dataExtent, false);
		}
	},
	
	/**
     * Disegna il box relativo alla selezione utente.
     * @param {Object} evt Oggetto contenente le proprietà relative alle operazioni da eseguire.
	 * @param {Object} [evt.scope] Scope su cui applicare le operazioni contenute.
     */
	onAfterBoxLayout: function(evt){
		var mgr = this;
		var map = this.map;
		var scope = evt.scope;
		var updatingNumbers = false;
		
        var link = Ext.get(scope.id+"_bboxAOI-set-EPSG");
        if(link){
          link.addListener("click", scope.openEPSGWin, scope);  
        }
        
		var baseProj = map.getProjection();
		var projection = baseProj ? baseProj : map.projection; 		
		
        scope.mapProjection = new OpenLayers.Projection(projection);
        
        var updateBoxDimension = function(field, newValue) {
        	if(!updatingNumbers){
        		
        		var left = this.westField.getValue();
        		var bottom = this.southField.getValue();
        		var right = this.eastField.getValue();
        		var top = this.northField.getValue();
        			
        		this.selectBBOX.updateBoxDimension(left,bottom,right,top);        		
        		var bounds = new OpenLayers.Bounds(left,bottom,right,top);
        		var geom = bounds.toGeometry();
                scope.ownerCt.setCurrentGeometry(geom);
                 
        	}
        }
        
        var updateCoordsFieldValue = function(bounds){
        	scope.northField.setValue(bounds.top);
            scope.southField.setValue(bounds.bottom);
            scope.westField.setValue(bounds.left);
            scope.eastField.setValue(bounds.right); 
        }
        
        var setCoordsReadOnly = function(readonly){
        	scope.northField.setReadOnly(readonly);
            scope.southField.setReadOnly(readonly);
            scope.westField.setReadOnly(readonly);
            scope.eastField.setReadOnly(readonly);
        }
        
        var activateMapExtentFiltering = function(){
        	scope.selectBBOX.deactivate();
			scope.bboxButton.disable();
			if(scope.bboxButton.pressed) {
				scope.bboxButton.toggle();
			}
			setCoordsReadOnly(true);
			updateCoordsFieldValue(mgr.map.getExtent());
			mgr.on("mapmoved", updateCoordsFieldValue);	
			updateBoxDimension.call(scope);
        }
        
        var deactivateMapExtentFiltering = function(){
        	mgr.un("mapmoved", updateCoordsFieldValue);
        	setCoordsReadOnly(false);
        	scope.bboxButton.enable();     
        	scope.selectBBOX.deactivate();
        	mgr.cleanLayer(scope.layerName);
        }
        
        scope.selectBBOX = new OpenLayers.Control.SetBox({      
            map: map,       
            layerName: scope.layerName,
            displayInLayerSwitcher: scope.displayBBOXInLayerSwitcher,
            boxDivClassName: "olHandlerBoxZoomBox_" + scope.id,
            aoiStyle: new OpenLayers.StyleMap({
				"default" : scope.defaultStyle,
				"select": scope.selectStyle,
				"temporary": scope.temporaryStyle
			}),
            onChangeAOI: function(){
            	updatingNumbers = true;
            	var bounds = new OpenLayers.Bounds.fromString(this.currentAOI);  
              
            	updateCoordsFieldValue(bounds); 
                
                this.deactivate();
                scope.bboxButton.toggle();
                
                var geom = bounds.toGeometry();
                scope.ownerCt.setCurrentGeometry(geom);
                updatingNumbers = false;                
            } 
        }); 
        
        var msDelay = 200;
        
        scope.northField.on('change',updateBoxDimension,scope,{delay: msDelay});
    	scope.southField.on('change',updateBoxDimension,scope,{delay: msDelay});
    	scope.westField.on('change',updateBoxDimension,scope,{delay: msDelay});
    	scope.eastField.on('change',updateBoxDimension,scope,{delay: msDelay});

        map.addControl(scope.selectBBOX);
        
        scope.on('mapExtentActive', activateMapExtentFiltering);
        scope.on('mapExtentDeactive',deactivateMapExtentFiltering);
        
        map.enebaleMapEvent = true;
	},
	
    /**
     * Aggiunge il conrollo di selezione delle coordinate sulla mappa.
	 * @param {Object} scope Scope su cui applicare le operazioni contenute.
     */
	addCoordinatePickerControl: function(scope){
		var map = this.map;
		
		if(scope && map){
			var compositeField = scope;
			scope.projectionObject = map.getProjectionObject();
			
	        //create the click control
	        var ClickControl = OpenLayers.Class(OpenLayers.Control, {                
	            defaultHandlerOptions: {
	                'single': true,
	                'double': false,
	                'pixelTolerance': 0,
	                'stopSingle': false,
	                'stopDouble': false
	            },

	            initialize: function(options) {
	                this.handlerOptions = OpenLayers.Util.extend(
	                    {}, this.defaultHandlerOptions
	                );
	                OpenLayers.Control.prototype.initialize.apply(
	                    this, arguments
	                ); 
	                this.handler = new OpenLayers.Handler.Click(
	                    compositeField, {
	                        'click': this.trigger
	                    }, this.handlerOptions
	                );
	            }, 
	            trigger: function(e){
	                //get lon lat
	                var lonlat = map.getLonLatFromPixel(e.xy);	                	                
	                var geoJsonPoint = lonlat.clone();	                	                	                
	                
	                /*  DECOMMENTARE SE SI VUOLE visualizzare le coordinate nel sistema di riferimento impostato per il buffer selector
	                 
		                var tPoint = new Point(lonlat.lon, lonlat.lat);
		                tPoint.transform(map.getProjectionObject().getCode(),scope.outputSRS);
		                
		                geoJsonPoint.lon = tPoint.x;
		                geoJsonPoint.lat = tPoint.y;
	                */
	                
	                scope.latitudeField.setValue(geoJsonPoint.lat);
	                scope.longitudeField.setValue(geoJsonPoint.lon);
	                
	                //update point on the map
	                scope.updateMapPoint(lonlat);
	                scope.toggleButton(false);
	            },
	            map: map
	        });       
	        
	        scope.selectLonLat = new ClickControl();
	        this.map.addControl(scope.selectLonLat);	
		}
	},
	
	/**
     * Aggiorna il punto sulla mappa a seguito di una selezione.
	 * @param {OpenLayers.LonLat} lonlat Coordinate relative al punto selezionato.
	 * @param {Object} scope Scope su cui applicare le operazioni contenute.
     */
	updateMapPoint: function(lonlat, scope){
		if(lonlat){
	        var style = new OpenLayers.Style(scope.selectStyle);
	        scope.layer = new OpenLayers.Layer.Vector(scope.selectLayerName, {
	            styleMap: style                
	        });
	        var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
	        var pointFeature = new OpenLayers.Feature.Vector(point);
	        scope.layer.addFeatures([pointFeature]);
	        scope.layer.displayInLayerSwitcher = scope.displayInLayerSwitcher;
	        this.map.addLayer(scope.layer);  
		}
	},
	
	/**
     * Disegna il buffer sulla mappa a seguito delle impostazioni utente.
	 * @param {OpenLayers.Geometry.Point} point Centro del poligono.
	 * @param {Boolean} geodesic Scope su cui applicare le operazioni contenute.
	 * @param {number} radius Raggio del poligono.
	 * @param {OpenLayers.Style} style Style con cui disegnare il buffer.
	 * @param {String} layername Il nome del layer.
	 * @param {Boolean} displayInLayerSwitcher Indica se mostrare il layers all'interno del layers switcher OpenLayers.
	 * @param {Function} callback Funzione di callback da invocare successivamente alle operazioni contenute.
	 * @param {Object} scope Scope su cui applicare le operazioni contenute.
     */
	drawBuffer: function(point, geodesic, radius, style, 
			layername, displayInLayerSwitcher, callback, scope){
		var regularPolygon;
		var map = this.map;
		
		if(geodesic){
			regularPolygon = OpenLayers.Geometry.Polygon.createGeodesicPolygon(
				point,
				radius,
				100, 
				0,
				map.getProjectionObject()
			);
			
		} else {
			
			var newRadius = radius;
			
			var geomUnits = this.map.getUnits();    
			var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[scope.distanceUnit];
			
			if(inPerDisplayUnit) {
				var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
				newRadius *= (inPerDisplayUnit/inPerMapUnit);
			}			 
			
			regularPolygon = OpenLayers.Geometry.Polygon.createRegularPolygon(
				point,
				newRadius,
				100, 
				0
			);
		}

		if(style){
			var layer = map.getLayersByName(layername)[0];
            if(layer){
                map.removeLayer(layer);
            }
                
            var style = new OpenLayers.Style(style);
            
			var bufferLayer = new OpenLayers.Layer.Vector(layername, {
                styleMap: style                
            });

            var bufferFeature = new OpenLayers.Feature.Vector(regularPolygon);
            bufferLayer.addFeatures([bufferFeature]);			
            bufferLayer.displayInLayerSwitcher = displayInLayerSwitcher;
            
            if(callback){
            	callback.call(scope ? scope : this, bufferLayer, bufferFeature);
            }
            
            map.addLayer(bufferLayer);  
        } 
	},
	
	/**
     * Attiva il controllo OpenLayers di selezione spaziale.
	 * @param {Object} scope Scope su cui applicare le operazioni contenute.
     */
	polygonSpatialSelectorActive: function(scope){
		/**
		 * Create Polygon Selector
		 */
		scope.drawings = new OpenLayers.Layer.Vector({},
			{
				displayInLayerSwitcher:false,
				styleMap : new OpenLayers.StyleMap({
					"default" : scope.defaultStyle,
					"select" : scope.selectStyle,
					"temporary" : scope.temporaryStyle
				})
			}
		);
			
		scope.persistance = new OpenLayers.Layer.Vector({},
			{
				displayInLayerSwitcher:false,
				styleMap : new OpenLayers.StyleMap({
					"default" : scope.defaultStyle,
					"select" : scope.selectStyle,
					"temporary" : scope.temporaryStyle
				})
			}
		);

		scope.drawings.events.on({
            "featureadded": function(event) {
            	scope.setCurrentGeometry(event.feature.geometry);
            	scope.persistance.addFeatures([event.feature]);
            	scope.freeze();
            },                                
            "beforefeatureadded": function(event) {
            	scope.drawings.destroyFeatures();
            },
            scope: scope
        });                                 
    
		// Aggiunge il layers di selezione spaziale alla mappa
		this.addLayer(scope.drawings);
		
		// Aggiunge il layer di visualizzazione definitiva
		this.addLayer(scope.persistance);
		
		scope.draw = scope.getDrawControl();
        
		// disable pan while drawing
		scope.draw.handler.stopDown = true;
		scope.draw.handler.stopUp = true;

		this.map.addControl(scope.draw);
		
        scope.draw.activate();
	},
	
	/**
     * Disattiva il controllo OpenLayers di selezione spaziale.
	 * @param {Object} scope Scope su cui applicare le operazioni contenute.
     */
	polygonSpatialSelectorDeactive: function(scope){
		if(scope.drawings){
			this.removeLayer(scope.drawings);
			scope.drawings = null;
		}
		if(scope.persistance){
			this.removeLayer(scope.persistance);
			scope.persistance = null;
		}
	},
	
	/**
     * Aggiorna le unità della mappa sul componente selezinato.
	 * @param {Object} component Componente su cui applicare le operazioni contenute.
     */
	setMapUnitsValueField: function(component){
		if(component){
			component.setValue(this.map.units);
		}
	}
	
});/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

Ext.ns('TolomeoExt.data');

/**
 * Un data store da usare come tool di autocompletamento paginato.
 * Non sono richiesti parametri di configurazione per il costruttore, il proxy
 * si appoggia alle canoniche funzionalità di TolomeoExt
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.data.ToloUniqueValuesStore', {
	
	extend: 'Ext.data.Store',
	
	alias: 'widget.tolomeo_uniquestore',

	/**
     * Crea un nuovo TolomeoExt.data.ToloUniqueValuesStore.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	constructor: function(config) {
        config.baseParams = Ext.apply(config.baseParams || {}, {});

        this.TOLOMEOServer =  config.TOLOMEOServer;
        this.TOLOMEOContext = config.TOLOMEOContext;
        
		var proxy = TolomeoExt.ToloCrossAjaxUtil.getProxy(null, this.TOLOMEOServer + this.TOLOMEOContext + '/UniqueValueServlet');
		var reader = proxy.getReader();
		reader.root = "rows";
		reader.totalProperty = "total";
		
        TolomeoExt.data.ToloUniqueValuesStore.superclass.constructor.call(this,
            Ext.applyIf(config, {
//                sortInfo: {
//                    field: 'value',
//                    direction: 'ASC'
//                },
                proxy: proxy
            })
        );
        
        this.pageSize = config.pageSize;
    },
	 
	/**
     * Imposta i parametri da passare al proxy per il caricamento dei dati.
     * @param {Object} params Un opzionale oggetto di configurazione per il componente ExtJs.
     */
    setParams: function(params) {
        this.baseParams = Ext.apply(this.baseParams, params);
    },
    
    /**
     * Carica i dati nello store.
     * @param {Object} options Oggetto contenente le opzioni di caricamento dei dati.
     */
    load: function(options) {
    	var count = this.getCount() > 0 && this.pageSize == 0;
     	if(!count){
        	var params = options.params;
            if (!params.inputs) {
                return;
            }
            
            var filter;
            params.query = params.query || "*";
            if(params.query) {
                var queryValue = params.query;
                if(queryValue.indexOf('*') === -1) {
                    queryValue = '*' + queryValue + '*';
                }
                
                filter = new OpenLayers.Filter.Comparison({ 
                    type: OpenLayers.Filter.Comparison.LIKE, 
                    property: params.inputs.fieldName, 
                    value: queryValue,
                    matchCase: false                
                });
                
                var node = new OpenLayers.Format.Filter({version: "1.1.0"}).write(filter);
                filter = new OpenLayers.Format.XML().write(node);
            }
            
    		var fparams = {
    			filter: filter,
    			codTPN: params.inputs.featureTypeName,
    			format: "ext",
    			ogcFilterVersion: "1.1.0",
    			attributeName: params.inputs.fieldName,
    			attributeLogicalName : params.inputs.attributeLogicalName 
    		};
    		
    		this.proxy.extraParams = this.proxy.extraParams || {};
    		this.proxy.startParam = "startIndex";
    		this.proxy.limitParam = "maxFeatures";
    		this.proxy.actionMethods = "POST";
    		
        	Ext.apply(this.proxy.extraParams, fparams); 
    		
            if (options) {
                this.baseParams = Ext.apply(this.baseParams, options.params);
            }
            
            this.superclass.load.call(this, options);
    	}
    } 
});

/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

// /////////////////////////////////////
// requires OpenLayers/Control.js
// requires OpenLayers/Handler/Box.js
// /////////////////////////////////////

/**
 * Controllo OpenLayers per la selezione del Box.
 * 
 * @extends {OpenLayers.Control}
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
OpenLayers.Control.SetBox = OpenLayers.Class(OpenLayers.Control, {

    /**
     * @cfg {OpenLayers.Control.TYPE} type [displayProjection="OpenLayers.Control.TYPE_TOOL"]
     * 
     */ 
    type: OpenLayers.Control.TYPE_TOOL,

    /**
     * @cfg {Boolean} out [out="false"]
     * 
     */ 
    out: false,
    
    /**
     * @cfg {Object} aoi [aoi="null"]
     * 
     */ 
    aoi: null,
    
    /**
     * @cfg {OpenLayers.Bounds} boxes [boxes="null"]
     * 
     */ 
    boxes: null,
    
    /**
     * @cfg {String} currentAOI
     * 
     */ 
    currentAOI: "",
    
    /**
     * @cfg {Function} onChangeAOI [onChangeAOI="null"]
     * 
     */ 
    onChangeAOI: null,
    
    /**
     * @cfg {String} layerName [layerName="AOI"]
     * 
     */ 
    layerName: "AOI",
    
    /**
     * @cfg {Object} aoiStyle [aoiStyle="null"]
     * 
     */ 
    aoiStyle: null,
    
    /**
     * @cfg {OpenLayers.Map} map
     * 
     */ 
    map: null,
    
    /**
     * @cfg {Boolean} displayInLayerSwitcher [displayInLayerSwitcher="false"]
     * 
     */ 
    displayInLayerSwitcher: false,

	/**
     * Disegna il BOX.
     * 
     */ 
    draw: function() {
       
        this.handler = new OpenLayers.Handler.Box(this,
        {
            done: this.setAOI
        }, 
        {
            boxDivClassName: this.boxDivClassName   
        },
        {
            keyMask: this.keyMask
        });
    },
    
    /**
     * Aggiorna il disegno del bbox
     * @param {Number} ascissa minima.
     * @param {Number} ordinata minima.
     * @param {Number} ascissa massima.
     * @param {Number} ordinata massima.
     * 
     */
    updateBoxDimension: function(xmin, ymin, xmax, ymax){
    	
    	if(this.aoi!=null){       
            this.boxes.removeFeatures(this.aoi);
        }
    	
    	var bounds;
    	
    	this.currentAOI = xmin + ',' + ymin + ',' + xmax + ',' + ymax;   
    	bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
    	
    	if(this.layerName){
            var x=this.map.getLayersByName(this.layerName);
            var index=null;
            if(x.length>0){
                index=this.map.getLayerIndex(x[0]);
                this.map.removeLayer(x[0]);
            }
            var me=this;
            this.boxes  = new OpenLayers.Layer.Vector( this.layerName,{
                displayInLayerSwitcher: me.displayInLayerSwitcher,
                styleMap: me.aoiStyle
            });
            this.aoi = new OpenLayers.Feature.Vector(bounds.toGeometry());
            this.boxes.addFeatures(this.aoi);
            this.map.addLayer(this.boxes);

            if(index){
                this.map.setLayerIndex(this.boxes,index);
            }
        }	
    },

	/**
     * Imposta la regione di interesse risultante dal disegno dell'utente sulla mappa.
     * @param {Object} la posizione del Box nella mappa.
     * 
     */
    setAOI: function (position) {
        var control;
      
        if(this.map.enebaleMapEvent)
            control = this.map.enebaleMapEvent;
        else
            control = false;
           
        if(control){                    
            
            var xmin, ymin, xmax, ymax;
            
            if (position instanceof OpenLayers.Bounds) {
                if (!this.out) {
                	
                    var minXY = this.map.getLonLatFromPixel(
                        new OpenLayers.Pixel(position.left, position.bottom));
                    var maxXY = this.map.getLonLatFromPixel(
                        new OpenLayers.Pixel(position.right, position.top));

                    xmin = minXY.lon;
                    ymin = minXY.lat;
                    xmax = maxXY.lon;
                    ymax = maxXY.lat;
                    
                } else {
                	
                    var pixWidth = Math.abs(position.right-position.left);
                    var pixHeight = Math.abs(position.top-position.bottom);
                    var zoomFactor = Math.min((this.map.size.h / pixHeight),
                        (this.map.size.w / pixWidth));
                    var extent = this.map.getExtent();
                    var center = this.map.getLonLatFromPixel(
                        position.getCenterPixel());
                    var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
                    var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
                    var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
                    var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
                    
                }

                this.updateBoxDimension(xmin, ymin, xmax, ymax);

                if(this.onChangeAOI)
                    this.onChangeAOI();
                   
            } else { 
                //
                // it's a pixel
                //
                if (!this.out) {
                    this.map.setCenter(this.map.getLonLatFromPixel(position),
                        this.map.getZoom() + 1);
                } else {
                    this.map.setCenter(this.map.getLonLatFromPixel(position),
                        this.map.getZoom() - 1);
                }
            }
        }      
    },

    CLASS_NAME: "OpenLayers.Control.SetBox"
    	
});
/*
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Rappresentazione di vertice per il routing
 * Può essere utilizzato come vertice iniziale, finale o intermedio.
 * 
 * @param street {string} nome della strada
 * @param point {JSGeometry} geometria
 * 
 * @property street {string} nome della strada
 * @property point {JSGeometry} geometria
 * 
 * @constructor
 */
function ToloOLSVertex(street, point, id)
{
	this.street = street;
	this.point = point;
	this.id = id;
}

/**
 * Modello dei dati generale per l'estensione OpenLS
 * 
 * @property geocodedAddresses {GeocodedAddress[]} array di indirizzi georiferiti
 * @property geometry {Point} geometria del punto di cui è richiesto il reverse geocoding
 * @property srsName {string} sistema di riferimento del punto di cui è richiesto il reverse geocoding
 * @property startPoint {ToloOLSVertex} vertice iniziale del routing
 * @property endPoint {ToloOLSVertex} vertice finale del routing
 * @property viaPoints {ToloOLSVertex[]} array di vertici intermedi del routing
 * @property method {string} metodo di routing
 * @property routeResponse {RouteResponse} risposta di routing
 * 
 * @constructor
 */
function ToloOLSModel()
{
	this.geocodedAddresses = null;
	this.geometry = null;
	this.srsname = null;
	this.startPoint = null;
	this.endPoint = null;
	this.viaPoints = [];
	this.viaPointId = 0;
	this.method = 'Fastest';
	this.routeResponse = null;
}

/**
 * Rappresentazione di una indirizzo georiferito
 * 
 * @param geometry {JSGeometry} geometria
 * @param postalCode {string} codice postale
 * @param address {Address} indirizzo
 * @param match {GeocodeMatch} tipo di match
 * 
 * @property geometry {JSGeometry} geometria
 * @property postalCode {string} codice postale
 * @property summary {Address} indirizzo
 * @property match {GeocodeMatch} tipo di match
 * 
 * @constructor
 */
function GeocodedAddress(geometry, srsName, postalCode, address, match)
{
 	this.geometry = geometry;
 	this.srsName = srsName;
 	this.postalCode = postalCode;
 	this.address = address;
 	this.match = match;
}

/**
 * Rappresentazione di un indirizzo
 * 
 * @param countryCode {string} codice nazione
 * @param streetAddress {StreetAddress} Indirizzo della strada
 * @param places {string[string]} luoghi
 * 
 * @property countryCode {string} codice nazione
 * @property streetAddress {StreetAddress} Indirizzo della strada
 * @property places {string[string]} luoghi
 * 
 * @constructor
 */
function Address(countryCode, streetAddress, places)
{
 	this.countryCode = countryCode;
 	this.streetAddress = streetAddress;
 	this.places = places;
}

/**
 * Rappresentazione di un indirizzo di una strada
 * 
 * @param street {string} Indirizzo della strada
 * 
 * @property street {string} Indirizzo della strada
 * 
 * @constructor
 */
function StreetAddress(street)
{
 	this.street = street;
}

/**
 * Rappresentazione di un match
 * 
 * @param accuracy {string} indice di accuratezza
 * @param type {string} tipo di match
 * 
 * @property accuracy {string} indice di accuratezza
 * @property type {string} tipo di match
 * 
 * @constructor
 */
function GeocodeMatch(accuracy, type)
{
 	this.accuracy = accuracy;
 	this.type = type;
}

/**
 * Rappresentazione di una risposta di routing
 * 
 * @param summary {RouteSummary} sommario del routing
 * @param geometry {JSGeometry} geometria
 * @param instructions {RouteInstruction[]} array di istruzioni di routing
 * 
 * @property summary {RouteSummary} sommario del routing
 * @property geometry {JSGeometry} geometria
 * @property instructions {RouteInstruction[]} array di istruzioni di routing
 * 
 * @constructor
 */
function RouteResponse(summary, geometry, instructions)
{
 	this.summary = summary;
 	this.geometry = geometry;
 	this.instructions = instructions;
}

/**
 * Rappresentazione delle informazioni di sintesi di una risposta di routing
 * 
 * @param totalTime {string} durata
 * @param totalDistance {TotalDistance} distanza totale
 * @param boundingBox {BBox} bounding box del routing
 * 
 * @property totalTime {string} durata
 * @property totalDistance {TotalDistance} distanza totale
 * @property boundingBox {BBox} bounding box del routing
 * 
 * @constructor
 */
function RouteSummary(totalTime, totalDistance, boundingBox)
{
 	this.totalTime = totalTime;
 	this.totalDistance = totalDistance;
 	this.boundingBox = boundingBox;
}

/**
 * Rappresentazione della distanza percorsa da un routing
 * 
 * @param value {number} distanza
 * @param uom {string} unità di misura
 * @param accuracy {number} accuratezza percentuale
 * 
 * @property value {number} distanza
 * @property uom {string} unità di misura
 * @property accuracy {number} accuratezza percentuale
 * 
 * @constructor
 */
function TotalDistance(value, uom, accuracy)
{
 	this.value = value;
 	this.uom = uom;
 	this.accuracy = accuracy;
}

/**
 * Rappresentazione di una istruzione di routing
 * 
 * @param instruction {string} istruzioni di routing
 * @param distance {number} distanza del singolo lag
 * @param geometry {JSGeometry} geoemtria del lag
 * 
 * @property instruction {string} istruzioni di routing
 * @property distance {number} distanza del singolo lag
 * @property geometry {JSGeometry} geoemtria del lag
 * 
 * @constructor
 */
function RouteInstruction(instruction, textInstruction, distance, geometry)
{
 	this.instruction = instruction;
 	this.textInstruction = textInstruction;
 	this.distance = distance;
 	this.geometry = geometry;
 	this.instructionId = Math.random(); 
}
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Class: TolomeoExt.OLS.BaseForm
 * Form base
 * @author Ugo Paternostro <br/>phoops s.r.l.
 */
Ext.define('TolomeoExt.OLS.BaseForm', {
	extend: 'Ext.form.FormPanel',
	
	/** 
	 * Property: srid
	 * {} sistema di riferimento in cui sono espresse le coordinate
	 */
	srid: null,
	
	/** 
	 * Property: url
	 * {} URL dove è esposto il server OpenLS
	 */
    url: 'http://localhost/geoserver/ols',
	
	/** 
	 * Property: username
	 * {} utente per l'autenticazione OpenLS
	 */
	username: null,
	
	/** 
	 * Property: password
	 * {} password per l'autenticazione OpenLS
	 */
	password: null,
	
	/** 
	 * Property: useProxyServlet
	 * {} forza l'utilizzo della TolomeoProxyServlet
	 */
	useProxyServlet: false,
	
	/** 
	 * Property: TOLOMEOContext
	 * {String}
	 */
	TOLOMEOContext: null,
	
    initComponent:function() {
    	var config = 
    	{
		    xlsNamespace:	'http://www.opengis.net/xls',
		    gmlNamespace:	'http://www.opengis.net/gml',
		    xlsVersion:		'1.2',
		    
		    submitRequest: function(method, requestId, xmlBuilderCallback, successCallback) {
		    	var	me = this;
		    	
	        	document.body.style.cursor = "wait";
	        	
		   		var xmlhttp = null;
				
		   		if (window.XMLHttpRequest) {
		    		// code for IE7+, Firefox, Chrome, Opera, Safari
		   			xmlhttp = new XMLHttpRequest();
		   		} else {
		    		// code for IE6, IE5
		   			xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		   		}
		   		
		   		var url = this.url;
		   		
		   		if (this.useProxyServlet) {
		   			url = this.TOLOMEOContext + "/TolomeoProxyServlet?" + this.url;
		   		}
		   		
		   		// Handle POST request
		   		xmlhttp.open("POST", url);
		   		
		   		xmlhttp.onreadystatechange = function() {
		   			if (xmlhttp.readyState == 4)
		   			{
		   			    switch (xmlhttp.status)
		   			    {
			   			    case 200:
			   			    	if (xmlhttp.responseXML != null) {
				   			    	// Check the workspace error;
				   			    	var errorMess = xmlhttp.responseXML.toString();
				   			    	
				   			    	if (errorMess != "noWorkspace") {
							        	// lvl 0
							        	var xls = xmlhttp.responseXML.firstChild;

							        	if (xls != null && xls.localName == 'XLS' && xls.namespaceURI == me.xlsNamespace) {
								        	// lvl 1
								        	var responseList = xls.getElementsByTagNameNS(me.xlsNamespace, "Response");
								        	
								        	if (responseList != null && responseList.length == 1) {
							   			    	successCallback.call(me, responseList.item(0));
								        	} else {
									    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noResponse.title"), ToloI18n.getMsg("BaseForm.err.noResponse.msg"));
								        	}
							        	} else {
								    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noXLS.title"), ToloI18n.getMsg("BaseForm.err.noXLS.msg"));
							        	}
				   			    	} else {
				   			    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noWKS.title"), ToloI18n.getMsg("BaseForm.err.noWKS.msg"));
				   			    	}
			   			    	} else {
			   			    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noWKS.title"), ToloI18n.getMsg("BaseForm.err.noWKS.msg"));
			   			    	}
			   			        break;
						    case 401:
						    	Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noAuth.title"), ToloI18n.getMsg("BaseForm.err.noAuth.msg"));
						        break;
						    case 404:
						    	Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noService.title"), ToloI18n.getMsg("BaseForm.err.noService.msg"));
						        break;
						    case 500:
						    	var dlg = new Ext.window.MessageBox({autoScroll: true}).show({
						    		title: ToloI18n.getMsg("BaseForm.err.server.title"), 
						    		msg: ToloI18n.getMsg("BaseForm.err.server.msg", {method : method }),
						    		buttons: Ext.Msg.YESNO,
						    	    icon: Ext.Msg.ERROR,
						    	    fn: function(btn) {
						    	        if (btn === 'yes') {
									    	new Ext.window.MessageBox({autoScroll: true}).alert(ToloI18n.getMsg("BaseForm.err.server.title"), xmlhttp.responseText);
						    	        } 
						    	    }
						    	});
						    	dlg.defaultButton = 2;
						    	break;
						    default:
						    	Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.generico.title"), ToloI18n.getMsg("BaseForm.err.generico.msg", {xmlhttpstatus: xmlhttp.status, xmlhttpresponseText: xmlhttp.responseText}));
						    	break;
		   			    }
		   			    
				    	document.body.style.cursor = "default";
		   			}    
		   	    };
		   	    
		   	    var dom = this.createEmptyXMLDoc('XLS xmlns="' + this.xlsNamespace + '"');
				var root = dom.documentElement;
				root.setAttribute("version",	this.xlsVersion);
				var header = dom.createElementNS(this.xlsNamespace, 'RequestHeader');
				header.setAttribute("srsName",	this.srid);
				if (this.username != null && this.username != "") {
					header.setAttribute("clientName", this.username);
				}
				if (this.password != null && this.password != "") {
					header.setAttribute("clientPassword", this.password);
				}
				root.appendChild(header);
				var request = dom.createElementNS(this.xlsNamespace, 'Request');
				request.setAttribute("methodName",	method);
				request.setAttribute("version",		this.xlsVersion);
				request.setAttribute("requestID",	requestId);
				root.appendChild(request);
				
				xmlBuilderCallback.call(me, dom, request);
				
		   	    xmlhttp.send(dom);
		   	},
		   	createEmptyXMLDoc: function(root)
		   	{
		   		var xmlDoc;
		   		
		   		if (window.DOMParser) {
			   		parser=new DOMParser();
			   		xmlDoc=parser.parseFromString("<" + root + "/>", "text/xml");
		   		} else {
		   			// Internet Explorer
			   		xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
			   		xmlDoc.async="false";
			   		xmlDoc.loadXML("<" + root + "/>");
		   		}
		   		
		   		return xmlDoc;
		   	},
	        buildPosition: function(dom, pointObj, srsName)
	        {
	        	var	position = dom.createElementNS(this.xlsNamespace, 'Position');
	        	var	point = dom.createElementNS(this.gmlNamespace, 'Point');
	        	var	pos = dom.createElementNS(this.gmlNamespace, 'pos');
	        	pos.setAttribute("srsName", srsName)
	        	var	posValue = dom.createTextNode(pointObj.x + " " + pointObj.y);
	        	pos.appendChild(posValue);
	        	point.appendChild(pos);
	        	position.appendChild(point);
	        	
	        	return position;
	        },
	        parseGeocodedAddresses: function(geocodedAddressList, isMatchCodeMandatory)
	        {
	        	var geocodedAddresses = [];
	        	
	        	for (var i = 0; i < geocodedAddressList.length; i++) {
	        		var item = geocodedAddressList.item(i);
	        		
		        	// lvl 5
		        	var pointList = item.getElementsByTagNameNS(this.gmlNamespace, "Point");
		        	
		        	if (pointList == null || pointList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.nopoint.title"), ToloI18n.getMsg("BaseForm.err.nopoint.msg"));
		        		return;
		        	}
	        		
		        	var postalCodeList = item.getElementsByTagNameNS(this.xlsNamespace, "PostalCode");
		        	
//		        	if (postalCodeList == null || postalCodeList.length != 1) {
//			    		Ext.MessageBox.alert('Error', 'No PostalCode found in geocoding response!');
//		        		return;
//		        	}
		        	
		        	var addressList = item.getElementsByTagNameNS(this.xlsNamespace, "Address");
		        	
		        	if (addressList == null || addressList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noaddress.title"), ToloI18n.getMsg("BaseForm.err.noaddress.msg"));
		        		return;
		        	}
		        	
		        	var address = addressList.item(0);
	        		
		        	var gmcList = item.getElementsByTagNameNS(this.xlsNamespace, "GeocodeMatchCode");
		        	
		        	if (gmcList == null || (isMatchCodeMandatory && gmcList.length != 1)) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.nogeomatch.title"), ToloI18n.getMsg("BaseForm.err.nogeomatch.msg"));
		        		return;
		        	}
	        		
		        	var gmc = gmcList.item(0);
	        		
		        	// lvl 6
		        	var posList = pointList.item(0).getElementsByTagNameNS(this.gmlNamespace, "pos");
		        	
		        	if (posList == null || posList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.nopos.title"), ToloI18n.getMsg("BaseForm.err.nopos.msg"));
		        		return;
		        	}
		        	
		        	var pos = posList.item(0);
	        		
		        	var streetAddressList = address.getElementsByTagNameNS(this.xlsNamespace, "StreetAddress");
		        	
		        	if (streetAddressList == null || streetAddressList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noStreetAddr.title"), ToloI18n.getMsg("BaseForm.err.noStreetAddr.msg"));
		        		return;
		        	}
	        		
		        	var placeList = address.getElementsByTagNameNS(this.xlsNamespace, "Place");
		        	
		        	if (placeList == null || placeList.length < 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noPlace.title"), ToloI18n.getMsg("BaseForm.err.noPlace.msg"));
		        		return;
		        	}
	        		
		        	// lvl 7
		        	var streetList = streetAddressList.item(0).getElementsByTagNameNS(this.xlsNamespace, "Street");
		        	
		        	if (streetList == null || streetList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("BaseForm.err.noStreet.title"), ToloI18n.getMsg("BaseForm.err.noStreet.msg"));
		        		return;
		        	}
	        		
		        	// let's rock
	        		var position = pos.firstChild.nodeValue.split(" ");
	        		var postalCode = "";
	        		var places = {};
	        		
	        		if (postalCodeList != null && postalCodeList.length == 1) {
	        			postalCode = postalCodeList.item(0).firstChild.nodeValue;
	        		}
	        		
	        		for (var j = 0; j < placeList.length; j++) {
	        			var place = placeList.item(j);
	        			
	        			places[place.getAttribute("type")] = place.firstChild.nodeValue;
	        		}
	        		
	        		geocodedAddresses.push(
		        		new GeocodedAddress(
			        			new Point(
			        				position[0],
			        				position[1]
			        			),
			        			pos.getAttribute("srsName"),
			        			postalCode,
			        			new Address(
			        					address.getAttribute("countryCode"),
			        				new StreetAddress(
			        					streetList.item(0).firstChild.nodeValue
			        				),
			        				places
			        			),
			        			gmc == null ? null : new GeocodeMatch(
			        				gmc.getAttribute("accuracy"),
			        				gmc.getAttribute("type")
			        			)
			        		)
	        		);
	        	}
	        	
	        	return geocodedAddresses;
	        }
    	};
    	
    	Ext.apply(this, Ext.apply(this.initialConfig, config));
		this.callParent();
    }
});/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Class: TolomeoExt.OLS.GeocodingForm
 * Form di geocoding
 * @author Ugo Paternostro <br/>phoops s.r.l.
 */
Ext.define('TolomeoExt.OLS.GeocodingForm', {
	extend: 'TolomeoExt.OLS.BaseForm',
	
	itemId: 'olsGeocodingID',
	collapsible: true,
	
    initComponent:function() {
		// Eventi
    	this.addEvents('dataReceived');
		
    	var config = 
    	{
		        labelWidth:		75, // label settings here cascade unless 	
		        frame:			true,
		        title:			'Ricerca indirizzo',
		        bodyStyle:		'padding:5px 5px 0',
		        cls:			'floating-form_left',
		        height:			150,
		        defaults: {
		        	width:		230
		        },
		        defaultType:	'textfield',
		        
		        items: [
		          {
		        	  xtype: 'combo',
		        	  store: new Ext.data.ArrayStore({
		        	        fields: [
		        	            'abbr',
		        	            'prov'
		        	        ],
		        	        data: [ ['AR', 'Arezzo - AR'], // @@@ FIXME!
		        	                ['FI', 'Firenze - FI'],
		        	                ['GR', 'Grosseto - GR'],
		        	                ['LI', 'Livorno - LI'],
		        	                ['LU', 'Lucca - LU'],
		        	                ['MS', 'Massa-Carrara - MS'],
		        	                ['PI', 'Pisa - PI'],
		        	                ['PT', 'Pistoia - PT'],
		        	                ['PO', 'Prato - PO'],
		        	                ['SI', 'Siena - SI']
		        	        	 ]
		        	    }),
		        	  valueField: 'abbr',
			          displayField: 'prov',
			          typeAhead: true,
			          mode: 'local',
			          forceSelection: true,
			          triggerAction: 'all',
			          emptyText:ToloI18n.getMsg("GeocodingForm.cmbProv.emptyText"),
			          selectOnFocus:true,
		        	  fieldLabel: ToloI18n.getMsg("GeocodingForm.cmbProv.lbl"),
		        	  itemId: 'provincia'
		          },
		          {
		        	  fieldLabel: ToloI18n.getMsg("GeocodingForm.fldComune"),	    	   
		        	  itemId: 'comune',
		        	  listeners: {
			              scope: this,
		                  specialkey: function(f, e) {
		                    if (e.getKey() == e.ENTER) {
		                    	this.submit();
		                    }
		                  }
		              }
		          },
		          {
		        	  fieldLabel: ToloI18n.getMsg("GeocodingForm.fldTopo"),
		        	  itemId: 'via',
		        	  listeners: {
			              scope: this,
		                  specialkey: function(f, e) {
		                    if (e.getKey() == e.ENTER) {
		                    	this.submit();
		                    }
		                  }
		              },
		        	  validator: function(v) {
		        		  if (v === "" || v == null || v.lenght == 0) {
		        		        return ToloI18n.getMsg("GeocodingForm.fldTopo.invalid");
		        		  }
		        		  
		        		  return true;
		        	  }
		          }
		        ],
		        buttons: [
		            {
		            	text: ToloI18n.getMsg("GeocodingForm.btnTrova"),
		            	scope: this,
		            	handler: function(toponimo) {
			        	  this.submit();
		            	}
		            },
		            {
		            	text: ToloI18n.getMsg("GeocodingForm.btnPulisci"),
		            	scope: this,
		            	handler: function(){
		            		this.items.get('provincia').setValue(null);
		            		this.items.get('comune').setValue(null);
		            		this.items.get('via').setValue(null);
			            }	
		            }
		        ],
		        submit: function() {
		        	if (this.items.get('provincia').isValid()
		        			&& this.items.get('comune').isValid()
		        			&& this.items.get('via').isValid()) {
		        		this.submitRequest("GeocodeRequest", "GRid", this.buildXMLRequest, this.onSuccess);
		        	}
		        },
		        buildXMLRequest: function(dom, request)
		        {
		        	var	geocodeRequest = dom.createElementNS(this.xlsNamespace, 'GeocodeRequest');
		        	var	address = dom.createElementNS(this.xlsNamespace, 'Address');
		        	address.setAttribute("countryCode", "IT");
		        	var	streetAddress = dom.createElementNS(this.xlsNamespace, 'StreetAddress');
		        	var	street = dom.createElementNS(this.xlsNamespace, 'Street');
		        	var	streetName = dom.createTextNode(this.items.get('via').getValue());
		        	street.appendChild(streetName);
		        	streetAddress.appendChild(street);
		        	address.appendChild(streetAddress);
		        	var	municipality = dom.createElementNS(this.xlsNamespace, 'Place');
		        	municipality.setAttribute("type", "Municipality");
		        	var	municipalityName = dom.createTextNode(this.items.get('comune').getValue());
		        	municipality.appendChild(municipalityName);
		        	address.appendChild(municipality);
		        	var	countrySecondarySubdivision = dom.createElementNS(this.xlsNamespace, 'Place');
		        	countrySecondarySubdivision.setAttribute("type", "CountrySecondarySubdivision");
		        	var	countrySecondarySubdivisionName = dom.createTextNode(this.items.get('provincia').getValue());
		        	countrySecondarySubdivision.appendChild(countrySecondarySubdivisionName);
		        	address.appendChild(countrySecondarySubdivision);
		        	var	postalCode = dom.createElementNS(this.xlsNamespace, 'PostalCode');
		        	address.appendChild(postalCode);
		        	geocodeRequest.appendChild(address);
		        	request.appendChild(geocodeRequest);
		        },
		        onSuccess: function(response)
		        {
		        	// lvl 2
		        	var grList = response.getElementsByTagNameNS(this.xlsNamespace, "GeocodeResponse");
		        	
		        	if (grList == null || grList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("GeocodingForm.indirizzoNonTrovato.title"),ToloI18n.getMsg("GeocodingForm.indirizzoNonTrovato.msg"));
		        		return;
		        	}
		        	
		        	// lvl 3
		        	var grlList = grList.item(0).getElementsByTagNameNS(this.xlsNamespace, "GeocodeResponseList");
		        	
		        	if (grlList == null || grlList.length != 1) {
		        		Ext.MessageBox.alert(("GeocodingForm.indirizzoNonTrovato.title"),ToloI18n.getMsg("GeocodingForm.indirizzoNonTrovato.msg"));
		        		return;
		        	}
		        	
		        	// lvl 4
		        	var geocodedAddressList = grlList.item(0).getElementsByTagNameNS(this.xlsNamespace, "GeocodedAddress");
		        	
		        	if (geocodedAddressList == null || geocodedAddressList.length < 1) {
		        		Ext.MessageBox.alert(("GeocodingForm.indirizzoNonTrovato.title"),ToloI18n.getMsg("GeocodingForm.indirizzoNonTrovato.msg"));
		        		return;
		        	}
		        	
   			    	this.fireEvent('dataReceived', response.getAttribute("requestID"), this.parseGeocodedAddresses(geocodedAddressList, true));
		        }
    	};
    	
    	Ext.apply(this, Ext.apply(this.initialConfig, config));
		this.callParent();
    }
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Class: TolomeoExt.OLS.ReverseGeocodingForm
 * Form di reverse geocoding
 * @author Ugo Paternostro <br/>phoops s.r.l.
 */
Ext.define('TolomeoExt.OLS.GeocodingListPanel', {
	extend: 'Ext.grid.GridPanel',
	
    itemId:	"streetsList",
    
	/** 
	 * Property: iconBasePath
	 * 
	 */
	iconBasePath: null,
	
	/** 
	 * Property: model
	 * 
	 */
	model: null,
	
    initComponent:function() {
    	this.addEvents('addressSelected');
    	this.addEvents('addressFocus');
    	this.addEvents('addressBlur');
    	this.addEvents('startAddressSelected');
    	this.addEvents('endAddressSelected');
    	this.addEvents('viaAddressSelected');
    	
        var config = {
        		frame:true,
            	stripeRows: true,
//                autoExpandColumn: 'street',
                height: 240,
               // title: 'Open LS Results',
                // config options for stateful behavior
                stateful: true,
                bodyStyle:'padding:5px',
                cls: 'floating-form_right',

                singleSelect : true,
                reserveScrollOffset: true,

                store: new Ext.data.ArrayStore({
                    fields: [
                       {name: 'street', convert: function(value, record) {
                           return record.raw.address.streetAddress.street;
                       }},
                       {name: 'postalcode', convert: function(value, record) {
                           return record.raw.postalCode;
                       }},
                       {name: 'place', convert: function(value, record) {
                           return record.raw.address.places['Municipality'];
                       }},
                       {name: 'secondarysubdivision', convert: function(value, record) {
                           return record.raw.address.places['CountrySecondarySubdivision'];
                       }},
                       {name: 'subdivision', convert: function(value, record) {
                           return record.raw.address.places['CountrySubdivision'];
                       }},
                       {name: 'country', convert: function(value, record) {
                           return record.raw.address.countryCode;
                       }}
                    ]
                }),
                refreshData: function(){
                    this.store.loadData(this.model.geocodedAddresses);
                },
        		viewConfig: {
	    			//firstCls: null,
	    			trackOver: true
	    		},
	    		listeners: {
        			'itemmouseenter':	{
        									fn: function(record, item, index, e) { 
						        				this.fireEvent('addressFocus', item); 
						        			},
						        			scope: this
						    			},
        			'itemmouseleave':	{
        									fn: function(record, item, index, e) { 
						        				this.fireEvent('addressBlur', item); 
						        			},
						        			scope: this
						    			},
        			'itemclick':		{
        									fn: function(obj, record, item, index, e) {
        										// Evitiamo di far partire due eventi nel caso di click su un'icona di un action column
        										if (e.getTarget().className.indexOf("action-col") == -1) {
        											this.fireEvent('addressSelected', record);
        										}
						        			},
						        			scope: this
					        			}
	    		},
                        columns: [
                                {
                              	  	menuDisabled: true,
                                    xtype: 'actioncolumn',
                                    width: 25,
                                    items: [{
                                        icon   : this.iconBasePath + 'ols/MapMarker.png',
                                        tooltip: 'Zoom into map',
                                        scope: this,
                                        handler: function(view, rowIndex, colIndex, item, e, record, row) {
                           			        this.fireEvent('addressSelected', record);
                                        }
                                    }]
                                },
                                  {
                                      id       :'street',
                                      header   : ToloI18n.getMsg("GeocodingListPanel.colStrada"),
                                      width    : 220,
                                      sortable : true,
                                      dataIndex: 'street'
                                  },
                                  {
                                      header   : ToloI18n.getMsg("GeocodingListPanel.colCAP"),
                                      width    : 40,
                                      sortable : true,
                                      dataIndex: 'postalcode'
                                  },
                                  {
                                      header   : ToloI18n.getMsg("GeocodingListPanel.colLuogo"),
                                      width    : 150,
                                      sortable : true,
                                      dataIndex: 'place'
                                  },
                                  {
                                      header   : ToloI18n.getMsg("GeocodingListPanel.colProvincia"),
                                      width    : 80,
                                      sortable : true,
                                      dataIndex: 'secondarysubdivision'
                                  },
                                  {
                                      header   : ToloI18n.getMsg("GeocodingListPanel.colRegione"),
                                      width    : 80,
                                      sortable : true,
                                      dataIndex: 'subdivision'
                                  },
                                  {
                                      header   : ToloI18n.getMsg("GeocodingListPanel.colNazione"),
                                      width    : 50,
                                      sortable : true,
                                      dataIndex: 'country'
                                  },
                                  {
                               	  	  menuDisabled: true,
                                      xtype: 'actioncolumn',
                                      width: 30,
                                      sortable : false,
                                      items: [{
                                          icon   : this.iconBasePath + 'ols/start.png',
                                          tooltip: ToloI18n.getMsg("GeocodingListPanel.Partenza"),
                                          scope: this,
                                          handler: function(view, rowIndex, colIndex, item, e, record, row) {
                             			      this.fireEvent('startAddressSelected', record);
                                          }
                                      }]
                                  },
                                  {
                               	  	  menuDisabled: true,
                                      xtype: 'actioncolumn',
                                      width: 30,
                                      sortable : false,
                                      items: [{
                                          icon   : this.iconBasePath + 'ols/via.png',
                                          tooltip: ToloI18n.getMsg("GeocodingListPanel.Arrivo"),
                                          scope: this,
                                          handler: function(view, rowIndex, colIndex, item, e, record, row) {
                             			      this.fireEvent('viaAddressSelected', record);
                                          }
                                      }]
                                  },
                                  {
                               	  	  menuDisabled: true,
                                      xtype: 'actioncolumn',
                                      width: 30,
                                      sortable : false,
                                      items: [{
                                          icon   : this.iconBasePath + 'ols/stop.png',
                                          tooltip: ToloI18n.getMsg("GeocodingListPanel.Interm"),
                                          scope: this,
                                          handler: function(view, rowIndex, colIndex, item, e, record, row) {
                             			      this.fireEvent('endAddressSelected', record);
                                          }
                                      }]
                                  }
                                  
                              ]
        };
    	
    	Ext.apply(this, Ext.apply(this.initialConfig, config));
		this.callParent();
    }
});/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Class: TolomeoExt.OLS.NavigationListPanel
 * Pannello per le istruzioni di navigazione
 * @author Ugo Paternostro <br/>phoops s.r.l.
 */
Ext.define('TolomeoExt.OLS.NavigationListPanel', {
	extend: 'Ext.grid.GridPanel',
	
    itemId:	"navigationList",
    
	/** 
	 * Property: model
	 * 
	 */
	model: null,
    
    initComponent:function() {
    	this.addEvents('navigationSelected');
    	this.addEvents('navigationFocus');
    	this.addEvents('navigationBlur');
    	
        var config = {
        		//frame:true,
//            	stripeRows: true,
//                autoExpandColumn: 'street',
//            	width: '78%',
//            	width: '22%',
                height: 185,
               // title: 'Navigation Information',
                // config options for stateful behavior
                stateful: true,
//                bodyStyle:'padding:5px 5px 0',
//                cls: 'floating-form_left',
                store: new Ext.data.ArrayStore({
                    fields: [
                             {name: 'navigation', convert: function(value, record) {
                                 return record.raw.instruction;
                             }},
                             {name: 'instructionId', convert: function(value, record) {
                                 return record.raw.instructionId;
                             }}]
                      }),
                singleSelect : true,
                header: false,
//                reserveScrollOffset: true,
                refreshData: function()
                {
                	this.store.loadData(this.model.routeResponse.instructions);
                },
	    		listeners: {
        			'itemmouseenter':	{
        									fn: function(thisgrid, record, item, index, e) { 
						        				this.fireEvent('navigationFocus', index); 
						        			},
						        			scope: this
						    			},
        			'itemmouseleave':	{
        									fn: function(thisgrid, record, item, index, e) { 
						        				this.fireEvent('navigationBlur', index); 
						        			},
						        			scope: this
						    			},
        			'itemclick':		{
        									fn: function(thisgrid, record, item, index, e) { 
						        				this.fireEvent('navigationSelected', index); 
						        			},
						        			scope: this
					        			} 
	    		},
                columns: [
                         {
                              itemId   :'navigation',
                      //        header   : 'Navigation Info',
                              width    : '100%',
                              sortable : false,
                              dataIndex: 'navigation'
                          }
                      ]
        };
    	
    	Ext.apply(this, Ext.apply(this.initialConfig, config));
		this.callParent();
    },
    
    routingInformationSelect: function(instructionId) {
    	var idx = this.store.find('instructionId', instructionId);
    	this.getSelectionModel().select(idx);
	}, 
	
	routingInformationDeSelect: function(instructionId) {
		var idx = this.store.find('instructionId', instructionId);
		this.getSelectionModel().deselect(idx);
	}
    
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Class: TolomeoExt.OLS.ReverseGeocodingForm
 * Form di reverse geocoding
 * @author Ugo Paternostro <br/>phoops s.r.l.
 */
Ext.define('TolomeoExt.OLS.ReverseGeocodingForm', {
	extend: 'TolomeoExt.OLS.BaseForm',
	
	itemId: 'olsReverseGeocodingID',
	collapsible: true,
	
	pointObj: null,
	srsName: null,
	
	initComponent:function() {
		// Eventi
    	this.addEvents('dataReceived');
		
		var config = {
				itemId: 'formReverseId',
		        labelWidth: 75, // label settings here cascade unless 	
		        frame:true,
		        collapsed:true,
		        title: ToloI18n.getMsg("ReverseGeocodingForm.title"),
		        bodyStyle:'padding:5px 5px 0',
//		        width: '22%',
		        cls: 'floating-form_left',
		        height: 100,
		        defaults: {width: 230},
		        defaultType: 'textfield',	
		        
		        items: [
				          {
				        	  fieldLabel: ToloI18n.getMsg("ReverseGeocodingForm.lat"),
				        	  itemId: 'lat',
				        	  disabled: true
				          },
				          {
				        	  fieldLabel: ToloI18n.getMsg("ReverseGeocodingForm.lon"),
				        	  itemId: 'lon',
				        	  disabled : true
				          }
				        ],
				        
		        submit: function(requestId) {
	        		this.submitRequest("ReverseGeocodeRequest", requestId, this.buildXMLRequest, this.onSuccess);
		        },
		        buildXMLRequest: function(dom, request)
		        {
		        	var	reverseGeocodeRequest = dom.createElementNS(this.xlsNamespace, 'ReverseGeocodeRequest');
		        	var	position = this.buildPosition(dom, this.pointObj, this.srsName);
		        	reverseGeocodeRequest.appendChild(position);
		        	request.appendChild(reverseGeocodeRequest);
		        },
		        onSuccess: function(response)
		        {
		        	// lvl 2
		        	var rgrList = response.getElementsByTagNameNS(this.xlsNamespace, "ReverseGeocodeResponse");
		        	
		        	if (rgrList == null || rgrList.length != 1) {
		        		//Ext.MessageBox.alert('Informazione', 'Nessun indirizzo trovato vicino a questa posizione');
		        		this.fireEvent('dataReceived', response.getAttribute("requestID"), []);
		        		return;
		        	}
		        	
		        	// lvl 3
		        	var rglList = rgrList.item(0).getElementsByTagNameNS(this.xlsNamespace, "ReverseGeocodedLocation");
		        	
		        	if (rglList == null || rglList.length < 1) {
		        		//Ext.MessageBox.alert('Informazione', 'Nessun indirizzo trovato vicino a questa posizione');
		        		this.fireEvent('dataReceived', response.getAttribute("requestID"), []);
		        		return;
		        	}
		        	
   			    	this.fireEvent('dataReceived', response.getAttribute("requestID"), this.parseGeocodedAddresses(rglList, false));
		        },
		        reverseGeocode: function(requestId, geometry, srsName)
		        {
		        	this.pointObj = geometry;
		        	this.srsName = srsName;
		        	this.submit(requestId);
		        }
    	};
    	
		Ext.apply(this, Ext.apply(this.initialConfig, config));
		this.callParent();
	}
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Class: TolomeoExt.OLS.NavigationListPanel
 * Pannello per le istruzioni di navigazione
 * @author Alessandro Radaelli
 */
Ext.define('TolomeoExt.OLS.NavigationSummaryPanel', {
	extend: 'Ext.Panel',
	
	/** 
	 * Property: model
	 * 
	 */
	model: null,
    
    initComponent:function() {
    	    	
        var config = {
                height: 70,
                title: 'Sommario', 
                collapsible: true,
                collapsed: false,
                bodyStyle:'padding:5px 5px 0'
//                cls: 'floating-form_left',
        };
    	
    	Ext.apply(this, Ext.apply(this.initialConfig, config));
		this.callParent();
		
    },
    
    refreshData: function() {
    	
    	var totalTime 		= "";
    	var totalDistance 	= "";
    	
    	if (this.model && this.model.routeResponse && this.model.routeResponse.summary) {
    		totalTime = this.model.routeResponse.summary.totalTime;
        	totalDistance = this.model.routeResponse.summary.totalDistance;
    	}
    	
    	var regExp =  /PT(\d*H)?(\d*M)?(\d*\.)(\d*S)/;
    	var time = regExp.exec(totalTime);
    	
    	var ore = time[1] ? time[1].substring(0,time[1].length-1) + "h " : ""; 
    	var minuti = time[2] ? time[2].substring(0,time[2].length-1) + "m " : "";
    	var secondi = time[3] ? time[3].substring(0,time[3].length-1) + "s " : "";
    	
    	this.update(ToloI18n.getMsg("NavigationSummaryPanel.navtime", {tempo: ore + minuti + secondi, dist:Ext.util.Format.number(totalDistance.value, '0.00'), um: totalDistance.uom }));
    }

});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Class: TolomeoExt.OLS.RoutingNavigationForm
 * Form di geocoding
 * @author Ugo Paternostro <br/>phoops s.r.l.
 */
Ext.define('TolomeoExt.OLS.RoutingNavigationForm', {
	extend: 'TolomeoExt.OLS.BaseForm',
	
	itemId: 'olsRoutingNavigationID',
	collapsible: true,
	
	/** 
	 * Property: model
	 * 
	 */
	model: null,
	
    initComponent:function() {
    	this.addEvents('methodChanged');
    	this.addEvents('reset');
    	this.addEvents('routeReceived');
    	
    	var config = 
    	{
		        labelWidth: 75, // label settings here cascade unless 	
		        frame:true,
		        //title: 'Routing Navigation',
		        bodyStyle:'padding:5px 5px 0',
//		        width: '22%',
		        cls: 'floating-form_left',
		        height: 200,
		        defaults: {
		        	width: 230
		        },
//		        defaultType: 'textfield',
		    	instructionFormat: "text/html",
		    	
		        items: [
		          {
		        	  xtype: 'textfield',
		        	  fieldLabel: ToloI18n.getMsg("RoutingNavigationForm.partenza"),
		        	  itemId: 'startPoint',
		        	  disabled: true,
		        	  handler: function(storeData){
		        		  	var store = storeData.toString();
		        		  	var values=store.split(",");
				        	this.setValue(values[0]);
		        	  },
		        	  validator: function(v) {
		        		  if (v == null || v === "" || v.lenght == 0) {
		        		        return ToloI18n.getMsg("RoutingNavigationForm.partenza.noncorr");
		        		    }
		        		    return true;
                      }
		          },
		          {
		        	  xtype: 'textfield',
		        	  fieldLabel: ToloI18n.getMsg("RoutingNavigationForm.arrivo"),
		        	  itemId: 'endPoint',
		        	  disabled: true,
		        	  handler: function(storeData){
		        		  	var store = storeData.toString();
		        		  	var values=store.split(",");
				        	this.setValue(values[0]);
		        	  },
		        	  validator: function(v) {
		        		  if (v == null || v === "" || v.lenght == 0) {
		        		        return ToloI18n.getMsg("RoutingNavigationForm.arrivo.noncorr");
		        		    }
		        		    return true;
                      }
		          },
		          {
		        	  xtype: 'combo',
		        	  store: new Ext.data.ArrayStore({
		        	        id: 0,
		        	        fields: [
		        	            'method'
		        	        ],
		        	        data: [ [ToloI18n.getMsg("RoutingNavigationForm.veloce")],
		        	                [ToloI18n.getMsg("RoutingNavigationForm.corto")],
		        	                [ToloI18n.getMsg("RoutingNavigationForm.metodo.pedoni")]
		        	        	 ]
		        	    }),
			          displayField: 'method',
			          typeAhead: true,
			          mode: 'local',
			          forceSelection: true,
			          triggerAction: 'all',
			          value: 'Fastest',
//			          emptyText:'Seleziona un Metodo...',
			          selectOnFocus:true,
		        	  fieldLabel: 'Method',
		        	  itemId: 'method',
		        	  listeners: {
		        		  scope: this,
			        	  select: function(combo, records, eOpts) {
			        		  this.fireEvent('methodChanged', records[0].raw);
			        	  }
		        	  }
		          },
		          {
		        	  	xtype:'label',
	                    text: ToloI18n.getMsg("RoutingNavigationForm.info"),
	                    name: 'infoLabel',
	                    labelStyle: 'font-weight:bold;',
	                    anchor:'93%'	    	   
		          }
		        ],
		        buttons: [
		            {
		            	text: ToloI18n.getMsg("RoutingNavigationForm.btnInvia"),
		            	scope: this,
		            	handler: function(toponimo){
	                    	this.submit();
		            	}
		            },
		            {	
		            	text: ToloI18n.getMsg("RoutingNavigationForm.btnPulisci"),
		            	scope: this,
		            	handler: function(){
			        		this.fireEvent('reset', 'dummy');
			            }	
		            }
		        ],
		        submit: function() {
		        	if (this.model.startPoint != null
		        			&& this.model.endPoint != null
		        			&& this.items.get('method').isValid()) {
		        		this.submitRequest("DetermineRouteRequest", "RRid", this.buildXMLRequest, this.onSuccess);
		        	}
		        },
		        buildXMLRequest: function(dom, request)
		        {
		        	var	determineRouteRequest = dom.createElementNS(this.xlsNamespace, 'DetermineRouteRequest');
		        	var	routePlan = dom.createElementNS(this.xlsNamespace, 'RoutePlan');
		        	var	routePreference = dom.createElementNS(this.xlsNamespace, 'RoutePreference');
		        	var	routePreferenceValue = dom.createTextNode(this.model.method);
		        	routePreference.appendChild(routePreferenceValue);
		        	routePlan.appendChild(routePreference);
		        	var	wayPointList = dom.createElementNS(this.xlsNamespace, 'WayPointList');
		        	
		        	// start point
		        	var	startPoint = dom.createElementNS(this.xlsNamespace, 'StartPoint');
		        	var	startPosition = this.buildPosition(dom, this.model.startPoint.point, this.srid);
		        	startPoint.appendChild(startPosition);
		        	wayPointList.appendChild(startPoint);
		        	
		        	// VIAs
            		for(var i = 0; i < this.model.viaPoints.length; i++) {
    		        	var	viaPoint = dom.createElementNS(this.xlsNamespace, 'ViaPoint');
    		        	var	viaPosition = this.buildPosition(dom, this.model.viaPoints[i].point, this.srid);
    		        	viaPoint.appendChild(viaPosition);
    		        	wayPointList.appendChild(viaPoint);
            		}
            		
            		// end point
		        	var	endPoint = dom.createElementNS(this.xlsNamespace, 'EndPoint');
		        	var	endPosition = this.buildPosition(dom, this.model.endPoint.point, this.srid);
		        	endPoint.appendChild(endPosition);
		        	wayPointList.appendChild(endPoint);
		        	routePlan.appendChild(wayPointList);
		        	determineRouteRequest.appendChild(routePlan);
		        	
		        	var	routeInstructionsRequest = dom.createElementNS(this.xlsNamespace, 'RouteInstructionsRequest');
		        	routeInstructionsRequest.setAttribute("format", this.instructionFormat);
		        	routeInstructionsRequest.setAttribute("provideGeometry", "false");
		        	determineRouteRequest.appendChild(routeInstructionsRequest);
		        	
		        	request.appendChild(determineRouteRequest);
		        },
		        onSuccess: function(response)
		        {
		        	// lvl 2
		        	var drrList = response.getElementsByTagNameNS(this.xlsNamespace, "DetermineRouteResponse");
		        	
		        	if (drrList == null || drrList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("RoutingNavigationForm.err.noDetRoute.title"),ToloI18n.getMsg("RoutingNavigationForm.err.noDetRoute.msg"));
		        		return;
		        	}
		        	
		        	var drr = drrList.item(0);
		        	
		        	// lvl 3
		        	var summaryList = drr.getElementsByTagNameNS(this.xlsNamespace, "RouteSummary");
		        	
		        	if (summaryList == null || summaryList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("RoutingNavigationForm.err.noRouteSumm.title"),ToloI18n.getMsg("RoutingNavigationForm.err.noRouteSumm.msg"));
		        		return;
		        	}
		        	
		        	var geometryList = drr.getElementsByTagNameNS(this.xlsNamespace, "RouteGeometry");
		        	
		        	if (geometryList == null || geometryList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("RoutingNavigationForm.err.noRouteGeom.title"),ToloI18n.getMsg("RoutingNavigationForm.err.noRouteGeom.msg"));
		        		return;
		        	}
		        	
		        	var instructionsListList = drr.getElementsByTagNameNS(this.xlsNamespace, "RouteInstructionsList");
		        	
		        	if (instructionsListList == null || instructionsListList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("RoutingNavigationForm.err.noRouteInstList.title"),ToloI18n.getMsg("RoutingNavigationForm.err.noRouteInstList.msg"));
		        		return;
		        	}
		        	
		        	var summary = summaryList.item(0);
		        	
		        	// lvl 4
		        	var totalTimeList = summary.getElementsByTagNameNS(this.xlsNamespace, "TotalTime");
		        	
		        	if (totalTimeList == null || totalTimeList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("RoutingNavigationForm.err.noTotalTime.title"),ToloI18n.getMsg("RoutingNavigationForm.err.noTotalTime.msg"));
		        		return;
		        	}
		        	
		        	var totalDistanceList = summary.getElementsByTagNameNS(this.xlsNamespace, "TotalDistance");
		        	
		        	if (totalDistanceList == null || totalDistanceList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("RoutingNavigationForm.err.noTotalDist.title"),ToloI18n.getMsg("RoutingNavigationForm.err.noTotalDist.msg"));
		        		return;
		        	}
		        	
		        	var boundingBoxList = summary.getElementsByTagNameNS(this.xlsNamespace, "BoundingBox");
		        	
		        	if (boundingBoxList == null || boundingBoxList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("RoutingNavigationForm.err.noBBOX.title"),ToloI18n.getMsg("RoutingNavigationForm.err.noBBOX.msg"));
		        		return;
		        	}
		        	
		        	var lineStringList = geometryList.item(0).getElementsByTagNameNS(this.gmlNamespace, "LineString");
		        	
		        	if (lineStringList == null || lineStringList.length != 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("RoutingNavigationForm.err.noLineStr.title"),ToloI18n.getMsg("RoutingNavigationForm.err.noLineStr.msg"));
		        		return;
		        	}
		        	
		        	var instructionsList = instructionsListList.item(0).getElementsByTagNameNS(this.xlsNamespace, "RouteInstruction");
		        	
		        	if (instructionsList == null || instructionsList.length < 1) {
			    		Ext.MessageBox.alert(ToloI18n.getMsg("RoutingNavigationForm.err.noRouteInst.title"),ToloI18n.getMsg("RoutingNavigationForm.err.noRouteInst.msg"));
		        		return;
		        	}
		        	
		        	// let's rock ;)
		        	var distanceAttributes = totalDistanceList.item(0).attributes;
		        	var routeGeometry = this.createLineString(lineStringList.item(0));
		        	var instructions = [];
		        	
		        	for (var i = 0; i < instructionsList.length; i++) {
		        		instructions.push(this.createRoutingInstruction(instructionsList.item(i)));
		        	}
		        	
		        	var routeResponse = new RouteResponse(
	        			new RouteSummary(
    	        			totalTimeList.item(0).firstChild.nodeValue,
    	        			new TotalDistance(
        		        		distanceAttributes.getNamedItem("value").nodeValue,
        		        		distanceAttributes.getNamedItem("uom").nodeValue,
        		        		distanceAttributes.getNamedItem("accuracy").nodeValue
        		        	),
    	        			this.createBoundingBox(boundingBoxList.item(0))
    	        		),
    	        		routeGeometry,
    	        		instructions
		        	);
		        	
   			    	this.fireEvent('routeReceived', response.getAttribute("requestID"), routeResponse);
		        },
		        createBoundingBox: function(xml)
		        {
	        		var posList = xml.getElementsByTagNameNS(this.gmlNamespace, "pos");
	        		var bottomLeftCoords = posList.item(0).firstChild.nodeValue.split(" ");
	        		var topRightCoords = posList.item(1).firstChild.nodeValue.split(" ");
	        		
	        		return new BBox(bottomLeftCoords[1], bottomLeftCoords[0], topRightCoords[1], topRightCoords[0]);
		        },
		        createLineString: function(xml)
		        {
		        	var routeWKT = "LINESTRING(";
        			var posList = xml.getElementsByTagNameNS(this.gmlNamespace, "pos");
        			
        			for (var i = 0; i < posList.length; i++) {
    	        		var itemPos = posList.item(i);
        				
        				if (i == posList.length - 1) {
        					routeWKT += itemPos.firstChild.nodeValue;
        				} else {
        					routeWKT += itemPos.firstChild.nodeValue + ",";
        				}
        			}
        			
        			routeWKT += ")";
        			
					return new JSGeometry(null, null, "OLS route", routeWKT, null, xml.attributes.getNamedItem("srsName").nodeValue, null);
		        },
		        createRoutingInstruction: function(xml)
		        {
		        	// lvl 1
        			var instructionList = xml.getElementsByTagNameNS(this.xlsNamespace, "Instruction");
        			var distanceList = xml.getElementsByTagNameNS(this.xlsNamespace, "distance");
        			var geometryList = xml.getElementsByTagNameNS(this.xlsNamespace, "RouteInstructionGeometry");
        			
        			// lvl 2
        			var lineStringList = geometryList.item(0).getElementsByTagNameNS(this.gmlNamespace, "LineString");
        			
        			var instruction = instructionList.item(0).firstChild.nodeValue;
        			var textInstruction;
        			
        			switch (this.instructionFormat) {
        				case "text/plain":
        					textInstruction = instruction;
        					break;
        				case "text/html":
        					textInstruction = instruction.replace(/<[^>]*>/g, ' ').trim();
        					break;
        			}
        			
        			return new RouteInstruction(
        				instruction,
        				textInstruction,
            			distanceList.item(0).attributes.getNamedItem('value').nodeValue,
            			this.createLineString(lineStringList.item(0))
        			);
		        },
		        changeStartAddress: function() {
		        	this.items.get('startPoint').setValue(this.model.startPoint == null ? "" : this.model.startPoint.street);
		        },
		        changeEndAddress: function() {
		        	this.items.get('endPoint').setValue(this.model.endPoint == null ? "" : this.model.endPoint.street);
		        },
		        changeViaAddresses: function() {
		        	// do nothing at the moment
		        },
		        changeMethod: function() {
		        	this.items.get('method').setValue(this.model.method);
		        }
    	};
    	
    	Ext.apply(this, Ext.apply(this.initialConfig, config));
		this.callParent();
    }
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * Class: TolomeoExt.OLS.ViaListPanel
 * Pannello per le tappe intermedie
 * @author Ugo Paternostro <br/>phoops s.r.l.
 * 
 */
Ext.define('TolomeoExt.OLS.ViaListPanel', {
	extend: 'Ext.grid.GridPanel',
	
	itemId: "viaPointList",
	
	/** 
	 * Property: iconBasePath
	 * 
	 */
	iconBasePath: null,
	
	/** 
	 * Property: model
	 * 
	 */
	model: null,
	
	initComponent: function() {
		// Eventi
    	this.addEvents('viaPointFocus');
    	this.addEvents('viaPointBlur');
    	this.addEvents('viaPointSelected');
    	this.addEvents('deleteViaPoint');
    	this.addEvents('moveViaPoint');
    	
		var config = {
				collapsible: true,
			    requires: [
			               'Ext.grid.RowNumberer'
			           ],
			  // frame: true,
				stripeRows: true,
//				width: '30%',
				height: 240,
				title: ToloI18n.getMsg("ViaListPanel.title"),
				// config options for stateful behavior
                stateful: true,
                bodyStyle:'padding:5px',
              //  cls: 'floating-form_right',
                store: new Ext.data.ArrayStore({
                    fields: [
                             {name: 'stopAt', convert: function(value, record) {
                                 return record.raw.street;
                             }}
                          ],
                    data: this.model.viaPoints
                      }),
                singleSelect : true,
            //    autoScroll: true,
                //reserveScrollOffset: true,
	    		listeners: {
        			'itemmouseenter':	{
        									fn: function(record, item, index, e) { 
						        				this.fireEvent('viaPointFocus', item); 
						        			},
						        			scope: this
						    			},
        			'itemmouseleave':	{
        									fn: function(record, item, index, e) { 
						        				this.fireEvent('viaPointBlur', item); 
						        			},
						        			scope: this
						    			},
        			'itemclick':		{
        									fn: function(record, item, index, e) { 
						        				this.fireEvent('viaPointSelected', item); 
						        			},
						        			scope: this
					        			}
	    		},
        		columns: [
                          {
                        	  xtype: 'rownumberer'
                          }, 
                           {
                        	  menuDisabled: true,
                               itemId       :'stopAt',
                              // header   : 'Stop At',
                               width    : 160,
                               sortable : false,
                               dataIndex: 'stopAt'
                           },
                           {
                        	   menuDisabled: true,
                               xtype: 'actioncolumn',
                               width: 30,
                               sortable : false,
                               items: [{
                                   icon   : this.iconBasePath + 'elimina.gif',
                                   tooltip: ToloI18n.getMsg("ViaListPanel.elimina"),
                                   scope: this,
                                   handler: function(view, recordIndex, cellIndex, item, e, record, row)
                                   {
                                	   this.fireEvent('deleteViaPoint', recordIndex);
                                   }
                               }]
                           },
                           {
                        	   menuDisabled: true,
                        	   xtype: 'actioncolumn',
                               width: 30,
                               sortable : false,
                               items: [{
                                   icon   : this.iconBasePath + 'ols/top.png',
                                   tooltip: ToloI18n.getMsg("ViaListPanel.su"),
                                   isDisabled: function(view, rowIndex, colIndex, item, record)
                                   {
                                	   return rowIndex == 0;
                                   },
                                   scope: this,
                                   handler: function(view, recordIndex, cellIndex, item, e, record, row)
                                   {
                                	   this.fireEvent('moveViaPoint', recordIndex, -1);
                                   }
                               }]
                           },
                           {
                        	   menuDisabled: true,
                        	   xtype: 'actioncolumn',
                               width: 30,
                               sortable : false,
                               items: [{
                                   icon   : this.iconBasePath + 'ols/down.png',
                                   tooltip: ToloI18n.getMsg("ViaListPanel.giu"),
                                   isDisabled: function(view, rowIndex, colIndex, item, record)
                                   {
                                	   return rowIndex == record.store.data.length - 1;
                                   },
                                   scope: this,
                                   handler: function(view, recordIndex, cellIndex, item, e, record, row)
                                   {
                                	   this.fireEvent('moveViaPoint', recordIndex, +1);
                                   }
                               }]
                           }
                          ],
            changeViaAddresses: function()
            {
            	this.store.loadData(this.model.viaPoints);
            }
		};
		
    	Ext.apply(this, Ext.apply(this.initialConfig, config));
		this.callParent();
	}
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/

/**
 * Class: TolomeoExt.ToloOLSPanelExt
 * Funzioni pubbliche di geocoding/reversegeocoding/routing & navigation
 * @author Ugo Paternostro <br/>phoops s.r.l.
 */
Ext.define('TolomeoExt.OLS.ToloOLSPanelExt', {

	extend: 'Ext.Panel',
	//requires: [],


	/** 
	 * Property: paramsJS
	 * {JSONObject}
	 */
	paramsJS: null,

	/** 
	 * Property: TOLOMEOServer
	 * {String}
	 */
	TOLOMEOServer: null,

	/** 
	 * Property: TOLOMEOContext
	 * {String}
	 */
	TOLOMEOContext: null,
	
	/** 
	 * Property: iconBasePath
	 * 
	 */
	iconBasePath: null,
	
	/** 
	 * Property: model
	 * 
	 */
	model: null,
	
	/** 
	 * Property: pnlGeocoding
	 * {}
	 */
	pnlGeocoding: null,

	/** 
	 * Property: pnlGeocodingList
	 * {}
	 */
	pnlGeocodingList: null,
	
	/** 
	 * Property: wndGeocodingList
	 * {}
	 */
	wndGeocodingList: null,
	
	/** 
	 * Property: pnlReverseGeocoding
	 * {}
	 */
	pnlReverseGeocoding: null,

	/** 
	 * Property: pnlReverseGeocoding
	 * {}
	 */
	pnlRoutingNavigation: null,

	/** 
	 * Property: pnlNavigationSummary
	 * {}
	 */
	pnlNavigationSummary: null,
	
	/** 
	 * Property: pnlNavigationList
	 * {}
	 */
	pnlNavigationList: null,

	/** 
	 * Property: wndNavigationList
	 * {}
	 */
	wndNavigationList: null,

	/** 
	 * Property: pnlViaList
	 * {}
	 */
	pnlViaList: null,

	/**
	 * Constructor: TolomeoExt.ToloOLSPanelExt
	 * Crea un nuovo TolomeoExt.ToloOLSPanelExt
	 *
	 * Returns:
	 * {<TolomeoExt.ToloOLSPanelExt>} Un nuovo TolomeoExt.ToloOLSPanelExt
	 */
	initComponent: function() {
    	this.addEvents('geocodingDataReceived');
    	this.addEvents('reverseGeocodingDataReceived');
    	this.addEvents('addressFocus');
    	this.addEvents('addressBlur');
    	this.addEvents('addressSelected');
    	this.addEvents('startAddressSelected');
    	this.addEvents('endAddressSelected');
    	this.addEvents('viaAddressAdded');
    	this.addEvents('reset');
    	this.addEvents('methodChanged');
    	this.addEvents('routeReceived');
    	this.addEvents('navigationFocus');
    	this.addEvents('navigationBlur');
    	this.addEvents('navigationSelected');
    	this.addEvents('viaPointFocus');
    	this.addEvents('viaPointBlur');
    	this.addEvents('viaPointSelected');
    	this.addEvents('viaPointsChanged');
    	
    	// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
		this.layout = {
						type: 'vbox',
						align: 'stretch'
		};
		
		this.on('activate', this.focusFirstField);
		
		if (this.iconBasePath==null) this.iconBasePath =  TolomeoExt.Vars.TOLOMEOServer + TolomeoExt.Vars.TOLOMEOStaticRoot + 'img/icone/16-default/';
		
		this.model = new ToloOLSModel();
		
		if (this.paramsJS.layOut.ols.conGeocoding) {
			this.pnlGeocoding = Ext.create('TolomeoExt.OLS.GeocodingForm', {
				srid: this.paramsJS.mappe.SRID,
			    url: this.paramsJS.layOut.ols.url,
				username: this.paramsJS.layOut.ols.username,
				password: this.paramsJS.layOut.ols.password,
			    useProxyServlet: this.paramsJS.layOut.ols.useProxyServlet,
			    TOLOMEOContext: this.TOLOMEOContext
			});			
		}
		
		this.pnlGeocodingList = Ext.create('TolomeoExt.OLS.GeocodingListPanel', {
			iconBasePath: this.iconBasePath,
			model: this.model
		});
		
		this.wndGeocodingList = Ext.create('Ext.window.Window', {
			title: ToloI18n.getMsg("ToloOLSPanelExt.wndNavigationList"),
			layout: 'fit',
			height: 400,
			width: 800,
			maximizable: true,
			closeAction: 'hide',
			items: [this.pnlGeocodingList]}); 
	
		
		this.pnlReverseGeocoding = Ext.create('TolomeoExt.OLS.ReverseGeocodingForm', {
		    url: this.paramsJS.layOut.ols.url,
			username: this.paramsJS.layOut.ols.username,
			password: this.paramsJS.layOut.ols.password,
		    useProxyServlet: this.paramsJS.layOut.ols.useProxyServlet,
		    TOLOMEOContext: this.TOLOMEOContext
		});
		
		this.pnlRoutingNavigation = Ext.create('TolomeoExt.OLS.RoutingNavigationForm', {
			srid: this.paramsJS.mappe.SRID,
		    url: this.paramsJS.layOut.ols.url,
			username: this.paramsJS.layOut.ols.username,
			password: this.paramsJS.layOut.ols.password,
		    useProxyServlet: this.paramsJS.layOut.ols.useProxyServlet,
		    TOLOMEOContext: this.TOLOMEOContext,
			model: this.model
		});
		
		this.pnlNavigationList = Ext.create('TolomeoExt.OLS.NavigationListPanel', {
			model: this.model, 
			flex: 1
		});
		
		this.pnlNavigationSummary = Ext.create('TolomeoExt.OLS.NavigationSummaryPanel', {
			model: this.model
		}); 
		
		this.wndNavigationList = Ext.create('Ext.window.Window', {
			title: "Dati navigazione",
			//layout: 'fit',
			layout: {
				type: 'vbox',
				align: 'stretch'
			},
			height: 400,
			width: 500,
			maximizable: true,
			closeAction: 'hide',
			items: [this.pnlNavigationSummary, this.pnlNavigationList]}); 
		
		
		this.pnlViaList = Ext.create('TolomeoExt.OLS.ViaListPanel', {
			iconBasePath: this.iconBasePath,
			model: this.model,
			flex: 1
		});
		
		this.callParent();
		
		if (this.pnlGeocoding) {
			this.add(this.pnlGeocoding);
			
			this.pnlGeocoding.on('dataReceived', this.olsGeocodingDataReceivedHandler, this);
			   
	    	this.pnlGeocodingList.on('addressFocus', this.addressFocusHandler, this);
	    	this.pnlGeocodingList.on('addressBlur', this.addressBlurHandler, this);
	    	this.pnlGeocodingList.on('addressSelected', this.addressSelectedHandler, this);
	    	this.pnlGeocodingList.on('startAddressSelected', this.startAddressSelectedHandler, this);
	    	this.pnlGeocodingList.on('endAddressSelected', this.endAddressSelectedHandler, this);
	    	this.pnlGeocodingList.on('viaAddressSelected', this.viaAddressSelectedHandler, this);

		}
    	
		this.add(this.pnlViaList);
		
    	this.pnlReverseGeocoding.on('dataReceived', this.olsReverseGeocodingDataReceivedHandler, this);

    	this.pnlRoutingNavigation.on('methodChanged', this.methodChangedHandler, this);
    	this.pnlRoutingNavigation.on('reset', this.resetHandler, this);
    	this.pnlRoutingNavigation.on('routeReceived', this.routeReceivedHandler, this);

    	this.pnlNavigationList.on('navigationFocus', this.navigationFocusHandler, this);
    	this.pnlNavigationList.on('navigationBlur', this.navigationBlurHandler, this);
    	this.pnlNavigationList.on('navigationSelected', this.navigationSelectedHandler, this);
    	
    	this.pnlViaList.on('viaPointFocus', this.viaPointFocusHandler, this);
    	this.pnlViaList.on('viaPointBlur', this.viaPointBlurHandler, this);
    	this.pnlViaList.on('viaPointSelected', this.viaPointSelectedHandler, this);
    	this.pnlViaList.on('deleteViaPoint', this.deleteViaPointHandler, this);
    	this.pnlViaList.on('moveViaPoint', this.moveViaPointHandler, this);
    },
    
    // metodi privati
	
	// handler
	focusFirstField:function() {
		if ( this.pnlGeocoding && this.pnlGeocoding.items && this.pnlGeocoding.items.items.size>0) this.pnlGeocoding.items.items[0].focus(false, 30); 	
	},	
	olsGeocodingDataReceivedHandler: function(requestId, geocodedAddresses) {
		this.model.geocodedAddresses = geocodedAddresses;
		
		// Da spostare nella vista quando si fara' la separazione View-Controller
	    this.pnlGeocodingList.refreshData();
	    this.wndGeocodingList.show();
	    
    	this.fireEvent('geocodingDataReceived', geocodedAddresses);
	},
	olsReverseGeocodingDataReceivedHandler: function(requestId, geocodedAddresses) {
		if (requestId == "RGRid:USERPOINT") {
			if (geocodedAddresses.length>0) {
				this.model.geocodedAddresses = geocodedAddresses;
				
				// Da spostare nella vista quando si fara' la separazione View-Controller
			    this.pnlGeocodingList.refreshData();
			    this.wndGeocodingList.show();
			} else {
				Ext.MessageBox.alert(ToloI18n.getMsg("ToloOLSPanelExt.nessunind.title"),ToloI18n.getMsg("ToloOLSPanelExt.nessunind.msg"));
        		return;
			}
			
	    	this.fireEvent('reverseGeocodingDataReceived', geocodedAddresses);
		} else if (requestId == "RGRid:STARTPOINT") {
			if (geocodedAddresses.length>0) {
				this.model.startPoint.street = geocodedAddresses[0].address.streetAddress.street;
				this.pnlRoutingNavigation.changeStartAddress();
			}
		} else if (requestId == "RGRid:ENDPOINT") {
			if (geocodedAddresses.length>0) {
				this.model.endPoint.street = geocodedAddresses[0].address.streetAddress.street;
				this.pnlRoutingNavigation.changeEndAddress();
			}
		} else {
			// richiesta relativa ad un VIA point: id della forma RGRid:VIAPOINT:number
			var idArray = requestId.split(":");
			
			for (var i = 0; i < this.model.viaPoints.length; i++) {
				if (this.model.viaPoints[i].id == idArray[2]) {
					if (geocodedAddresses.length>0) {
						this.model.viaPoints[i].street = geocodedAddresses[0].address.streetAddress.street;
					} else {
						this.model.viaPoints[i].street = ToloI18n.getMsg("ToloOLSPanelExt.intermediocoord", {x: this.model.viaPoints[i].point.x.toFixed(4), y: this.model.viaPoints[i].point.y.toFixed(4)});
					}	
					this.pnlViaList.changeViaAddresses();
					break;
				}
			}
		}
	},
	addressFocusHandler: function(record) {
        var street = record.raw.address.streetAddress.street;
        var geometry = record.raw.geometry;
		
    	this.fireEvent('addressFocus', street, geometry.x, geometry.y, record.raw.srsName);
	},
	addressBlurHandler: function(record) {
        var street = record.raw.address.streetAddress.street;
        var geometry = record.raw.geometry;
		
    	this.fireEvent('addressBlur', street, geometry.x, geometry.y, record.raw.srsName);
	},
	addressSelectedHandler: function(record) {
        var street = record.raw.address.streetAddress.street;
        var geometry = record.raw.geometry;
		
    	this.fireEvent('addressSelected', street, geometry.x, geometry.y, record.raw.srsName, this.paramsJS.layOut.ols.zoomLevel);
	},
	startAddressSelectedHandler: function(record) {
        var street = record.raw.address.streetAddress.street;
        var geometry = record.raw.geometry;
        var srsName = record.raw.srsName;

        this.setStartAddress(geometry, srsName, street);
    	this.fireEvent('startAddressSelected', street, geometry.x, geometry.y, srsName, this.paramsJS.layOut.ols.zoomLevel);
	},
	endAddressSelectedHandler: function(record) {
        var street = record.raw.address.streetAddress.street;
        var geometry = record.raw.geometry;
        var srsName = record.raw.srsName;
		
        this.setEndAddress(geometry, srsName, street);
    	this.fireEvent('endAddressSelected', street, geometry.x, geometry.y, srsName, this.paramsJS.layOut.ols.zoomLevel);
	},
	viaAddressSelectedHandler: function(record) {
        var street = record.raw.address.streetAddress.street;
        var geometry = record.raw.geometry;
        var srsName = record.raw.srsName;
        var viaId = this.addViaAddress(geometry, srsName, street);
        
    	this.fireEvent('viaAddressAdded', viaId, street, geometry.x, geometry.y, srsName, this.paramsJS.layOut.ols.zoomLevel);
	},
	methodChangedHandler: function(method) {
		this.model.method = method;
		this.pnlRoutingNavigation.submit();
		
    	this.fireEvent('methodChanged', method);
	},
	resetHandler: function() {
		this.reset();
		
    	this.fireEvent('reset');
	},
	routeReceivedHandler: function(requestId, routeResponse) {
		this.model.routeResponse = routeResponse;
		
		// Da spostare nella vista quando si fara' la separazione View-Controller
	    this.wndGeocodingList.close();
	    this.pnlNavigationList.refreshData();
	    this.pnlNavigationSummary.refreshData();
	    this.wndNavigationList.show();
	    
    	this.fireEvent('routeReceived', routeResponse);
	},
	navigationFocusHandler: function(index) {
		var instruction = this.model.routeResponse.instructions[index]
		
    	this.fireEvent('navigationFocus', instruction, this.paramsJS.mappe.SRID);
	},
	navigationBlurHandler: function(index) {
		var instruction = this.model.routeResponse.instructions[index]
		
    	this.fireEvent('navigationBlur', instruction, this.paramsJS.mappe.SRID);
	},
	navigationSelectedHandler: function(index) {
		var instruction = this.model.routeResponse.instructions[index]
		
    	this.fireEvent('navigationSelected', instruction, this.paramsJS.mappe.SRID, this.paramsJS.layOut.ols.zoomLevel);
	},
	viaPointFocusHandler: function(record) {
        var geometry = record.raw.point;
		
    	this.fireEvent('viaPointFocus', record.raw.id, record.raw.street, geometry.x, geometry.y, this.paramsJS.mappe.SRID);
	},
	viaPointBlurHandler: function(record) {
        var geometry = record.raw.point;
		
    	this.fireEvent('viaPointBlur', record.raw.id, record.raw.street, geometry.x, geometry.y, this.paramsJS.mappe.SRID);
	},
	viaPointSelectedHandler: function(record) {
        var geometry = record.raw.point;
		
    	this.fireEvent('viaPointSelected', record.raw.id, record.raw.street, geometry.x, geometry.y, this.paramsJS.mappe.SRID, this.paramsJS.layOut.ols.zoomLevel);
	},
	deleteViaPointHandler: function(index) {
		this.model.viaPoints.splice(index, 1);
		
		// Da spostare nella vista quando si fara' la separazione View-Controller
    	this.pnlRoutingNavigation.changeViaAddresses();
        this.pnlViaList.changeViaAddresses();
		
		this.pnlRoutingNavigation.submit();
		
    	this.fireEvent('viaPointsChanged', this.model.viaPoints);
	},
	moveViaPointHandler: function(index, delta) {
		var via = this.model.viaPoints[index];
		this.model.viaPoints[index] = this.model.viaPoints[index+delta];
		this.model.viaPoints[index+delta] = via;
		
		// Da spostare nella vista quando si fara' la separazione View-Controller
    	this.pnlRoutingNavigation.changeViaAddresses();
        this.pnlViaList.changeViaAddresses();
		
		this.pnlRoutingNavigation.submit();
		
    	this.fireEvent('viaPointsChanged', this.model.viaPoints);
	},
	
	// metodi pubblici
	reverseGeocode: function(geometry, srsName) {
    	this.pnlReverseGeocoding.reverseGeocode("RGRid:USERPOINT", geometry, srsName);
    },
    setStartAddress: function(geometry, srsName, street) {
    	this.model.startPoint = new ToloOLSVertex(
    		street || ToloI18n.getMsg("ToloOLSPanelExt.setStartAddress.partenza"),
    		geometry.clone()
    	);
    	
    	if (this.paramsJS.mappe.SRID != srsName) {
	    	this.model.startPoint.point.transform(srsName, this.paramsJS.mappe.SRID);
    	}
		
		// Da spostare nella vista quando si fara' la separazione View-Controller
		this.pnlRoutingNavigation.changeStartAddress();
		
		this.pnlRoutingNavigation.submit();
		
		if (typeof street == "undefined") {
    		this.model.geometry = geometry;
    		this.model.srsName = srsName;
        	this.pnlReverseGeocoding.reverseGeocode("RGRid:STARTPOINT", geometry, srsName);
    	}
    },
    setEndAddress: function(geometry, srsName, street) {
    	this.model.endPoint = new ToloOLSVertex(
    		street || ToloI18n.getMsg("ToloOLSPanelExt.setEndAddress.arrivo"),
    		geometry.clone()
    	);
    	
    	if (this.paramsJS.mappe.SRID != srsName) {
	    	this.model.endPoint.point.transform(srsName, this.paramsJS.mappe.SRID);
    	}
    	
		// Da spostare nella vista quando si fara' la separazione View-Controller
		this.pnlRoutingNavigation.changeEndAddress();
		
		this.pnlRoutingNavigation.submit();
		
		if (typeof street == "undefined") {
    		this.model.geometry = geometry;
    		this.model.srsName = srsName;
        	this.pnlReverseGeocoding.reverseGeocode("RGRid:ENDPOINT", geometry, srsName);
    	}
    },
    addViaAddress: function(geometry, srsName, street) {
//        this._addViaAddress(geometry, srsName, street);
    	var viaId = this.model.viaPointId++;
    	
        var viaPoint = new ToloOLSVertex(
    		street || ToloI18n.getMsg("ToloOLSPanelExt.addViaAddress.intermedio"),
    		geometry.clone(),
    		viaId
	    );
        
    	if (this.paramsJS.mappe.SRID != srsName) {
    		viaPoint.point.transform(srsName, this.paramsJS.mappe.SRID);
    	}
        
        this.model.viaPoints.push(viaPoint);

		// Da spostare nella vista quando si fara' la separazione View-Controller
    	this.pnlRoutingNavigation.changeViaAddresses();
        this.pnlViaList.changeViaAddresses();
		
		this.pnlRoutingNavigation.submit();
		
		if (typeof street == "undefined") {
    		this.model.geometry = geometry;
    		this.model.srsName = srsName;
        	this.pnlReverseGeocoding.reverseGeocode("RGRid:VIAPOINT:" + viaId, geometry, srsName);
    	}
		
		return viaId;
    },
    setMethod: function(method) {
    	this.model.method = method;
    	
		// Da spostare nella vista quando si fara' la separazione View-Controller
    	this.pnlRoutingNavigation.changeMethod();
    	
		this.pnlRoutingNavigation.submit();
    },
    moveViaAddress: function(viaId, geometry, srsName) {
		for (var i = 0; i < this.model.viaPoints.length; i++) {
			if (this.model.viaPoints[i].id == viaId) {
				this.model.viaPoints[i].point = geometry.clone();
				
		    	if (this.paramsJS.mappe.SRID != srsName) {
		    		this.model.viaPoints[i].point.transform(srsName, this.paramsJS.mappe.SRID);
		    	}
		    	
				this.pnlRoutingNavigation.submit();
		    	
	    		this.model.geometry = geometry;
	    		this.model.srsName = srsName;
	        	this.pnlReverseGeocoding.reverseGeocode("RGRid:VIAPOINT:" + viaId, geometry, srsName);
				break;
			}
		}
    },
    reset: function() {
		this.model.startPoint = null;
		this.model.endPoint = null;
    	this.model.viaPoints = [];
    	this.model.viaPointId = 0;
    	this.model.method = 'Fastest';
    	this.model.routeResponse = null;
    	
		this.pnlRoutingNavigation.changeStartAddress();
		this.pnlRoutingNavigation.changeEndAddress();
    	this.pnlRoutingNavigation.changeViaAddresses();
        this.pnlViaList.changeViaAddresses();
    	this.pnlRoutingNavigation.changeMethod();
    	this.wndNavigationList.close();
    },
    
    routingInformationSelect: function(instructionId) {
    	this.pnlNavigationList.routingInformationSelect(instructionId);
	}, 
	
	routingInformationDeSelect: function(instructionId) {
		this.pnlNavigationList.routingInformationDeSelect(instructionId);
	}
    
});
/*!
 * CTemplate
 * Version 1.1
 * Copyright(c) 2011-2013 Skirtle's Den
 * License: http://skirtlesden.com/ux/ctemplate
 */
Ext.define('Skirtle.CTemplate', {
    extend: 'Ext.XTemplate',

    statics: {
        AUTO_ID: 0
    },

    // May need to be increased if components are included deeper in the data object
    copyDepth: 10,

    // Placeholder element template. Should be changed in tandem with getPlaceholderEl()
    cTpl: '<p id="ctemplate-{0}-{1}"></p>',

    // Flag
    isCTemplate: true,

    constructor: function() {
        var me = this;

        me.callParent(arguments);

        me.id = ++me.statics().AUTO_ID;

        me.reset();
    },

    /* Takes a recursive copy of the values provided, switching out components for placeholder values. The component ids
     * are recorded and injectComponents() uses the ids to find the placeholder elements in the DOM and switch in the
     * components.
     */
    copyValues: function(values, depth) {
        var me = this,
            id,
            copy = {},
            copyDepth = depth || me.copyDepth;

        if (copyDepth === 1) {
            return values;
        }

        if (Ext.isArray(values)) {
            return Ext.Array.map(values, function(value) {
                return me.copyValues(value, copyDepth - 1);
            });
        }

        if (!Ext.isObject(values)) {
            return values;
        }

        // This is the key sleight-of-hand that makes the whole thing work
        if (values.isComponent) {
            id = values.getId();
            me.ids.push(id);
            return Ext.String.format(me.cTpl, id, me.id);
        }

        Ext.Object.each(values, function(key, value) {
            // $comp is a special value for a renderTpl that references the current component
            copy[key] = key === '$comp' ? value : me.copyValues(value, copyDepth - 1);
        });

        return copy;
    },

    // Override
    doInsert: function() {
        var ret = this.callParent(arguments);

        // There's no guarantee this will succeed so we still need polling as well
        this.injectComponents();

        return ret;
    },

    /* We have to resort to polling for component injection as we don't have full control over when the generated HTML
     * will be added to the DOM
     */
    doPolling: function(interval) {
        var me = this;

        me.pollInterval = interval;

        if (me.pollId) {
            clearTimeout(me.pollId);
        }

        me.pollId = Ext.defer(me.injectComponents, interval, me);
    },

    getPlaceholderEl: function(id) {
        return Ext.get('ctemplate-' + id + '-' + this.id);
    },

    /* Attempts to substitute all placeholder elements with the real components. If a component is successfully injected
     * or it has been destroyed then it won't be attempted again. This method is repeatedly invoked by a polling
     * mechanism until no components remain, however relying on the polling is not advised. Instead it is preferable to
     * call this method directly as soon as the generated HTML is inserted into the DOM.
     */
    injectComponents: function() {
        var me = this,
            ids = me.ids,
            index = ids.length - 1,
            id,
            cmp,
            placeholderEl;

        // Iterate backwards because we remove some elements in the loop
        for ( ; index >= 0 ; --index) {
            id = ids[index];
            cmp = Ext.getCmp(id);
            placeholderEl = me.getPlaceholderEl(id);

            if (me.renderComponent(cmp, placeholderEl) || !cmp) {
                // Either we've successfully done the switch or the component has been destroyed
                Ext.Array.splice(ids, index, 1);

                if (placeholderEl) {
                    placeholderEl.remove();
                }
            }
        }

        if (ids.length) {
            // Some components have not been injected. Polling acts both to do deferred injection and as a form of GC
            me.doPolling(me.pollInterval * 1.5);
        }
    },

    // Override
    overwrite: function(el) {
        var dom,
            firstChild,
            ret;

        /* In IE setting the innerHTML will destroy the nodes for the previous content. If we try to reuse components it
         * will fail as their DOM nodes will have been torn apart. We can't defend against external updates to the DOM
         * but we can guard against the case where all updates come through this template.
         */
        if (Ext.isIE) {
            dom = Ext.getDom(el);

            while (firstChild = dom.firstChild) {
                dom.removeChild(firstChild);
            }
        }

        ret = this.callParent(arguments);

        // There's no guarantee this will succeed so we still need polling as well
        this.injectComponents();

        return ret;
    },

    renderComponent: function(cmp, placeholderEl) {
        if (cmp && placeholderEl) {
            var parent = placeholderEl.parent();

            if (cmp.rendered) {
                // Move a component that has been rendered previously
                cmp.getEl().replace(placeholderEl);
            }
            else {
                cmp.render(parent, placeholderEl);
            }

            if (Ext.isIE6) {
                // Some components (mostly form fields) reserve space but fail to show up without a repaint in IE6
                parent.repaint();
            }

            return true;
        }

        return false;
    },

    reset: function() {
        var me = this;

        // The ids of injected components that haven't yet been rendered
        me.ids = [];

        if (me.pollId) {
            clearTimeout(me.pollId);
            me.pollId = null;
        }
    }
}, function(ctemplate) {
    var apply = function() {
        var me = this,
            args = Ext.Array.slice(arguments);

        args[0] = me.copyValues(args[0]);

        // As we're returning an HTML string/array we can't actually complete the injection here
        me.doPolling(10);

        return me.callParent(args);
    };

    // The main override is different depending on whether we're using ExtJS 4.0 or 4.1+
    if (ctemplate.prototype.applyOut) {
        // 4.1+
        ctemplate.override({
            applyOut: apply
        });
    }
    else {
        // 4.0
        ctemplate.override({
            applyTemplate: apply
        });

        ctemplate.createAlias('apply', 'applyTemplate');
    }
});/*!
 * Component Column
 * Version 1.1
 * Copyright(c) 2011-2013 Skirtle's Den
 * License: http://skirtlesden.com/ux/component-column
 */
Ext.define('Skirtle.grid.column.Component', {
    alias: 'widget.componentcolumn',
    extend: 'Ext.grid.column.Column',
    requires: ['Skirtle.CTemplate'],

    // Whether or not to automatically resize the components when the column resizes
    autoWidthComponents: true,

    // Whether or not to destroy components when they are removed from the DOM
    componentGC: true,

    // Override the superclass - this must always be true or odd things happen, especially in IE
    hasCustomRenderer: true,

    // The estimated size of the cell frame. This is updated once there is a cell where it can be measured
    lastFrameWidth: 12,

    /* Defer durations for updating the component width when a column resizes. Required when a component has an animated
     * resize that causes the scrollbar to appear/disappear. Otherwise the animated component can end up the wrong size.
     *
     * For ExtJS 4.0 both delays are required. For 4.1 just having the 10ms delay seems to be sufficient.
     */
    widthUpdateDelay: [10, 400],

    constructor: function(cfg) {
        var me = this;

        me.callParent(arguments);

        // Array of component ids for both component queries and GC
        me.compIds = [];

        // We need a dataIndex, even if it doesn't correspond to a real field
        me.dataIndex = me.dataIndex || Ext.id(null, 'cc-dataIndex-');

        me.tpl = me.createTemplate(me.tpl);
        me.renderer = me.createRenderer(me.renderer);

        me.registerColumnListeners();
    },

    addRefOwner: function(child) {
        var me = this,
            fn = me.refOwnerFn || (me.refOwnerFn = function() {
                return me;
            });

        if (me.extVersion < 40200) {
            // Component queries for ancestors use getBubbleTarget in 4.1 ...
            child.getBubbleTarget = fn;
        }
        else {
            // ... and getRefOwner in 4.2+
            child.getRefOwner = fn;
        }
    },

    applyTemplate: function(data, value) {
        if (Ext.isDefined(value)) {
            data[this.dataIndex] = value;
        }

        return this.tpl.apply(data);
    },

    /* In IE setting the innerHTML will destroy the nodes for the previous content. If we try to reuse components it
     * will fail as their DOM nodes will have been torn apart. To defend against this we must remove the components
     * from the DOM just before the grid view is refreshed.
     */
    beforeViewRefresh: function() {
        if (Ext.isIE) {
            var ids = this.compIds,
                index = 0,
                len = ids.length,
                item,
                el,
                parentEl;

            for ( ; index < len ; index++) {
                if ((item = Ext.getCmp(ids[index])) && (el = item.getEl()) && (el = el.dom) && (parentEl = el.parentNode)) {
                    parentEl.removeChild(el);
                }
            }
        }
    },

    calculateFrameWidth: function(component) {
        var el = component.getEl(),
            parentDiv = el && el.parent(),
            // By default the TD has no padding but it is quite common to add some via a tdCls
            parentTd = parentDiv && parentDiv.parent();

        if (parentTd) {
            // Cache the frame width so that it can be used as a 'best guess' in cases where we don't have the elements
            return this.lastFrameWidth = parentDiv.getFrameWidth('lr') + parentTd.getFrameWidth('lr');
        }
    },

    createRenderer: function(renderer) {
        var me = this;

        return function(value, p, record) {
            var data = Ext.apply({}, record.data, record.getAssociatedData());

            if (renderer) {
                // Scope must be this, not me
                value = renderer.apply(this, arguments);
            }

            // Process the value even with no renderer defined as the record may contain a component config
            value = me.processValue(value);

            return me.applyTemplate(data, value);
        };
    },

    createTemplate: function(tpl) {
        return tpl && tpl.isTemplate
            ? tpl
            : Ext.create('Skirtle.CTemplate', tpl || ['{', this.dataIndex ,'}']);
    },

    destroyChild: function(child) {
        child.destroy();
    },

    getRefItems: function(deep) {
        var items = this.callParent([deep]),
            ids = this.compIds,
            index = 0,
            len = ids.length,
            item;

        for ( ; index < len ; index++) {
            if (item = Ext.getCmp(ids[index])) {
                items.push(item);

                if (deep && item.getRefItems) {
                    items.push.apply(items, item.getRefItems(true));
                }
            }
        }

        return items;
    },

    onChildAfterRender: function(child) {
        this.resizeChild(child);
    },

    onChildBoxReady: function(child) {
        // Pass false to avoid triggering deferred resize, the afterrender listener will already cover those cases
        this.resizeChild(child, false);
    },

    onChildDestroy: function(child) {
        Ext.Array.remove(this.compIds, child.getId());
    },

    onChildResize: function() {
        this.redoScrollbars();
    },

    onColumnResize: function(column) {
        column.resizeAll();
    },

    onColumnShow: function(column) {
        column.resizeAll();
    },

    // This is called in IE 6/7 as the components can still be seen even when a column is hidden
    onColumnVisibilityChange: function(column) {
        var items = column.getRefItems(),
            index = 0,
            length = items.length,
            visible = !column.isHidden();

        // In practice this probably won't help but it shouldn't hurt either
        Ext.suspendLayouts && Ext.suspendLayouts();

        for ( ; index < length ; ++index) {
            items[index].setVisible(visible);
        }

        Ext.resumeLayouts && Ext.resumeLayouts(true);
    },

    onDestroy: function() {
        Ext.destroy(this.getRefItems());

        this.callParent();
    },

    // Override
    onRender: function() {
        this.registerViewListeners();
        this.callParent(arguments);
    },

    // View has changed, may be a full refresh or just a single row
    onViewChange: function() {
        var me = this,
            tpl = me.tpl;

        // Batch the resizing of child components until after they've all been injected
        me.suspendResizing();

        if (tpl.isCTemplate) {
            // No need to wait for the polling, the sooner we inject the less painful it is
            tpl.injectComponents();

            // If the template picked up other components in the data we can just ignore them, they're not for us
            tpl.reset();
        }

        // A view change could mean scrollbar problems. Note this won't actually do anything till we call resumeResizing
        me.redoScrollbars();

        me.resumeResizing();
        
        me.performGC();
    },

    // Component GC, try to stop components leaking
    performGC: function() {
        var compIds = this.compIds,
            index = compIds.length - 1,
            comp,
            el;

        for ( ; index >= 0 ; --index) {
            // Could just assume that the component id is the el id but that seems risky
            comp = Ext.getCmp(compIds[index]);
            el = comp && comp.getEl();

            if (!el || (this.componentGC && (!el.dom || Ext.getDom(Ext.id(el)) !== el.dom))) {
                // The component is no longer in the DOM
                if (comp && !comp.isDestroyed) {
                    comp.destroy();
                }
            }
        }
    },

    processValue: function(value) {
        var me = this,
            compIds = me.compIds,
            id, initialWidth, dom, parent;

        if (Ext.isObject(value) && !value.isComponent && value.xtype) {
            // Do not default to a panel, not only would it be an odd default but it makes future enhancements trickier
            value = Ext.widget(value.xtype, value);
        }

        if (value && value.isComponent) {
            id = value.getId();

            // When the view is refreshed the renderer could return a component that's already in the list
            if (!Ext.Array.contains(compIds, id)) {
                compIds.push(id);
            }

            me.addRefOwner(value);
            me.registerListeners(value);

            if (value.rendered) {
                /* This is only necessary in IE because it is just another manifestation of the innerHTML problems.
                 * The problem occurs when a record value is changed and the components in that same row are being
                 * reused. The view doesn't go through a full refresh, instead it performs a quick update on just the
                 * one row. Unfortunately this nukes the existing components so we need to remove them first.
                 */
                if (Ext.isIE) {
                    // TODO: Should this be promoted to CTemplate?
                    dom = value.el.dom;
                    parent = dom.parentNode;

                    if (parent) {
                        if (me.extVersion === 40101) {
                            // Workaround for the bugs in Element.syncContent - p tag matches CTemplate.cTpl
                            Ext.core.DomHelper.insertBefore(dom, {tag: 'p'});
                        }

                        // TODO: Removing the element like this could fall foul of Element GC
                        parent.removeChild(dom);
                    }
                }
            }
            else if (me.autoWidthComponents) {
                /* Set the width to a 'best guess' before the component is rendered to ensure that the component's
                 * layout is using a configured width and not natural width. This avoids problems with 4.1.1 where
                 * subsequent calls to setWidth are ignored because it believes the width is already correct but only
                 * the outermost element is actually sized correctly. We could use an arbitrary width but instead we
                 * make a reasonable guess at what the actual width will be to try to avoid extra resizing.
                 */
                initialWidth = me.getWidth() - me.lastFrameWidth;

                // Impose a minimum width of 4, we really don't want negatives values or NaN slipping through
                initialWidth = initialWidth > 4 ? initialWidth : 4;

                value.setWidth(initialWidth);
            }

            // Part of the same IE 6/7 hack as onColumnVisibilityChange
            if ((Ext.isIE6 || Ext.isIE7) && me.isHidden()) {
                value.hide();
            }
        }

        return value;
    },

    redoScrollbars: function() {
        var me = this,
            grid = me.up('tablepanel');

        if (grid) {
            // The presence of a resizeQueue signifies that we are currently suspended
            if (me.resizeQueue) {
                me.redoScrollbarsRequired = true;
                return;
            }

            // After components are injected the need for a grid scrollbar may need redetermining
            if (me.extVersion < 40100) {
                // 4.0
                grid.invalidateScroller();
                grid.determineScrollbars();
            }
            else {
                // 4.1+
                grid.doLayout();
            }
        }
    },

    registerColumnListeners: function() {
        var me = this;

        if (me.autoWidthComponents) {
            // Need to resize children when the column resizes
            me.on('resize', me.onColumnResize);

            // Need to resize children when the column is shown as they can't be resized correctly while it is hidden
            me.on('show', me.onColumnShow);
        }

        if (Ext.isIE6 || Ext.isIE7) {
            me.on({
                hide: me.onColumnVisibilityChange,
                show: me.onColumnVisibilityChange
            });
        }
    },

    registerListeners: function(component) {
        var me = this;

        // Remove the component from the child list when it is destroyed
        component.on('destroy', me.onChildDestroy, me);

        if (me.autoWidthComponents) {
            // Need to resize children after render as some components (e.g. comboboxes) get it wrong otherwise
            component.on('afterrender', me.onChildAfterRender, me, {single: true});

            // With 4.1 boxready gives more reliable results than afterrender as it occurs after the initial sizing
            if (!me.extVersion < 40100) {
                component.on('boxready', me.onChildBoxReady, me, {single: true});
            }
        }

        // Need to redo scrollbars when a child resizes
        component.on('resize', me.onChildResize, me);
    },

    registerViewListeners: function() {
        var me = this,
            view = me.up('tablepanel').getView();

        me.mon(view, 'beforerefresh', me.beforeViewRefresh, me);
        me.mon(view, 'refresh', me.onViewChange, me);
        me.mon(view, 'itemupdate', me.onViewChange, me);
        me.mon(view, 'itemadd', me.onViewChange, me);
        me.mon(view, 'itemremove', me.onViewChange, me);
    },

    resizeAll: function() {
        var me = this;

        me.suspendResizing();
        me.resizeQueue = me.getRefItems();
        me.resumeResizing();
    },

    resizeChild: function(component, defer) {
        var me = this,
            frameWidth,
            newWidth,
            oldWidth,
            resizeQueue;

        if (me.resizingSuspended) {
            resizeQueue = me.resizeQueue;

            if (!Ext.Array.contains(resizeQueue, component)) {
                resizeQueue.push(component);
            }

            return;
        }

        frameWidth = me.calculateFrameWidth(component);

        // TODO: Should we destroy the component here if it doesn't have a parent element? Already picked up anyway?
        if (Ext.isNumber(frameWidth)) {
            newWidth = me.getWidth() - frameWidth;
            oldWidth = component.getWidth();

            // Returns true if a resize actually happened
            if (me.setChildWidth(component, newWidth, oldWidth)) {
                // Avoid an infinite resizing loop, deferring will only happen once
                if (defer !== false) {
                    // Do the sizing again after a delay. This is because child panel collapse animations undo our sizing
                    Ext.each(me.widthUpdateDelay, function(delay) {
                        Ext.defer(me.resizeChild, delay, me, [component, false]);
                    });
                }
            }
        }
    },

    resumeResizing: function() {
        var me = this,
            index = 0,
            resizeQueue = me.resizeQueue,
            len = resizeQueue.length;

        if (!--me.resizingSuspended) {
            for ( ; index < len ; ++index) {
                me.resizeChild(resizeQueue[index]);
            }

            me.resizeQueue = null;

            if (me.redoScrollbarsRequired) {
                me.redoScrollbars();
            }
        }
    },

    setChildWidth: function(component, newWidth, oldWidth) {
        if (oldWidth === newWidth) {
            return false;
        }

        component.setWidth(newWidth);

        return true;
    },

    suspendResizing: function() {
        var me = this;

        me.resizingSuspended = (me.resizingSuspended || 0) + 1;

        if (!me.resizeQueue) {
            me.resizeQueue = [];
        }
    }
}, function(cls) {
    var proto = cls.prototype,
        version = Ext.getVersion();

    // ExtJS version detection
    proto.extVersion = (version.getMajor() * 100 + version.getMinor()) * 100 + version.getPatch();

    // 4.1.1 initially reported its version as 4.1.0
    if (Ext.Element.prototype.syncContent && version.toString() === '4.1.0') {
        proto.extVersion = 40101;
    }
});Ext.namespace("TolomeoExt.Styler.Renderer");

Ext.define("TolomeoExt.Styler.Renderer.DefaultSymbolizer", {
	singleton: true,
	fillColor: "#000000",
	fillOpacity: 1,
	fontColor: "#000000",
	fontSize: 10,
	haloColor: "#FFFFFF",
	haloOpacity: 1,
	haloRadius: 1,
	pointRotation: 0,
	strokeColor: "#000000",
	strokeDashstyle: "solid",
	strokeOpacity: 1,
	strokeWidth: 2
});
Ext.namespace("TolomeoExt.Styler.slider");

Ext.define("TolomeoExt.Styler.slider.Tip", {
	extend: "Ext.slider.Tip",
	hover: true,
	dragging: false,
	init: function(slider) {
		if (this.hover) {
			slider.on("render", this.registerThumbListeners, this);
		}
		this.slider = slider;
		TolomeoExt.Styler.slider.Tip.superclass.init.apply(this, arguments);
	},
	registerThumbListeners: function() {
		for (var i = 0, len = this.slider.thumbs.length; i < len; ++i) {
			this.slider.thumbs[i].el.on({
				"mouseover": this.createHoverListener(i),
				"mouseout": function() {
					if (!this.dragging) {
						this.hide.apply(this, arguments);
					}
				},
				scope: this
			});
		}
	},
	createHoverListener: function(index) {
		return Ext.Function.pass(function() {
			this.onSlide(this.slider, {}, this.slider.thumbs[index]);
			this.dragging = false;
		}, [], this);
	},
	onSlide: function(slider, e, thumb) {
		this.dragging = true;
		TolomeoExt.Styler.slider.Tip.superclass.onSlide.apply(this, arguments);
	}
});
Ext.namespace("TolomeoExt.Styler.form");

Ext.define("TolomeoExt.Styler.form.ColorField", {
	extend: "Ext.form.TextField",
	alias: "widget.styler_colorfield",
	cssColors: {
		aqua: "#00FFFF",
		black: "#000000",
		blue: "#0000FF",
		fuchsia: "#FF00FF",
		gray: "#808080",
		green: "#008000",
		lime: "#00FF00",
		maroon: "#800000",
		navy: "#000080",
		olive: "#808000",
		purple: "#800080",
		red: "#FF0000",
		silver: "#C0C0C0",
		teal: "#008080",
		white: "#FFFFFF",
		yellow: "#FFFF00"
	},
	defaultBackground: "#ffffff",
	colorPicker: null,
	colorPickerDialog: null,
	enableKeyEvents: true,
	initComponent: function() {
		this.value = this.value.trim();
		this.colorPicker = Ext.create("Ext.picker.Color", {
			listeners: {
				select: function(picker, selColor) {
					this.setValue("#" + selColor);
					this.colorField();
				},
				scope: this
			}
		});
		this.colorPickerDialog = Ext.create("Ext.Window", {
			title: "Cattura Colore",
			closeAction: "hide",
			items: [
				this.colorPicker
			]
		});
		if (this.value) {
			this.value = this.hexToColor(this.value);
		}
		TolomeoExt.Styler.form.ColorField.superclass.initComponent.call(this);
		this.on({
			render: this.colorField,
			change: this.colorField,
			focus: this.fieldFocus,
			scope: this
		});
	},
	isDark: function(hex) {
		var dark = false;
		if (hex) {
			var r = parseInt(hex.substring(1, 3), 16) / 255;
			var g = parseInt(hex.substring(3, 5), 16) / 255;
			var b = parseInt(hex.substring(5, 7), 16) / 255;
			var brightness = (r * 0.299) + (g * 0.587) + (b * 0.144);
			dark = brightness < 0.5;
		}
		return dark;
	},
	colorField: function() {
		var color = this.colorToHex(this.getValue()) || this.defaultBackground;
		this.inputEl.setStyle({
			"background": color,
			"color": this.isDark(color) ? "#ffffff" : "#000000"
		});
	},
	getHexValue: function() {
		return this.colorToHex(TolomeoExt.Styler.form.ColorField.superclass.getValue.apply(this, arguments));
	},
	getValue: function() {
		var v = this.getHexValue();
		var o = this.initialConfig.value;
		if (v === this.hexToColor(o)) {
			v = o;
		}
		return v;
	},
	setValue: function(value) {
		TolomeoExt.Styler.form.ColorField.superclass.setValue.apply(this, [this.hexToColor(value)]);
	},
	colorToHex: function(color) {
		if (!color) {
			return color;
		}
		var hex;
		if (color.match(/^#[0-9a-f]{6}$/i)) {
			hex = color;
		} else {
			hex = this.cssColors[color.toLowerCase()] || null;
		}
		return hex;
	},
	hexToColor: function(hex) {
		if (!hex) {
			return hex;
		}
		var color = hex;
		for (var c in this.cssColors) {
			if (this.cssColors[c] == color.toUpperCase()) {
				color = c;
				break;
			}
		}
		return color;
	},
	fieldFocus: function(field) {
		if (this.colorPicker.colors.indexOf(this.getValue().substring(1)) == -1) {
			this.colorPicker.colors.push(this.getValue().substring(1));
		}
		this.colorPicker.select(this.getValue(), true);
		var color = this.colorToHex(this.getValue()) || this.defaultBackground;
		this.colorPickerDialog.show();
	}
});
Ext.namespace("TolomeoExt.Styler.form");

Ext.define("TolomeoExt.Styler.form.FontComboBox", {
	extend: "Ext.form.ComboBox",
	alias: "widget.styler_fontcombo",
	fonts: ["Serif", "SansSerif", "Arial", "Courier New", "Tahoma", "Times New Roman", "Verdana"],
	defaultFont: "Serif",
	allowBlank: false,
	mode: "local",
	triggerAction: "all",
	editable: false,
	initComponent: function() {
		var fonts = this.fonts || TolomeoExt.Styler.form.FontComboBox.prototype.fonts;
		var defaultFont = this.defaultFont;
		if (fonts.indexOf(this.defaultFont) === -1) {
			defaultFont = fonts[0];
		}
		var defConfig = {
			displayField: "field1",
			valueField: "field1",
			store: fonts,
			value: defaultFont,
			tpl: new Ext.XTemplate('<tpl for=".">' + '<div class="x-combo-list-item">' + '<span style="font-family: {field1};">{field1}</span>' + '</div></tpl>')
		};
		Ext.applyIf(this, defConfig);
		TolomeoExt.Styler.form.FontComboBox.superclass.initComponent.call(this);
	}
});
Ext.namespace("TolomeoExt.Styler.form");

Ext.define("TolomeoExt.Styler.form.ComparisonComboBox", {
	extend: "Ext.form.ComboBox",
	alias: "widget.styler_comparisoncombo",
	allowedTypes: [
		{value: OpenLayers.Filter.Comparison.EQUAL_TO, name: "="},
		{value: OpenLayers.Filter.Comparison.NOT_EQUAL_TO, name: "<>"},
		{value: OpenLayers.Filter.Comparison.LESS_THAN, name: "<"},
		{value: OpenLayers.Filter.Comparison.GREATER_THAN, name: ">"},
		{value: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO, name: "<="},
		{value: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO, name: ">="},
		{value: OpenLayers.Filter.Comparison.LIKE, name: "like"}
	],
	allowBlank: false,
	queryMode: "local",
	triggerAction: "all",
	width: 50,
	editable: false,
	initComponent: function() {
		var defConfig = {
			displayField: "name",
			valueField: "value",
			store: Ext.create("Ext.data.JsonStore", {
				data: this.allowedTypes,
				fields: ["value", "name"]
			}),
			value: (this.value === undefined) ? this.allowedTypes[0]["value"] : this.value
		};
		Ext.apply(this, defConfig);
		TolomeoExt.Styler.form.ComparisonComboBox.superclass.initComponent.call(this);
	}
});
Ext.namespace("TolomeoExt.Styler.form");

Ext.define("TolomeoExt.Styler.form.FilterField", {
	extend: "Ext.form.FieldContainer",
	alias: "widget.styler_filterfield",
	filter: null,
	attributes: null,
	attributesComboConfig: null,
	initComponent: function() {
		if (!this.filter) {
			this.filter = this.createDefaultFilter();
		}
		if (!this.attributes) {
			this.attributes = new GeoExt.data.AttributeStore();
		}
		var defAttributesComboConfig = {
			xtype: "combo",
			store: this.attributes,
			editable: false,
			triggerAction: "all",
			allowBlank: false,
			displayField: "name",
			valueField: "name",
			value: this.filter.property,
			queryMode: "local",
			listeners: {
				select: function(combo, record) {
					this.filter.property = record[0].get("name");
					this.fireEvent("change", this.filter);
				},
				scope: this
			},
			width: 120
		};
		this.attributesComboConfig = this.attributesComboConfig || {};
		Ext.applyIf(this.attributesComboConfig, defAttributesComboConfig);
		this.items = this.createFilterItems();
		this.addEvents("change");
		TolomeoExt.Styler.form.FilterField.superclass.initComponent.call(this);
	},
	createDefaultFilter: function() {
		return new OpenLayers.Filter.Comparison();
	},
	createFilterItems: function() {
		return [this.attributesComboConfig, {
			xtype: "styler_comparisoncombo",
			value: this.filter.type,
			listeners: {
				select: function(combo, record) {
					this.filter.type = record[0].get("value");
					this.fireEvent("change", this.filter);
				},
				scope: this
			}
		}, {
			xtype: "textfield",
			value: this.filter.value,
			width: 50,
			grow: true,
			growMin: 50,
			anchor: "100%",
			allowBlank: false,
			listeners: {
				change: function(el, value) {
					this.filter.value = value;
					this.fireEvent("change", this.filter);
				},
				scope: this
			}
		}];
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.FeatureRenderer", {
	extend: Ext.form.FieldContainer,
	//extend: Ext.container.Container,
	alias: "widget.styler_featurerenderer",
	feature: void 0,
	symbolizers: [OpenLayers.Feature.Vector.style["default"]],
	symbolType: "Polygon",
	resolution: 1,
	minWidth: 20,
	minHeight: 20,
	renderers: ["SVG", "VML", "Canvas"],
	rendererOptions: null,
	pointFeature: undefined,
	lineFeature: undefined,
	polygonFeature: undefined,
	textFeature: undefined,
	renderer: null,
	initComponent: function() {
		this.autoEl = {
			tag: "div",
			"class": this.imgCls ? this.imgCls : "",
			id: this.getId()
		};
		this.callParent(arguments);
		Ext.applyIf(this, {
			pointFeature: new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 0)),
			lineFeature: new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([new OpenLayers.Geometry.Point(-8, -3), new OpenLayers.Geometry.Point(-3, 3), new OpenLayers.Geometry.Point(3, -3), new OpenLayers.Geometry.Point(8, 3)])),
			polygonFeature: new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(-8, -4), new OpenLayers.Geometry.Point(-6, -6), new OpenLayers.Geometry.Point(6, -6), new OpenLayers.Geometry.Point(8, -4), new OpenLayers.Geometry.Point(8, 4), new OpenLayers.Geometry.Point(6, 6), new OpenLayers.Geometry.Point(-6, 6), new OpenLayers.Geometry.Point(-8, 4)])])),
			textFeature: new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 0))
		});
		this.feature || this.setFeature(null, {
			draw: false
		});
		this.addEvents("click")
	},
	initCustomEvents: function() {
		this.clearCustomEvents();
		this.el.on("click", this.onClick, this)
	},
	clearCustomEvents: function() {
		this.el && this.el.removeAllListeners && this.el.removeAllListeners()
	},
	onClick: function() {
		this.fireEvent("click", this)
	},
	onRender: function(a, b) {
		this.el || (this.el = document.createElement("div"), this.el.id = this.getId());
		(!this.renderer || !this.renderer.supported()) && this.assignRenderer();
		this.renderer.map = {
			getResolution: Ext.Function.bind(function() {
				return this.resolution
			}, this)
		};
		this.callParent(arguments);
		this.drawFeature()
	},
	afterRender: function() {
		this.callParent(arguments);
		this.initCustomEvents()
	},
	onResize: function(a, b) {
		this.setRendererDimensions();
		this.callParent(arguments)
	},
	setRendererDimensions: function() {
		var a = this.feature.geometry.getBounds(),
			b = a.getWidth(),
			c = a.getHeight(),
			d = this.initialConfig.resolution;
		d || (d = Math.max(b / this.width || 0, c / this.height || 0) || 1);
		this.resolution = d;
		var b = Math.max(this.width || this.minWidth, b / d),
			c = Math.max(this.height || this.minHeight, c / d),
			a = a.getCenterPixel(),
			e = b * d / 2,
			d = c * d / 2,
			d = new OpenLayers.Bounds(a.x - e, a.y - d, a.x + e, a.y + d);
		this.renderer.setSize(new OpenLayers.Size(Math.round(b), Math.round(c)));
		this.renderer.setExtent(d, !0)
	},
	assignRenderer: function() {
		for (var a =
				0, b = this.renderers.length; a < b; ++a) {
			var c = OpenLayers.Renderer[this.renderers[a]];
			if (c && c.prototype.supported()) {
				this.renderer = new c(this.el, this.rendererOptions);
				break
			}
		}
	},
	setSymbolizers: function(a, b) {
		this.symbolizers = a;
		(!b || b.draw) && this.drawFeature()
	},
	setSymbolType: function(a, b) {
		this.symbolType = a;
		this.setFeature(null, b)
	},
	setFeature: function(a, b) {
		this.feature = a || this[this.symbolType.toLowerCase() + "Feature"];
		(!b || b.draw) && this.drawFeature()
	},
	drawFeature: function() {
		this.renderer.clear();
		this.setRendererDimensions();
		for (var a, b, c = 0, d = this.symbolizers.length; c < d; ++c) a = this.symbolizers[c], b = this.feature, a instanceof OpenLayers.Symbolizer ? (a = a.clone(), OpenLayers.Symbolizer.Text && (a instanceof OpenLayers.Symbolizer.Text && !1 === a.graphic) && (a.fill = a.stroke = !1), this.initialConfig.feature || (b = a.CLASS_NAME.split(".").pop().toLowerCase(), b = this[b + "Feature"])) : a = Ext.apply({}, a), this.renderer.drawFeature(b.clone(), a)
	},
	update: function(a) {
		a = a || {};
		a.feature ? this.setFeature(a.feature, {
			draw: !1
		}) : a.symbolType && this.setSymbolType(a.symbolType, {
			draw: !1
		});
		a.symbolizers && this.setSymbolizers(a.symbolizers, {
			draw: !1
		});
		this.drawFeature()
	},
	beforeDestroy: function() {
		this.clearCustomEvents();
		this.renderer && this.renderer.destroy()
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("Field", {
	extend: "Ext.data.Model",
	fields: [{
		name: "name",
		type: "string"
	}]
});

Ext.define("TolomeoExt.Styler.StylePanel", {
	extend: "Ext.Panel",
	alias: "widget.styler_stylepanel",
	statics:{
		styleToSld: function(style) {

		    	var sld = {
						namedLayers: {
							"Default Styler": {
								userStyles: [
									style
								],
								name: "Default Styler"
							}
						},
						version: "1.0.0"
					}
					
				var format = new OpenLayers.Format.SLD({
					multipleSymbolizers: true
				});
		    	
		    	return format.write(sld);
		    },
		    
	    sldToStyle: function(sld) {
	    		
			var format = new OpenLayers.Format.SLD({
				multipleSymbolizers: true
			});
			
			var style = format.read(sld).namedLayers["Default Styler"].userStyles[0];
	    	
	    	return style; 
	    }
	},
		
	tipoGenerazioneStile: 0,
	
	
	layout: 'border',
  	actualStyle: null,
  	styleRecord: null,
  	rulesPanel: null,
  	selectedRule: null,
  	selectedRuleIndex: null,
  	selectedRulePanel: null,
  	mapPanel: null,
  	
  	layerGeomType: null,
  	
  	
  	/** 
	 * Property: TOLOMEOServer
	 * {String}
	 */
	TOLOMEOServer: null,
	
	/** 
	 * Property: TOLOMEOStaticRoot
	 * {String}
	 */
	TOLOMEOStaticRoot: null,

	/** 
	 * Property: TOLOMEOContext
	 * {String}
	 */
	TOLOMEOContext: null,
  
	getSymbolTypeFromRule: function(rule) {
		var candidate, type;
		for (var i = 0, ii = rule.symbolizers.length; i < ii; ++i) {
			candidate = rule.symbolizers[i];
			if (!(candidate instanceof OpenLayers.Symbolizer.Text)) {
				type = candidate.CLASS_NAME.split(".").pop();
				break;
			}
		}
		return type;
	},
	
	initComponent: function() {

		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);
		var me = this;
		
		if (this.styleRecord==null) {
			this.styleRecord = Ext.create('modelloDatiStile', {});
		} else {
			this.actualStyle = TolomeoExt.Styler.StylePanel.sldToStyle(this.styleRecord.get('sldtext'));
		}
		
  		this.addEvents("stylecanceled", "stylesaved", "ruleselected", "ruleadded", "ruleremoved");
  		
  		this.nomeField = Ext.create('Ext.form.field.Text', {
  			fieldLabel: "Nome",
			value: (this.styleRecord) ? this.styleRecord.get('name') : "",
			width: 100
  		});
  		
  		this.titoloField = Ext.create('Ext.form.field.Text', {
  			fieldLabel: "Titolo",
			value: (this.styleRecord) ? this.styleRecord.get('title') : "",
			width: 100
  		});
  		/*
  		this.descrizioneField = Ext.create('Ext.form.field.Text', {
  			fieldLabel: "Descrizione",
			value: (this.styleRecord) ? this.styleRecord.get('styleAbstract') : "",
			width: 100
  		});*/ 
  		
  		var headPanel = Ext.create('Ext.Panel', {
  			region: 'north',
  			layout: 'form',
  			defaults: {
		  				style: {
							"padding": "0.3em 0 0 1em",
							"padding-left": "10px" 
						},
						labelWidth: 70
  					},
  			items: [this.nomeField, this.titoloField, this.descrizioneField]
  		});
  		
		var rulesStoreData = {
			items: []
		};
		if (this.actualStyle != null) {
			for (var ruleIndex = 0 ; ruleIndex < this.actualStyle.rules.length ; ruleIndex ++) {
				var rule = this.actualStyle.rules[ruleIndex];
				rulesStoreData.items[ruleIndex] = {
					name: rule.title,
					//symbol: 'Available',
						/*
						Ext.create("TolomeoExt.Styler.FeatureRenderer", {
						symbolType: this.getSymbolTypeFromRule(rule),
						symbolizers: rule.symbolizers
					})*/
					rule: rule
				};
			}
		}
		var rulesStore = Ext.create("Ext.data.Store", {
			storeId: "rulesStore",
			fields: ["name", "symbol", "rule"],
			data: rulesStoreData,
			autoSync: true,
			proxy: {
				type: "memory",
				reader: {
				  type: "json",
				  root: "items"
				}
			}
		});
		rulesStore.on('add', 
				function(store, records, index, eOpts ) {
					this.rulesPanel.getSelectionModel().select(index);
				}, this);
		
		this.rulesPanel = Ext.create("Ext.grid.Panel", {
			title: "Regole",
			region: 'west',
			split: true,
			store: Ext.data.StoreManager.lookup("rulesStore"),
			columns: [
				{
					text: "Nome",
					dataIndex: "name",
					width: 99
				}
				// Commentato in attesa di risolvere problema
				// FeatureRenderer estende Ext.form.FieldContainer che ha elemento bodyEl 
				// che viene valorizzato solo dopo il rendering, ma questo sembra andare in conflitto con 
				// l'uso in questo contesto
				/*,
				{
					text: "Simbolo",
					dataIndex: "rule",
					xtype: 'componentcolumn', 
		            renderer: function(rule, meta, record) {
		            	return  {
		            		xtype: 'styler_featurerenderer',
		            		symbolType: me.getSymbolTypeFromRule(rule),
							symbolizers: rule.symbolizers//,
							//width: 10,
							//height: 10
		            	};
		            },
					width: 50
				}*/
			],
			height: 200,
			width: 200,
			bbar: [{
					iconCls: "add",
					cls: 'clearCSS',
				    text: 'Aggiungi',
				    menu: {
				    	cls: 'clearCSS',
				    	items: [{
				    		text: 'Puntuale',
				    		menu: {
				    			cls: 'clearCSS',
				    			items: [{
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.SEMPLICE.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.SEMPLICE, 1);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.NATURAL_BREAK.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.NATURAL_BREAK, 1);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_COUNT.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_COUNT, 1);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_RANGE.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_RANGE, 1);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.VALORI_UNIVOCI.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.VALORI_UNIVOCI, 1);
				    				}
				    			}]
				    	
				    		}
				    	},{
				    		text: 'Lineare',
	        				menu: {
				    			cls: 'clearCSS',
				    			items: [{
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.SEMPLICE.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.SEMPLICE, 2);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.NATURAL_BREAK.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.NATURAL_BREAK, 2);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_COUNT.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_COUNT, 2);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_RANGE.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_RANGE, 2);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.VALORI_UNIVOCI.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.VALORI_UNIVOCI, 2);
				    				}
				    			}]
	        				}
		        		},{
		        			text: 'Poligonale',
		        			menu: {
				    			cls: 'clearCSS',
				    			items: [{
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.SEMPLICE.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.SEMPLICE, 3);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.NATURAL_BREAK.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.NATURAL_BREAK, 3);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_COUNT.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_COUNT, 3);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_RANGE.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.EQUAL_RANGE, 3);
				    				}
				    			}, {
				    				text: TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.VALORI_UNIVOCI.nome,
				    				scope: this,
				    				handler: function() {
				    					this.aggiungiRegolaHandler(TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.VALORI_UNIVOCI, 3);
				    				}
				    			}]
		        			}
		        		}]
					    
						} 
					},{
					text: "Rimuovi",
					iconCls: "delete",
					disabled: true,
					handler: function() {
						Ext.Msg.show({
							title: "Rimuovi Regola",
							msg: "Sei sicuro di voler rimuovere la regola \"" + this.selectedRule.title + "\"?",
							width: 300,
							buttons: Ext.Msg.YESNO,
							fn: function(buttonId, text) {
								if (buttonId == "yes") {
									Ext.data.StoreManager.lookup("rulesStore").removeAt(this.selectedRuleIndex);
									this.getDeleteRuleButton().disable();
									this.selectedRulePanel.removeAll();
									this.selectedRulePanel.setTitle("Regola");
									var rules = this.actualStyle.rules.splice(this.selectedRuleIndex, 1);
									this.fireEvent("ruleremoved", rules[0]);
								}
							},
							icon: Ext.Msg.QUESTION,
							scope: this
						});
					},
					scope: this
				}
			]
		});
		this.rulesPanel.on({
			select: function(rulesPanel, record, index) {
				this.selectedRule = record.get("rule");
				this.selectedRuleIndex = index;
				this.selectedRuleRecord = record;
				this.getDeleteRuleButton().enable();
				this.showRule(this.selectedRule);
				this.fireEvent("ruleselected", this.selectedRule);
			},
			scope: this
		});
		
		// Pannello con la definizione della regola (colori, spessori etc.)
		// Inizialmente � vuoto perch� ci viene caricato dentro in funzione del tipo di regola
		this.selectedRulePanel = Ext.create("Ext.Panel", {
			title: "Regola",
			region: 'center',
			autoScroll: true,
			width: 300
		});
		
		this.items = [
		    headPanel,
			this.rulesPanel,
			this.selectedRulePanel
		];
		
		this.btnSalva = Ext.create("Ext.button.Button",{
				text: "Salva",
				iconCls: "save",
				disabled: this.checkSaveDisabled(),
				handler: function() {
					
					this.styleRecord.set('name', this.nomeField.getValue());
			  		this.styleRecord.set('title', this.titoloField.getValue());
			  		//this.styleRecord.set('styleAbstract', this.descrizioneField.getValue());
			  		
			  	   
			    	//style['abstract'] = 'abstarct';
			  		this.actualStyle.title = this.titoloField.getValue();
			    	this.actualStyle.name = this.nomeField.getValue();
			  		
			  		this.styleRecord.set('sldtext',TolomeoExt.Styler.StylePanel.styleToSld(this.actualStyle));					
					this.fireEvent("stylesaved", this.styleRecord);
				},
				scope: this
			});
		
		this.bbar = [
			"->",
			{
				text: "Annulla",
				iconCls: "cancel",
				handler: function() {
					this.fireEvent("stylecanceled");
				},
				scope: this
			}, this.btnSalva
			
		];
		
		this.on("ruleadded", function() {
			this.btnSalva.setDisabled(false); 
		}, this);
		
		this.on("ruleremoved", function() {
			this.btnSalva.setDisabled(this.checkSaveDisabled()); 
		}, this);
		
		this.callParent();
	},
	
	
	aggiungiRegolaHandler: function(tipogenerazione, tipogeometria) {
		
		var s = null;
		switch (tipogeometria){
		case 1:
			s = new OpenLayers.Symbolizer.Point({
					pointRadius: 3,
					fillColor: '#FF0000',
					graphicName: 'circle'
				});
			break;
		case 2:
			s = new OpenLayers.Symbolizer.Line({
					strokeColor: '#FF0000'
				});
			break;
		case 3:
			s = new OpenLayers.Symbolizer.Polygon({
					strokeColor: '#FF0000',
					fillColor: '#FF0000'
				});
			break;
		}
		var rule = new OpenLayers.Rule({
			title: "Nuova regola",
			symbolizers: [s]
		});
		
		if (tipogenerazione==TolomeoExt.Styler.RuleGeneratorPanel.TIPOGENERAZIONE.SEMPLICE.codice) {
			this.addRule(s);
		} else {
			this.mostraFinestraGenerazioneStile(tipogenerazione, rule, null, null);
			
		}
		
		//layer
		
		
		
		
	},
	
	checkSaveDisabled: function() {
		return (this.actualStyle == null) || this.actualStyle.rules.length==0;
	
	},
	
	addRule: function(s) {
		
		var rule = new OpenLayers.Rule({
			title: "Nuova regola",
			symbolizers: [s]
		});
		if (this.actualStyle == null) {
			this.actualStyle = new OpenLayers.Style();
		}
		this.actualStyle.rules.push(rule);
		Ext.data.StoreManager.lookup("rulesStore").add({
			name: rule.title/*,
			symbol: Ext.create("TolomeoExt.Styler.FeatureRenderer", {
				symbolType: this.getSymbolTypeFromRule(rule),
				symbolizers: rule.symbolizers
			})*/,
			rule: rule
		});
		
		//this.showRule(rule);
		this.fireEvent("ruleadded");
		
	},
	
	showRule: function(rule) {
		this.selectedRulePanel.removeAll();
		this.selectedRulePanel.setTitle("Regola \"" + rule.title + "\"");
		var rulePanel = Ext.create("TolomeoExt.Styler.RulePanel", {
			autoHeight: false,
			autoScroll: true,
			rule: rule,
			nestedFilters: false,
			scaleLevels: (this.mapPanel) ? this.mapPanel.map.baseLayer.numZoomLevels : undefined,
			minScaleDenominatorLimit: (this.mapPanel) ? OpenLayers.Util.getScaleFromResolution(this.mapPanel.map.baseLayer.resolutions[this.mapPanel.map.baseLayer.numZoomLevels - 1], this.mapPanel.map.units) : undefined,
			maxScaleDenominatorLimit: (this.mapPanel) ? OpenLayers.Util.getScaleFromResolution(this.mapPanel.map.baseLayer.resolutions[0], this.mapPanel.map.units) : undefined,
			scaleSliderTemplate: "<div>{scaleType} Zoom Level: {zoom}</div>" + "<div>Current Map Zoom: {mapZoom}</div>",
			modifyScaleTipContext: Ext.Function.pass(function(panel, data) {
				if (this.mapPanel) {
					data.mapZoom = this.mapPanel.map.getZoom();
				}
			}, [], this),
			// TODO Recuperare campi effettivi
			attributes: Ext.create("Ext.data.Store", {
				model: "Field",
				data : [{
						name: "pippo"
					},
					{
						name: "pluto"
					},
					{
						name: "paperino"
					},
					{
						name: "cat"
					},
					{
						name: "str1"
					}
				]
			})
		});
		rulePanel.on('change', 
				function(rulePanel, rule) {
								if (this.selectedRuleRecord) {
									this.selectedRuleRecord.set('name', rule.title)
								}
							}, this);
		this.selectedRulePanel.add(rulePanel);
	},
	
	getAddRuleButton: function() {
		return this.rulesPanel.getDockedItems()[2].getComponent(0);
	},
	
	getDeleteRuleButton: function() {
		return this.rulesPanel.getDockedItems()[2].getComponent(1);
	},
	
	// Mostra la finestra per la creazione di una nuova stilizzazione non semplice
	// tipoCreazione - indica il tipo di creazione da eseguire (NB, EC, ER ..)
	// regola - rule vuota o di default da valorizzare (utilizzata per le caretteristiche base comuni)
	// symbolType - tipo di simbolo del livello selezionato (punto, linea poligono)
	// closeCallback - funzione di callback per la chiusura
	mostraFinestraGenerazioneStile : function(tipoCreazione, regola, symbolType, closeCallback) {
		var nuovaRule = (regola) ? regola.clone() : null;

		this.finestraGenerazioneStileDlg = new Ext.Window(
		{
			title : tipoCreazione.titoloFinestra,
			layout : "fit",
			width : 420,
			constrain : true,
			items : [ Ext.create('TolomeoExt.Styler.RuleGeneratorPanel', {
						rule : nuovaRule,	
						tipoCreazione: tipoCreazione,
						layer: this.layer,
						// TODO Recuperare campi effettivi
						attributes: Ext.create("Ext.data.Store", {
							model: "Field",
							data : [{ name: "CIRC"	},
								{
									name: "pluto"
								},
								{
									name: "paperino"
								},
								{
									name: "cat"
								},
								{
									name: "str1"
								}
							]
						}),
						autoHeight : false,
						autoScroll : true,
						symbolType : symbolType,
						nestedFilters : false,
						//TODO ALE scaleLevels : this.map.baseLayer.numZoomLevels,
						//TODO ALE minScaleLimit : OpenLayers.Util.getScaleFromResolution(this.map.baseLayer.resolutions[this.map.baseLayer.numZoomLevels - 1], this.map.units),
						//TODO ALE maxScaleLimit : OpenLayers.Util.getScaleFromResolution(this.map.baseLayer.resolutions[0], this.map.units),
						//TODO ALE minScaleDenominatorLimit: this.valoreMinScale,
						//TODO ALE maxScaleDenominatorLimit: this.valoreMaxScale,
						scaleSliderTemplate : "<div>{zoomType} Livello Zoom: {zoom}</div>" + "<div>Zoom Mappa Corrente: {mapZoom}</div>",
						listeners : {
							cancelPressed : function() {
								this.finestraGenerazioneStileDlg.close();
								//legenda.unselect();
								//this.getLegend().unselect();
								//if (closeCallback) {
								//	closeCallback.call(this);
								//}
							},
							/*move : function(cp, x, y) {
								this.windowPositions["finestraGenerazioneStileDlg"] = {
									x : x,
									y : y
								};
							},*/
							scope : this
						}
						
						
						
						//,
						// TODO ALE
						/* modifyScaleTipContext : (function(panel, data) {
							data.mapZoom = this.map.getZoom();
						}).createDelegate(this),
						attributes : new GeoExt.data.AttributeStore({
							url: stylesManagerUrl + "?url=" + layer.url.split("?")[0].replace('/wms', '/wfs') + '?',
							baseParams : {
								action: 'proxy',
								version : "1.1.1",
								request : "DescribeFeatureType",
								typeName : layer.params["LAYERS"]
							},
						ignore : { name : this.schemaManager.getFieldTypeToIgnore(layer) }
						}),*/
					}) ],
					/*
			bbar : [
					"->",
					{
						text : "Annulla",
						iconCls : "cancel",
						handler : function() {
							this.finestraGenerazioneStileDlg.close();
						},
						scope : this
					}, {
						text : "Genera",
						iconCls : "save",
						scope : this,
						handler : function() {
							Ext.getBody().mask('Elaborazione in corso...');

							this.rule = this.finestraGenerazioneStileDlg.items.items[0].rule.clone();
																
							new TolomeoExt.ToloCrossAjax().request({
								url: this.TOLOMEOServer + this.TOLOMEOContext + "/AjaxGeneraStiliServlet",
								method: 'POST',
								params: {
									//url: this.urlWFS,
									tipoGenerazione: tipoStilizzazioneRichiesta,
									precisione: precisioneDecimale,
									classi : numeroClassiTematizzazione,
									campo : nomeCampoTematizzazione,
									codTPN: this.layer.codTPN,
									// TODO
									//codice_comune: codiceComuneParamJsLocal,
									codice_comune: ""
								},
								failure : generaStiliFailure,
								success : generaStiliSuccess,
								scope : this
							});
						}
					} ],*/
			listeners : {
				close : function() {
					//legenda.unselect();
					//this.getLegend().unselect();
					//if (closeCallback) {
					//	closeCallback.call(this);
					//}
				},
				/*move : function(cp, x, y) {
					this.windowPositions["finestraGenerazioneStileDlg"] = {
						x : x,
						y : y
					};
				},*/
				scope : this
			}
		});
		this.finestraGenerazioneStileDlg.show();
	},
	
	generaStiliSuccess: function (results, store){
		
	/*
		var ajaxOptions = { method: 'post',
				url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxSpatialQueryServlet',
				params: {
					dtInizioFiltro: this.temporalFilterDtInizio,
					dtFineFiltro: this.temporalFilterDtFine,
					coordX: point.x, 
					coordY: point.y,
					codTPN: codTPN,
					range: tolleranceRange,
					SRID: this.paramsJS.mappe.SRID,
					format: 'ext',
					selectionMode: selectionMode,
					//Parametri aggiunti per GetFeatureInfo
					bbox:  bounds.left+","+bounds.bottom+","+bounds.right+","+bounds.top ,
					mapwidth:  this.viewer.pluginGetMapWidth()  ,
					mapheight: this.viewer.pluginGetMapViewerHeight() ,
					X: mapXPixel,
					Y: mapYPixel,
					additionalWMSLayers: (additionalWMSLayers && additionalWMSLayers.length > 0) ? Ext.JSON.encode(additionalWMSLayers) : undefined,
					paramPreset: this.paramsJS.nomePreset
				},
				success: function(results, store){this.ajaxQuerySelectOK(results, store, addToSelected, visualize);},
				failure: function(transport){
					this.fireEvent('selectRequestEnd',{
						ok:false,
						nResults:0,
						errText:transport.responseText?transport.responseText:""+transport
					});
					this.showAjaxError(transport);
				}, 
				scope: this
			}
			
			// Aggiunta parametri WMS da url
			Ext.apply(ajaxOptions.params, this.paramsJS.urlAdditionalParams);
					
			if(this.fireEvent('selectRequestBeforeStart')){
				new TolomeoExt.ToloCrossAjax().request(ajaxOptions);
				this.fireEvent('selectRequestStart');
				*/
		
		var classiGenerate;
		if(Ext.isEmpty(response.responseText)) return;
		else classiGenerate = Ext.decode(response.responseText);

		var panel = this.getLegend();													
		// Rimuovo le regole presenti											
		while(panel.rules.length > 0)
		{
			for ( var h = 0; h < panel.rules.length; h++) {
				panel.rules.remove(panel.rules[h]);		
				this.fireEvent("ruleremoved", panel.rules[h]);
			}
		}
		
		this.saving = true;
		this.finestraGenerazioneStileDlg.disable();
		// INIZIO - Per ogni classe generata
		var classeCorrente = 0;
		var trovataAlmenoUnaClasse = false;
		
		while(classiGenerate[classeCorrente] != null && classiGenerate[classeCorrente] != undefined)
		{	
			var nuovaRegola = this.rule.clone();
			
			var masterFilter = new OpenLayers.Filter.Logical();
			masterFilter.type  = classiGenerate[classeCorrente].tipofiltro;
			
			for(filtroCorrente in classiGenerate[classeCorrente].filtri)
			{
				var emptyFilter = new OpenLayers.Filter.Comparison();
				
				if(!Ext.isEmpty(classiGenerate[classeCorrente].filtri[filtroCorrente].valoreFiltro) &&
				   !Ext.isEmpty(classiGenerate[classeCorrente].filtri[filtroCorrente].tipoFiltro))
				{
					emptyFilter.value = classiGenerate[classeCorrente].filtri[filtroCorrente].valoreFiltro;
					emptyFilter.type  = classiGenerate[classeCorrente].filtri[filtroCorrente].tipoFiltro;
					emptyFilter.property = classiGenerate[classeCorrente].filtri[filtroCorrente].colonna;
					masterFilter.filters[filtroCorrente] = emptyFilter;
				}
			}

			if(panel.symbolType == "Line")
			{
				nuovaRegola.symbolizers[0].strokeColor = classiGenerate[classeCorrente].colore;
			}
			else
			{
				nuovaRegola.symbolizers[0].fillColor = classiGenerate[classeCorrente].colore;
			}
			nuovaRegola.name = classiGenerate[classeCorrente].nome;
			nuovaRegola.title = classiGenerate[classeCorrente].nome;
			nuovaRegola.filter = masterFilter;
			nuovaRegola.id =  "OpenLayers.Rule_" + classeCorrente;
			panel.rules.push(nuovaRegola);
			trovataAlmenoUnaClasse = true;
			classeCorrente = classeCorrente + 1;
			this.fireEvent("ruleadded", nuovaRegola);
		}// FINE - Per ogni classe generata

		if(!trovataAlmenoUnaClasse)
		{
			Ext.MessageBox.show({
				title: 'Errore',
				msg: "Impossibile generare delle classi per con le impostazioni correnti",
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.ERROR
			});
			Ext.getBody().unmask();
			return;
		}
		
		this.sldManager.saveSld(this.currentLayer, function(){
			this.repaint();
		}, this);
				
		this.saving = false;
		
		this.repaint();
		panel.update();
		
		//this.currentLayer.display(false);
		//this.currentLayer.clearGrid();
		//this.currentLayer.mergeNewParams({'random':Math.random()});
		//this.currentLayer.redraw(true);
		//this.currentLayer.display(true);
		
		if(this.finestraGenerazioneStileDlg)
		{
			this.finestraGenerazioneStileDlg.close();
			this.finestraGenerazioneStileDlg.destroy();
		}
		
		
	},
	
	generaStiliFailure: function (){
		Ext.MessageBox.show({
			title: 'Errore',
			msg: "Errore nella generazione della stilizzazione",
			buttons: Ext.MessageBox.OK,
			icon: Ext.MessageBox.ERROR
		});
	}
	
	
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.StrokeSymbolizer", {
	extend: "Ext.FormPanel",
	alias: "widget.styler_strokesymbolizer",
	symbolizer: null,
	colorManager: null,
	checkboxToggle: true,
	defaultColor: null,
	withColor: true, 
	dashStyles: [{
		value: "solid",
		display: "solid"
	}, {
		value: "4 4",
		display: "dash"
	},{
		value: "2 4",
		display: "dot"
	}],
	border: false,
	initComponent: function() {
		if (!this.symbolizer) {
			this.symbolizer = {};
		}
		var colorFieldPlugins;
		if (this.colorManager) {
			colorFieldPlugins = [new this.colorManager];
		}
		this.items = [{
			xtype: "fieldset",
			title: "Tratto",
			autoHeight: true,
			checkboxToggle: this.checkboxToggle,
			collapsed: this.checkboxToggle === true && this.symbolizer.stroke === false,
			hideMode: "offsets",
			defaults: {
				width: 200
			},
			items: [{
				xtype: "combo",
				name: "style",
				fieldLabel: "Stile",
				store: Ext.create("Ext.data.JsonStore", {
					data: this.dashStyles,
					fields: ["value", "display"]
				}),
				displayField: "display",
				valueField: "value",
				value: this.getDashArray(this.symbolizer.strokeDashstyle) || TolomeoExt.Styler.Renderer.DefaultSymbolizer.strokeDashstyle,
				queryMode: "local",
				allowBlank: true,
				triggerAction: "all",
				editable: false,
				listeners: {
					select: function(combo, record) {
						this.symbolizer.strokeDashstyle = record[0].get("value");
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}, {
				xtype: "styler_colorfield",
				name: "color",
				fieldLabel: "Colore",
				hidden: !this.withColor, 
				emptyText: TolomeoExt.Styler.Renderer.DefaultSymbolizer.strokeColor,
				value: this.symbolizer.strokeColor || this.defaultColor || TolomeoExt.Styler.Renderer.DefaultSymbolizer.strokeColor,
				defaultBackground: this.defaultColor || TolomeoExt.Styler.Renderer.DefaultSymbolizer.strokeColor,
				plugins: colorFieldPlugins,
				listeners: {
					change: function(field, newValue, oldValue) {
						this.symbolizer.strokeColor = newValue;
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}, {
				xtype: "numberfield",
				name: "width",
				fieldLabel: "Spessore",
				minValue: 0,
				emptyText: TolomeoExt.Styler.Renderer.DefaultSymbolizer.strokeWidth,
				value: this.symbolizer.strokeWidth || TolomeoExt.Styler.Renderer.DefaultSymbolizer.strokeWidth,
				listeners: {
					change: function(field, value) {
						value = parseFloat(value);
						if (isNaN(value)) {
							delete this.symbolizer.strokeWidth;
						} else {
							this.symbolizer.strokeWidth = value;
						}
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}, {
				xtype: "slider",
				name: "opacity",
				fieldLabel: "Opacit&agrave;",
				values: [
					(("strokeOpacity" in this.symbolizer) ? this.symbolizer.strokeOpacity : TolomeoExt.Styler.Renderer.DefaultSymbolizer.strokeOpacity) * 100
				],
				isFormField: true,
				listeners: {
					changecomplete: function(slider, value) {
						this.symbolizer.strokeOpacity = value / 100;
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				},
				tipText: function(thumb) {
					return thumb.value + "%";
				}
			}],
			listeners: {
				"collapse": function() {
					if (this.symbolizer.stroke !== false) {
						this.symbolizer.stroke = false;
						this.fireEvent("change", this.symbolizer);
					}
				},
				"expand": function() {
					this.symbolizer.stroke = true;
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			}
		}];
		this.addEvents("change");
		TolomeoExt.Styler.StrokeSymbolizer.superclass.initComponent.call(this);
	},
	getDashArray: function(style) {
		var array;
		if (style) {
			var parts = style.split(/\s+/);
			var ratio = parts[0] / parts[1];
			var array;
			if (!isNaN(ratio)) {
				array = ratio >= 1 ? "4 4" : "2 4"
			}
		}
		return array;
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.FillSymbolizer", {
	extend: "Ext.FormPanel",
	alias: "widget.styler_fillsymbolizer",
	symbolizer: null,
	colorManager: null,
	checkboxToggle: true,
	defaultColor: null,
	withColor: true, 
	border: false,
	initComponent: function() {
		if (!this.symbolizer) {
			this.symbolizer = {};
		}
		var colorFieldPlugins;
		if (this.colorManager) {
			colorFieldPlugins = [new this.colorManager];
		}
		this.items = [{
			xtype: "fieldset",
			title: "Riempimento",
			autoHeight: true,
			checkboxToggle: this.checkboxToggle,
			collapsed: this.checkboxToggle === true && this.symbolizer.fill === false,
			hideMode: "offsets",
			defaults: {
				width: 200
			},
			items: [{
				xtype: "styler_colorfield",
				fieldLabel: "Colore",
				hidden: !this.withColor, 
				name: "color",
				emptyText: TolomeoExt.Styler.Renderer.DefaultSymbolizer.fillColor,
				value: this.symbolizer.fillColor || this.defaultColor || TolomeoExt.Styler.Renderer.DefaultSymbolizer.fillColor,
				defaultBackground: this.defaultColor || TolomeoExt.Styler.Renderer.DefaultSymbolizer.fillColor,
				plugins: colorFieldPlugins,
				listeners: {
					change: function(field, newValue, oldValue) {
						this.symbolizer.fillColor = newValue;
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}, {
				xtype: "slider",
				fieldLabel: "Opacit&agrave;",
				name: "opacity",
				values: [
					(("fillOpacity" in this.symbolizer) ? this.symbolizer.fillOpacity : TolomeoExt.Styler.Renderer.DefaultSymbolizer.fillOpacity) * 100
				],
				isFormField: true,
				listeners: {
					changecomplete: function(slider, value) {
						this.symbolizer.fillOpacity = value / 100;
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				},
				tipText: function(thumb) {
					return thumb.value + "%";
				}
			}],
			listeners: {
				"collapse": function() {
					if (this.symbolizer.fill !== false) {
						this.symbolizer.fill = false;
						this.fireEvent("change", this.symbolizer);
					}
				},
				"expand": function() {
					this.symbolizer.fill = true;
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			}
		}];
		this.addEvents("change");
		TolomeoExt.Styler.FillSymbolizer.superclass.initComponent.call(this);
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.TextSymbolizer", {
	extend: "Ext.Panel",
	alias: "widget.styler_textsymbolizer",
	fonts: undefined,
	symbolizer: null,
	defaultSymbolizer: null,
	attributes: null,
	colorManager: null,
	haloCache: null,
	border: false,
	layout: "form",
	initComponent: function() {
		if (!this.symbolizer) {
			this.symbolizer = {};
		}
		Ext.applyIf(this.symbolizer, this.defaultSymbolizer);
		this.haloCache = {};
		var defAttributesComboConfig = {
			xtype: "combo",
			fieldLabel: "Valori Etichetta",
			store: this.attributes,
			editable: false,
			triggerAction: "all",
			allowBlank: false,
			displayField: "name",
			valueField: "name",
			queryMode: "local",
			value: this.symbolizer.label && this.symbolizer.label.replace(/^\${(.*)}$/, "$1"),
			listeners: {
				select: function(combo, record) {
					this.symbolizer.label = "${" + record[0].get("name") + "}";
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			},
			width: 120
		};
		this.attributesComboConfig = this.attributesComboConfig || {};
		Ext.applyIf(this.attributesComboConfig, defAttributesComboConfig);
		this.labelWidth = 80;
		this.items = [this.attributesComboConfig, {
			cls: "x-html-editor-tb",
			style: "background: transparent; border: none; padding: 0 0em 0.5em;",
			xtype: "toolbar",
			items: [{
				xtype: "styler_fontcombo",
				fonts: this.fonts || undefined,
				width: 110,
				value: this.symbolizer.fontFamily,
				listeners: {
					select: function(combo, record) {
						this.symbolizer.fontFamily = record.get("field1");
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}, {
				xtype: "numberfield",
				fieldLabel: "Dimensione",
				labelWidth: 30,
				minValue: 0,
				emptyText: TolomeoExt.Styler.Renderer.DefaultSymbolizer.fontSize,
				value: this.symbolizer.fontSize || TolomeoExt.Styler.Renderer.DefaultSymbolizer.fontSize,
				width: 80,
				listeners: {
					change: function(field, value) {
						value = parseFloat(value);
						if (isNaN(value)) {
							delete this.symbolizer.fontSize;
						} else {
							this.symbolizer.fontSize = value;
						}
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}, {
				enableToggle: true,
				cls: "x-btn-icon",
				iconCls: "x-edit-bold",
				pressed: this.symbolizer.fontWeight === "bold",
				listeners: {
					toggle: function(button, pressed) {
						this.symbolizer.fontWeight = pressed ? "bold" : "normal";
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}, {
				enableToggle: true,
				cls: "x-btn-icon",
				iconCls: "x-edit-italic",
				pressed: this.symbolizer.fontStyle === "italic",
				listeners: {
					toggle: function(button, pressed) {
						this.symbolizer.fontStyle = pressed ? "italic" : "normal";
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}]
		}, {
			xtype: "styler_fillsymbolizer",
			symbolizer: this.symbolizer,
			defaultColor: TolomeoExt.Styler.Renderer.DefaultSymbolizer.fontColor,
			checkboxToggle: false,
			autoHeight: true,
			labelWidth: 70,
			plugins: this.colorManager && [new this.colorManager()],
			listeners: {
				change: function(symbolizer) {
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			}
		}, {
			xtype: "fieldset",
			title: "Alone",
			checkboxToggle: true,
			collapsed: !(this.symbolizer.haloRadius || this.symbolizer.haloColor || this.symbolizer.haloOpacity),
			autoHeight: true,
			labelWidth: 50,
			items: [{
				xtype: "numberfield",
				fieldLabel: "Size",
				anchor: "89%",
				minValue: 0,
				emptyText: TolomeoExt.Styler.Renderer.DefaultSymbolizer.haloRadius,
				value: this.symbolizer.haloRadius || TolomeoExt.Styler.Renderer.DefaultSymbolizer.haloRadius,
				listeners: {
					change: function(field, value) {
						value = parseFloat(value);
						if (isNaN(value)) {
							delete this.symbolizer.haloRadius;
						} else {
							this.symbolizer.haloRadius = value;
						}
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}, {
				xtype: "styler_fillsymbolizer",
				symbolizer: {
					fillColor: ("haloColor" in this.symbolizer) ? this.symbolizer.haloColor : TolomeoExt.Styler.Renderer.DefaultSymbolizer.haloColor,
					fillOpacity: ("haloOpacity" in this.symbolizer) ? this.symbolizer.haloOpacity : TolomeoExt.Styler.Renderer.DefaultSymbolizer.haloOpacity
				},
				defaultColor: TolomeoExt.Styler.Renderer.DefaultSymbolizer.haloColor,
				checkboxToggle: false,
				labelWidth: 60,
				plugins: this.colorManager && [new this.colorManager()],
				listeners: {
					change: function(symbolizer) {
						this.symbolizer.haloColor = symbolizer.fillColor;
						this.symbolizer.haloOpacity = symbolizer.fillOpacity;
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}],
			listeners: {
				collapse: function() {
					this.haloCache = {
						haloRadius: this.symbolizer.haloRadius,
						haloColor: this.symbolizer.haloColor,
						haloOpacity: this.symbolizer.haloOpacity
					};
					delete this.symbolizer.haloRadius;
					delete this.symbolizer.haloColor;
					delete this.symbolizer.haloOpacity;
					this.fireEvent("change", this.symbolizer)
				},
				expand: function() {
					Ext.apply(this.symbolizer, this.haloCache);
					this.doLayout();
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			}
		}];
		this.addEvents("change");
		TolomeoExt.Styler.TextSymbolizer.superclass.initComponent.call(this);
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.ScaleLimitPanel", {
	extend: "Ext.Panel",
	alias: "widget.styler_scalelimitpanel",
	maxScaleDenominatorLimit: 40075016.68 * 39.3701 * OpenLayers.DOTS_PER_INCH / 256,
	limitMaxScaleDenominator: true,
	maxScaleDenominator: undefined,
	minScaleDenominatorLimit: Math.pow(0.5, 19) * 40075016.68 * 39.3701 * OpenLayers.DOTS_PER_INCH / 256,
	limitMinScaleDenominator: true,
	minScaleDenominator: undefined,
	scaleLevels: 20,
	scaleSliderTemplate: "{scaleType} Scale 1:{scale}",
	modifyScaleTipContext: Ext.emptyFn,
	scaleFactor: null,
	changing: false,
	border: false,
	initComponent: function() {
		this.layout = "column";
		this.defaults = {
			border: false,
			bodyStyle: "margin: 0 5px;"
		};
		this.bodyStyle = {
			padding: "5px"
		};
		this.scaleSliderTemplate = new Ext.Template(this.scaleSliderTemplate);
		Ext.applyIf(this, {
			minScaleDenominator: this.minScaleDenominatorLimit,
			maxScaleDenominator: this.maxScaleDenominatorLimit
		});
		this.scaleFactor = Math.pow(this.maxScaleDenominatorLimit / this.minScaleDenominatorLimit, 1 / (this.scaleLevels - 1));
		this.scaleSlider = Ext.create("Ext.Slider", {
			vertical: true,
			height: 100,
			values: [0, 100],
			listeners: {
				changecomplete: function(slider, value) {
					this.updateScaleValues(slider);
				},
				render: function(slider) {
					slider.thumbs[0].el.setVisible(this.limitMaxScaleDenominator);
					slider.thumbs[1].el.setVisible(this.limitMinScaleDenominator);
					slider.setDisabled(!this.limitMinScaleDenominator && !this.limitMaxScaleDenominator);
				},
				scope: this
			},
			plugins: [Ext.create("TolomeoExt.Styler.slider.Tip", {
				getText: Ext.Function.pass(function(thumb) {
					var index = thumb.slider.thumbs.indexOf(thumb);
					var value = thumb.value;
					var scales = this.sliderValuesToScale([thumb.value]);
					var data = {
						scale: String(scales[0]),
						zoom: (thumb.value * (this.scaleLevels / 100)).toFixed(1),
						type: (index === 0) ? "Max" : "Min",
						scaleType: (index === 0) ? "Min" : "Max"
					};
					this.modifyScaleTipContext(this, data);
					return this.scaleSliderTemplate.apply(data);
				}, [], this)
			})]
		});
		this.maxScaleDenominatorInput = Ext.create("Ext.form.NumberField", {
			minValue: 0,
			width: 100,
			fieldLabel: "1",
			value: Math.round(this.maxScaleDenominator),
			disabled: !this.limitMaxScaleDenominator,
			validator: Ext.Function.pass(function(value) {
				return !this.limitMinScaleDenominator || (value > this.minScaleDenominator);
			}, [], this),
			listeners: {
				valid: function(field) {
					var value = Number(field.getValue());
					var limit = Math.round(this.maxScaleDenominatorLimit);
					if (value < limit && value > this.minScaleDenominator) {
						this.maxScaleDenominator = value;
						this.updateSliderValues();
					}
				},
				change: function(field) {
					var value = Number(field.getValue());
					var limit = Math.round(this.maxScaleDenominatorLimit);
					if (value > limit) {
						field.setValue(limit);
					} else if (value < this.minScaleDenominator) {
						field.setValue(this.minScaleDenominator);
					} else {
						this.maxScaleDenominator = value;
						this.updateSliderValues();
					}
				},
				scope: this
			}
		});
		this.minScaleDenominatorInput = Ext.create("Ext.form.NumberField", {
			minValue: 0,
			width: 100,
			fieldLabel: "1",
			value: Math.round(this.minScaleDenominator),
			disabled: !this.limitMinScaleDenominator,
			validator: Ext.Function.pass(function(value) {
				return !this.limitMaxScaleDenominator || (value < this.maxScaleDenominator);
			}, [], this),
			listeners: {
				valid: function(field) {
					var value = Number(field.getValue());
					var limit = Math.round(this.minScaleDenominatorLimit);
					if (value > limit && value < this.maxScaleDenominator) {
						this.minScaleDenominator = value;
						this.updateSliderValues();
					}
				},
				change: function(field) {
					var value = Number(field.getValue());
					var limit = Math.round(this.minScaleDenominatorLimit);
					if (value < limit) {
						field.setValue(limit);
					} else if (value > this.maxScaleDenominator) {
						field.setValue(this.maxScaleDenominator);
					} else {
						this.minScaleDenominator = value;
						this.updateSliderValues();
					}
				},
				scope: this
			}
		});
		this.items = [this.scaleSlider, {
			xtype: "panel",
			layout: "form",
			defaults: {
				border: false
			},
			items: [{
				labelWidth: 90,
				layout: "form",
				width: 150,
				items: [{
					xtype: "checkbox",
					checked: !!this.limitMinScaleDenominator,
					fieldLabel: "Limite di Scala Massimo",
					listeners: {
						change: function(box, newValue, oldValue) {
							this.limitMinScaleDenominator = newValue;
							var slider = this.scaleSlider;
							slider.setValue(1, 100);
							slider.thumbs[1].el.setVisible(newValue);
							this.minScaleDenominatorInput.setDisabled(!newValue);
							this.updateScaleValues(slider);
							slider.setDisabled(!this.limitMinScaleDenominator && !this.limitMaxScaleDenominator);
						},
						scope: this
					}
				}]
			}, {
				labelWidth: 10,
				layout: "form",
				items: [this.minScaleDenominatorInput]
			}, {
				labelWidth: 90,
				layout: "form",
				items: [{
					xtype: "checkbox",
					checked: !!this.limitMaxScaleDenominator,
					fieldLabel: "Limite di Scala Minimo",
					listeners: {
						change: function(box, newValue, oldValue) {
							this.limitMaxScaleDenominator = newValue;
							var slider = this.scaleSlider;
							slider.setValue(0, 0);
							slider.thumbs[0].el.setVisible(newValue);
							this.maxScaleDenominatorInput.setDisabled(!newValue);
							this.updateScaleValues(slider);
							slider.setDisabled(!this.limitMinScaleDenominator && !this.limitMaxScaleDenominator);
						},
						scope: this
					}
				}]
			}, {
				labelWidth: 10,
				layout: "form",
				items: [this.maxScaleDenominatorInput]
			}]
		}];
		this.addEvents("change");
		TolomeoExt.Styler.ScaleLimitPanel.superclass.initComponent.call(this);
	},
	updateScaleValues: function(slider) {
		if (!this.changing) {
			var values = slider.getValues();
			var resetSlider = false;
			if (!this.limitMaxScaleDenominator) {
				if (values[0] > 0) {
					values[0] = 0;
					resetSlider = true;
				}
			}
			if (!this.limitMinScaleDenominator) {
				if (values[1] < 100) {
					values[1] = 100;
					resetSlider = true;
				}
			}
			if (resetSlider) {
				slider.setValue(0, values[0]);
				slider.setValue(1, values[1]);
			} else {
				var scales = this.sliderValuesToScale(values);
				var max = scales[0];
				var min = scales[1];
				this.changing = true;
				this.minScaleDenominatorInput.setValue(min);
				this.maxScaleDenominatorInput.setValue(max);
				this.changing = false;
				this.fireEvent("change", this, (this.limitMinScaleDenominator) ? min : undefined, (this.limitMaxScaleDenominator) ? max : undefined);
			}
		}
	},
	updateSliderValues: function() {
		if (!this.changing) {
			var min = this.minScaleDenominator;
			var max = this.maxScaleDenominator;
			var values = this.scaleToSliderValues([max, min]);
			this.changing = true;
			this.scaleSlider.setValue(0, values[0]);
			this.scaleSlider.setValue(1, values[1]);
			this.changing = false;
			this.fireEvent("change", this, (this.limitMinScaleDenominator) ? min : undefined, (this.limitMaxScaleDenominator) ? max : undefined);
		}
	},
	sliderValuesToScale: function(values) {
		var interval = 100 / (this.scaleLevels - 1);
		return [Math.round(Math.pow(this.scaleFactor, (100 - values[0]) / interval) * this.minScaleDenominatorLimit), Math.round(Math.pow(this.scaleFactor, (100 - values[1]) / interval) * this.minScaleDenominatorLimit)];
	},
	scaleToSliderValues: function(scales) {
		var interval = 100 / (this.scaleLevels - 1);
		return [100 - (interval * Math.log(scales[0] / this.minScaleDenominatorLimit) / Math.log(this.scaleFactor)), 100 - (interval * Math.log(scales[1] / this.minScaleDenominatorLimit) / Math.log(this.scaleFactor))];
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.PointSymbolizer", {
	extend: "Ext.Panel",
	alias: "widget.styler_pointsymbolizer",
	symbolizer: null,
	withColor: true,
	pointGraphics: null,
	colorManager: null,
	external: null,
	layout: "form",
	initComponent: function() {
		
		//Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);

		if (this.pointGraphics==null) {
			this.pointGraphics = [{
				value: "circle",
				display: "circle",
				preview:  this.TOLOMEOServer + this.TOLOMEOStaticRoot + "img/styler/circle.gif",
				mark: true
			}, {
				value: "square",
				display: "square",
				preview: this.TOLOMEOServer + this.TOLOMEOStaticRoot + "img/styler/square.gif",
				mark: true
			}, {
				value: "triangle",
				display: "triangle",
				preview: this.TOLOMEOServer + this.TOLOMEOStaticRoot + "img/styler/triangle.gif",
				mark: true
			}, {
				value: "star",
				display: "star",
				preview: this.TOLOMEOServer + this.TOLOMEOStaticRoot + "img/styler/star.gif",
				mark: true
			}, {
				value: "cross",
				display: "cross",
				preview: this.TOLOMEOServer + this.TOLOMEOStaticRoot + "img/styler/cross.gif",
				mark: true
			}, {
				value: "x",
				display: "x",
				preview: this.TOLOMEOServer + this.TOLOMEOStaticRoot + "img/styler/x.gif",
				mark: true
			}, {
				display: "external"
			}]	
		}
			
		if (!this.symbolizer) {
			this.symbolizer = {};
		}
		this.external = !!this.symbolizer["externalGraphic"];
		this.markPanel = Ext.create("Ext.Panel", {
			border: false,
			collapsed: this.external,
			layout: "form",
			items: [{
				xtype: "styler_fillsymbolizer",
				symbolizer: this.symbolizer,
				withColor: this.withColor, 
				labelWidth: this.labelWidth,
				labelAlign: this.labelAlign,
				colorManager: this.colorManager,
				listeners: {
					change: function(symbolizer) {
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}, {
				xtype: "styler_strokesymbolizer",
				symbolizer: this.symbolizer,
				withColor: this.withColor, 
				labelWidth: this.labelWidth,
				labelAlign: this.labelAlign,
				colorManager: this.colorManager,
				listeners: {
					change: function(symbolizer) {
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				}
			}]
		});
		this.urlField = Ext.create("Ext.form.TextField", {
			name: "url",
			fieldLabel: "URL",
			value: this.symbolizer["externalGraphic"],
			hidden: true,
			listeners: {
				change: function(field, value) {
					this.symbolizer["externalGraphic"] = value;
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			},
			width: 100
		});
		this.graphicPanel = Ext.create("Ext.Panel", {
			border: false,
			collapsible: true,
			collapsed: !this.external,
			layout: "form",
			items: [this.urlField, {
				xtype: "slider",
				name: "opacity",
				fieldLabel: "Opacit&agrave;",
				values: [
					(this.symbolizer["graphicOpacity"] == null) ? 100 : this.symbolizer["graphicOpacity"] * 100
				],
				isFormField: true,
				listeners: {
					changecomplete: function(slider, value) {
						this.symbolizer["graphicOpacity"] = value / 100;
						this.fireEvent("change", this.symbolizer);
					},
					scope: this
				},
				tipText: function(thumb) {
					return thumb.value + "%";
				},
				width: 100
			}]
		});
		this.items = [{
			xtype: "combo",
			name: "mark",
			fieldLabel: "Simbolo",
			store: Ext.create("Ext.data.JsonStore", {
				data: this.pointGraphics,
				fields: ["value", "display", "preview", {
					name: "mark",
					type: "boolean"
				}]
			}),
			value: this.external ? 0 : this.symbolizer["graphicName"],
			displayField: "display",
			valueField: "value",
			tpl: new Ext.XTemplate(
				'<tpl for=".">',
					'<div class="x-boundlist-item x-combo-list-item gx-pointsymbolizer-mark-item">',
						'<tpl if="preview">',
							'<img src="{preview}" alt="{display}"/>',
						'</tpl>',
						'<span>{display}</span>',
					'</div>',
				'</tpl>'
			),
			queryMode: "local",
			allowBlank: false,
			triggerAction: "all",
			editable: false,
			listeners: {
				select: function(combo, records) {
					var mark = records[0].get("mark");
					var value = records[0].get("value");
					if (!mark) {
						if (value) {
							this.urlField.hide();
							this.urlField.getEl().up('.x-form-item').setDisplayed(false);
							this.symbolizer["externalGraphic"] = value;
						} else {
							this.urlField.show();
							this.urlField.getEl().up('.x-form-item').setDisplayed(true);
						}
						if (!this.external) {
							this.external = true;
							this.updateGraphicDisplay();
						}
					} else {
						if (this.external) {
							this.external = false;
							delete this.symbolizer["externalGraphic"];
							this.updateGraphicDisplay();
						}
						this.symbolizer["graphicName"] = value;
					}
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			},
			width: 100
		}, {
			xtype: "textfield",
			name: "size",
			fieldLabel: "Size",
			value: this.symbolizer["pointRadius"] && this.symbolizer["pointRadius"] * 2,
			listeners: {
				change: function(field, value) {
					this.symbolizer["pointRadius"] = value / 2;
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			},
			width: 100
		}, {
			xtype: "textfield",
			name: "rotation",
			fieldLabel: "Rotazione",
			value: this.symbolizer["rotation"] || TolomeoExt.Styler.Renderer.DefaultSymbolizer.pointRotation,
			listeners: {
				change: function(field, value) {
					this.symbolizer["rotation"] = value;
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			},
			width: 100
		}, this.markPanel, this.graphicPanel];
		this.addEvents("change");
		TolomeoExt.Styler.PointSymbolizer.superclass.initComponent.call(this);
	},
	updateGraphicDisplay: function() {
		if (this.external) {
			this.markPanel.collapse();
			this.graphicPanel.expand();
		} else {
			this.graphicPanel.collapse();
			this.markPanel.expand();
		}
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.LineSymbolizer", {
	extend: "Ext.Panel",
	alias: "widget.styler_linesymbolizer",
	symbolizer: null,
	withColor: true, 
	initComponent: function() {
		this.items = [{
			xtype: "styler_strokesymbolizer",
			symbolizer: this.symbolizer,
			withColor: this.withColor, 
			listeners: {
				change: function(symbolizer) {
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			}
		}];
		this.addEvents("change");
		TolomeoExt.Styler.LineSymbolizer.superclass.initComponent.call(this);
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.PolygonSymbolizer", {
	extend: "Ext.Panel",
	alias: "widget.styler_polygonsymbolizer",
	symbolizer: null,
	withColor: true,
	initComponent: function() {
		this.items = [{
			xtype: "styler_fillsymbolizer",
			symbolizer: this.symbolizer,
			withColor: this.withColor, 
			listeners: {
				change: function(symbolizer) {
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			}
		}, {
			xtype: "styler_strokesymbolizer",
			symbolizer: this.symbolizer,
			withColor: this.withColor, 
			listeners: {
				change: function(symbolizer) {
					this.fireEvent("change", this.symbolizer);
				},
				scope: this
			}
		}];
		this.addEvents("change");
		TolomeoExt.Styler.PolygonSymbolizer.superclass.initComponent.call(this);
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.FilterBuilder", {
	extend: "Ext.Container",
	alias: "widget.styler_filterbuilder",
	statics: {
		ANY_OF: 0,
		ALL_OF: 1,
		NONE_OF: 2,
		NOT_ALL_OF: 3
	},
	builderTypeNames: ["any", "all", "none", "not all"],
	allowedBuilderTypes: null,
	preComboText: "Corrisponde",
	postComboText: "del seguente:",
	cls: "gx-filterbuilder",
	builderType: null,
	childFilterContainer: null,
	customizeFilterOnInit: true,
	allowGroups: true,
	initComponent: function() {
		var defConfig = {
			defaultBuilderType: TolomeoExt.Styler.FilterBuilder.ANY_OF
		};
		Ext.applyIf(this, defConfig);
		if (this.customizeFilterOnInit) {
			this.filter = this.customizeFilter(this.filter);
		}
		this.builderType = this.getBuilderType();
		this.items = [{
			xtype: "container",
			layout: "form",
			defaults: {
				anchor: "100%"
			},
			hideLabels: true,
			items: [{
				xtype: "fieldcontainer",
				style: "padding-left: 2px",
				items: [{
					xtype: "label",
					style: "padding-top: 0.3em",
					text: this.preComboText
				}, this.createBuilderTypeCombo(), {
					xtype: "label",
					style: "padding-top: 0.3em",
					text: this.postComboText
				}]
			}, this.createChildFiltersPanel(), {
				xtype: "toolbar",
				items: this.createToolBar()
			}]
		}];
		this.addEvents("change");
		TolomeoExt.Styler.FilterBuilder.superclass.initComponent.call(this);
	},
	createToolBar: function() {
		var bar = [{
			text: "Aggiungi Condizione",
			iconCls: "add",
			handler: function() {
				this.addCondition();
			},
			scope: this
		}];
		if (this.allowGroups) {
			bar.push({
				text: "add group",
				iconCls: "add",
				handler: function() {
					this.addCondition(true);
				},
				scope: this
			});
		}
		return bar;
	},
	getFilter: function() {
		var filter;
		if (this.filter) {
			filter = this.filter.clone();
			if (filter instanceof OpenLayers.Filter.Logical) {
				filter = this.cleanFilter(filter);
			}
		}
		return filter;
	},
	cleanFilter: function(filter) {
		if (filter instanceof OpenLayers.Filter.Logical) {
			if (filter.type !== OpenLayers.Filter.Logical.NOT && filter.filters.length === 1) {
				filter = this.cleanFilter(filter.filters[0]);
			} else {
				var child;
				for (var i = 0, len = filter.filters.length; i < len; ++i) {
					child = filter.filters[i];
					if (child instanceof OpenLayers.Filter.Logical) {
						child = this.cleanFilter(child);
						if (child) {
							filter.filters[i] = child;
						} else {
							filter = child;
							break;
						}
					} else if (!child || child.type === null || child.property === null || child.value === null) {
						filter = false;
						break;
					}
				}
			}
		} else {
			if (!filter || filter.type === null || filter.property === null || filter.value === null) {
				filter = false;
			}
		}
		return filter;
	},
	customizeFilter: function(filter) {
		if (!filter) {
			filter = this.wrapFilter(this.createDefaultFilter());
		} else {
			filter = this.cleanFilter(filter);
			switch (filter.type) {
				case OpenLayers.Filter.Logical.AND:
				case OpenLayers.Filter.Logical.OR:
					if (!filter.filters || filter.filters.length === 0) {
						filter.filters = [this.createDefaultFilter()];
					} else {
						var child;
						for (var i = 0, len = filter.filters.length; i < len; ++i) {
							child = filter.filters[i];
							if (child instanceof OpenLayers.Filter.Logical) {
								filter.filters[i] = this.customizeFilter(child);
							}
						}
					}
					filter = new OpenLayers.Filter.Logical({
						type: OpenLayers.Filter.Logical.OR,
						filters: [filter]
					});
					break;
				case OpenLayers.Filter.Logical.NOT:
					if (!filter.filters || filter.filters.length === 0) {
						filter.filters = [new OpenLayers.Filter.Logical({
							type: OpenLayers.Filter.Logical.OR,
							filters: [this.createDefaultFilter()]
						})];
					} else {
						var child = filter.filters[0];
						if (child instanceof OpenLayers.Filter.Logical) {
							if (child.type !== OpenLayers.Filter.Logical.NOT) {
								var grandchild;
								for (var i = 0, len = child.filters.length; i < len; ++i) {
									grandchild = child.filters[i];
									if (grandchild instanceof OpenLayers.Filter.Logical) {
										child.filters[i] = this.customizeFilter(grandchild);
									}
								}
							} else {
								if (child.filters && child.filters.length > 0) {
									filter = this.customizeFilter(child.filters[0]);
								} else {
									filter = this.wrapFilter(this.createDefaultFilter());
								}
							}
						} else {
							var type;
							if (this.defaultBuilderType === TolomeoExt.Styler.FilterBuilder.NOT_ALL_OF) {
								type = OpenLayers.Filter.Logical.AND;
							} else {
								type = OpenLayers.Filter.Logical.OR;
							}
							filter.filters = [new OpenLayers.Filter.Logical({
								type: type,
								filters: [child]
							})];
						}
					}
					break;
				default:
					filter = this.wrapFilter(filter);
			}
		}
		return filter;
	},
	createDefaultFilter: function() {
		return new OpenLayers.Filter.Comparison();
	},
	wrapFilter: function(filter) {
		var type;
		if (this.defaultBuilderType === TolomeoExt.Styler.FilterBuilder.ALL_OF) {
			type = OpenLayers.Filter.Logical.AND;
		} else {
			type = OpenLayers.Filter.Logical.OR;
		}
		return new OpenLayers.Filter.Logical({
			type: OpenLayers.Filter.Logical.OR,
			filters: [new OpenLayers.Filter.Logical({
				type: type,
				filters: [filter]
			})]
		});
	},
	addCondition: function(group) {
		var filter, type;
		if (group) {
			type = "styler_filterbuilder";
			filter = this.wrapFilter(this.createDefaultFilter());
		} else {
			type = "styler_filterfield";
			filter = this.createDefaultFilter();
		}
		var newChild = this.newRow({
			xtype: type,
			filter: filter,
			columnWidth: 1,
			attributes: this.attributes,
			customizeFilterOnInit: group && false,
			listeners: {
				change: function() {
					this.fireEvent("change", this);
				},
				scope: this
			}
		});
		this.childFilterContainer.add(newChild);
		this.filter.filters[0].filters.push(filter);
		this.childFilterContainer.doLayout();
	},
	removeCondition: function(item, filter) {
		var parent = this.filter.filters[0].filters;
		if (parent.length > 1) {
			this.childFilterContainer.remove(item, true);
		}
		this.fireEvent("change", this);
	},
	createBuilderTypeCombo: function() {
		var types = this.allowedBuilderTypes || [TolomeoExt.Styler.FilterBuilder.ANY_OF, TolomeoExt.Styler.FilterBuilder.ALL_OF, TolomeoExt.Styler.FilterBuilder.NONE_OF];
		var numTypes = types.length;
		var data = new Array(numTypes);
		var type;
		for (var i = 0; i < numTypes; ++i) {
			type = types[i];
			data[i] = {value: type, name: this.builderTypeNames[type]};
		}
		return {
			xtype: "combo",
			store: Ext.create("Ext.data.JsonStore", {
				data: data,
				fields: ["value", "name"]
			}),
			value: this.builderType,
			displayField: "name",
			valueField: "value",
			triggerAction: "all",
			queryMode: "local",
			listeners: {
				select: function(combo, record) {
					this.changeBuilderType(record[0].get("value"));
					this.fireEvent("change", this);
				},
				scope: this
			},
			width: 60
		};
	},
	changeBuilderType: function(type) {
		if (type !== this.builderType) {
			this.builderType = type;
			var child = this.filter.filters[0];
			switch (type) {
				case TolomeoExt.Styler.FilterBuilder.ANY_OF:
					this.filter.type = OpenLayers.Filter.Logical.OR;
					child.type = OpenLayers.Filter.Logical.OR;
					break;
				case TolomeoExt.Styler.FilterBuilder.ALL_OF:
					this.filter.type = OpenLayers.Filter.Logical.OR;
					child.type = OpenLayers.Filter.Logical.AND;
					break;
				case TolomeoExt.Styler.FilterBuilder.NONE_OF:
					this.filter.type = OpenLayers.Filter.Logical.NOT;
					child.type = OpenLayers.Filter.Logical.OR;
					break;
				case TolomeoExt.Styler.FilterBuilder.NOT_ALL_OF:
					this.filter.type = OpenLayers.Filter.Logical.NOT;
					child.type = OpenLayers.Filter.Logical.AND;
					break;
			}
		}
	},
	createChildFiltersPanel: function() {
		this.childFilterContainer = new Ext.Container();
		var grandchildren = this.filter.filters[0].filters;
		var grandchild;
		for (var i = 0, len = grandchildren.length; i < len; ++i) {
			grandchild = grandchildren[i];
			var fieldCfg = {
				xtype: "styler_filterfield",
				columnWidth: 1,
				filter: grandchild,
				attributes: this.attributes,
				listeners: {
					change: function() {
						this.fireEvent("change", this);
					},
					scope: this
				}
			};
			var containerCfg = Ext.applyIf(grandchild instanceof OpenLayers.Filter.Logical ? {
				xtype: "styler_filterbuilder"
			} : {
				xtype: "container",
				layout: "form",
				hideLabels: true,
				items: fieldCfg
			}, fieldCfg);
			this.childFilterContainer.add(this.newRow(containerCfg));
		}
		return this.childFilterContainer;
	},
	newRow: function(filterContainer) {
		var ct = Ext.create("Ext.Container", {
			layout: "column",
			items: [{
				xtype: "container",
				width: 28,
				style: "padding-left: 2px",
				items: {
					xtype: "button",
					tooltip: "Rimuovi Condizione",
					iconCls: "delete",
					handler: function(btn) {
						this.removeCondition(ct, filterContainer.filter);
					},
					scope: this
				}
			}, filterContainer]
		});
		return ct;
	},
	getBuilderType: function() {
		var type = this.defaultBuilderType;
		if (this.filter) {
			var child = this.filter.filters[0];
			if (this.filter.type === OpenLayers.Filter.Logical.NOT) {
				switch (child.type) {
					case OpenLayers.Filter.Logical.OR:
						type = TolomeoExt.Styler.FilterBuilder.NONE_OF;
						break;
					case OpenLayers.Filter.Logical.AND:
						type = TolomeoExt.Styler.FilterBuilder.NOT_ALL_OF;
						break;
				}
			} else {
				switch (child.type) {
					case OpenLayers.Filter.Logical.OR:
						type = TolomeoExt.Styler.FilterBuilder.ANY_OF;
						break;
					case OpenLayers.Filter.Logical.AND:
						type = TolomeoExt.Styler.FilterBuilder.ALL_OF;
						break;
				}
			}
		}
		return type;
	}
});
Ext.namespace("TolomeoExt.Styler");

Ext.define("TolomeoExt.Styler.RulePanel", {
	extend: "Ext.TabPanel",
	alias: "widget.styler_rulepanel",
	fonts: undefined,
	symbolType: "Point",
	rule: null,
	attributes: null,
	nestedFilters: true,
	minScaleDenominatorLimit: Math.pow(0.5, 19) * 40075016.68 * 39.3701 * OpenLayers.DOTS_PER_INCH / 256,
	maxScaleDenominatorLimit: 40075016.68 * 39.3701 * OpenLayers.DOTS_PER_INCH / 256,
	scaleLevels: 20,
	scaleSliderTemplate: "{scaleType} Scale 1:{scale}",
	modifyScaleTipContext: Ext.emptyFn,
	
	initComponent: function() {
		
		this.addEvents('change');
	/*
		this.addEvents('save');
		this.addEvents('cancel');
		
		this.buttons = ["->", {
						text: "Annulla",
						iconCls: "cancel",
						handler: function() {
							this.fireEvent('cancel');
						},
						scope: this
					}, {
						text: "Salva",
						iconCls: "save",
						handler: function() {
							this.fireEvent('save', this.rule);
							//this.close();
							//this.ruleDialog.disable();
							//this.updateRule(rule, newRule);
							//this.ruleDialog.close();
			//				this.sldManager.saveSld(layer, function() {
			//					this.ruleDialog.close();
			//					this.repaint();
			//					this.saving = false;
			//				}, this);
						},
						scope: this
					}];
		
		*/
		var defConfig = {
			plain: true,
			border: false
		};
		Ext.applyIf(this, defConfig);
		if (!this.rule) {
			this.rule = new OpenLayers.Rule({
				name: this.uniqueRuleName()
			});
		} else {
			if (!this.initialConfig.symbolType) {
				this.symbolType = this.getSymbolTypeFromRule(this.rule) || this.symbolType;
			}
		}
		this.activeTab = 0;
		this.textSymbolizer = Ext.create("TolomeoExt.Styler.TextSymbolizer", {
			symbolizer: this.getTextSymbolizer(),
			attributes: this.attributes,
			fonts: this.fonts,
			listeners: {
				change: function(symbolizer) {
					this.fireEvent("change", this, this.rule);
				},
				scope: this
			}
		});
		this.scaleLimitPanel = Ext.create("TolomeoExt.Styler.ScaleLimitPanel", {
			maxScaleDenominator: this.rule.maxScaleDenominator || undefined,
			limitMaxScaleDenominator: !!this.rule.maxScaleDenominator,
			maxScaleDenominatorLimit: this.maxScaleDenominatorLimit,
			minScaleDenominator: this.rule.minScaleDenominator || undefined,
			limitMinScaleDenominator: !!this.rule.minScaleDenominator,
			minScaleDenominatorLimit: this.minScaleDenominatorLimit,
			scaleLevels: this.scaleLevels,
			scaleSliderTemplate: this.scaleSliderTemplate,
			modifyScaleTipContext: this.modifyScaleTipContext,
			listeners: {
				change: function(comp, min, max) {
					this.rule.minScaleDenominator = min;
					this.rule.maxScaleDenominator = max;
					this.fireEvent("change", this, this.rule);
				},
				scope: this
			}
		});
		this.filterBuilder = Ext.create("TolomeoExt.Styler.FilterBuilder", {
			allowGroups: this.nestedFilters,
			filter: this.rule && this.rule.filter && this.rule.filter.clone(),
			attributes: this.attributes,
			listeners: {
				change: function(builder) {
					var filter = builder.getFilter();
					this.rule.filter = filter;
					this.fireEvent("change", this, this.rule)
				},
				scope: this
			}
		});
		this.items = [{
			title: "Etichette",
			autoScroll: true,
			bodyStyle: {
				"padding": "10px"
			},
			items: [{
				xtype: "fieldset",
				title: "Caratteristiche Etichetta",
				autoHeight: true,
				checkboxToggle: true,
				collapsed: !this.hasTextSymbolizer(),
				items: [this.textSymbolizer],
				listeners: {
					collapse: function() {
						OpenLayers.Util.removeItem(this.rule.symbolizers, this.getTextSymbolizer());
						this.fireEvent("change", this, this.rule);
					},
					expand: function() {
						this.setTextSymbolizer(this.textSymbolizer.symbolizer);
						this.fireEvent("change", this, this.rule);
					},
					scope: this
				}
			}]
		}];
		if (this.getSymbolTypeFromRule(this.rule) || this.symbolType) {
			this.items = [{
				title: "Base",
				autoScroll: true,
				items: [
					this.createHeaderPanel(),
					this.createSymbolizerPanel()
				]
			}, this.items[0], {
				title: "Avanzato",
				defaults: {
					style: {
						margin: "7px"
					}
				},
				autoScroll: true,
				items: [{
					xtype: "fieldset",
					title: "Limite per Scala",
					checkboxToggle: true,
					collapsed: !(this.rule && (this.rule.minScaleDenominator || this.rule.maxScaleDenominator)),
					autoHeight: true,
					items: [this.scaleLimitPanel],
					listeners: {
						collapse: function() {
							delete this.rule.minScaleDenominator;
							delete this.rule.maxScaleDenominator;
							this.fireEvent("change", this, this.rule)
						},
						expand: function() {
							var tab = this.getActiveTab();
							this.activeTab = null;
							this.setActiveTab(tab);
							var changed = false;
							if (this.scaleLimitPanel.limitMinScaleDenominator) {
								this.rule.minScaleDenominator = this.scaleLimitPanel.minScaleDenominator;
								changed = true;
							}
							if (this.scaleLimitPanel.limitMaxScaleDenominator) {
								this.rule.maxScaleDenominator = this.scaleLimitPanel.maxScaleDenominator;
								changed = true;
							}
							if (changed) {
								this.fireEvent("change", this, this.rule)
							}
						},
						scope: this
					}
				}, {
					xtype: "fieldset",
					title: "Limite per Condizione",
					checkboxToggle: true,
					collapsed: !(this.rule && this.rule.filter),
					autoHeight: true,
					items: [this.filterBuilder],
					listeners: {
						collapse: function() {
							delete this.rule.filter;
							this.fireEvent("change", this, this.rule)
						},
						expand: function() {
							var changed = false;
							this.rule.filter = this.filterBuilder.getFilter();
							this.fireEvent("change", this, this.rule)
						},
						scope: this
					}
				}]
			}]
		}
		this.items[0].autoHeight = true;
		this.addEvents("change");
		this.on({
			tabchange: function(panel, tab) {
				tab.doLayout();
			},
			scope: this
		});
		TolomeoExt.Styler.RulePanel.superclass.initComponent.call(this);
	},
	
	hasTextSymbolizer: function() {
		var candidate, symbolizer;
		for (var i = 0, ii = this.rule.symbolizers.length; i < ii; ++i) {
			candidate = this.rule.symbolizers[i];
			if (candidate instanceof OpenLayers.Symbolizer.Text) {
				symbolizer = candidate;
				break;
			}
		}
		return symbolizer;
	},
	
	getTextSymbolizer: function() {
		var symbolizer = this.hasTextSymbolizer();
		if (!symbolizer) {
			symbolizer = new OpenLayers.Symbolizer.Text();
		}
		return symbolizer;
	},
	
	setTextSymbolizer: function(symbolizer) {
		var found;
		for (var i = 0, ii = this.rule.symbolizers.length; i < ii; ++i) {
			candidate = this.rule.symbolizers[i];
			if (this.rule.symbolizers[i] instanceof OpenLayers.Symbolizer.Text) {
				this.rule.symbolizers[i] = symbolizer;
				found = true;
				break;
			}
		}
		if (!found) {
			this.rule.symbolizers.push(symbolizer);
		}
	},
	
	uniqueRuleName: function() {
		return OpenLayers.Util.createUniqueID("rule_");
	},
	
	createHeaderPanel: function() {
		this.symbolizerSwatch = Ext.create("TolomeoExt.Styler.FeatureRenderer", {
			symbolType: this.symbolType,
			symbolizers: this.rule.symbolizers,
			fieldLabel: "Simbolo",
			labelAlign: "top"
		});
		return {
			xtype: "form",
			border: false,
			labelAlign: "top",
			defaults: {
				border: false
			},
			style: {
				"padding": "0.3em 0 0 1em"
			},
			items: [{
				layout: "column",
				defaults: {
					border: false,
					style: {
						"padding-right": "1em"
					}
				},
				items: [{
					layout: "form",
					width: 150,
					items: [{
						xtype: "textfield",
						fieldLabel: "Nome",
						labelAlign: "top",
						anchor: "95%",
						value: this.rule && (this.rule.title || this.rule.name || ""),
						listeners: {
							change: function(el, value) {
								this.rule.title = value;
								this.fireEvent("change", this, this.rule);
							},
							scope: this
						}
					}]
				}, {
					layout: "form",
					width: 70,
					items: [
						this.symbolizerSwatch
					]
				}]
			}]
		};
	},
	
	createSymbolizerPanel: function() {
		var candidate, symbolizer;
		var Type = OpenLayers.Symbolizer[this.symbolType];
		var existing = false;
		if (Type) {
			for (var i = 0, ii = this.rule.symbolizers.length; i < ii; ++i) {
				candidate = this.rule.symbolizers[i];
				if (candidate instanceof Type) {
					existing = true;
					symbolizer = candidate;
					break;
				}
			}
			if (!symbolizer) {
				symbolizer = Ext.create("Type", {
					fill: false,
					stroke: false
				});
			}
		} else {
			throw new Error("Appropriate symbolizer type not included in build: " + this.symbolType);
		}
		var cfg = {
			xtype: "styler_" + this.symbolType.toLowerCase() + "symbolizer",
			symbolizer: symbolizer,
			bodyStyle: {
				padding: "10px"
			},
			border: false,
			labelWidth: 70,
			defaults: {
				labelWidth: 70
			},
			listeners: {
				change: function(symbolizer) {
					this.symbolizerSwatch.setSymbolizers([symbolizer], {
						draw: this.symbolizerSwatch.rendered
					});
					if (!existing) {
						this.rule.symbolizers.push(symbolizer);
						existing = true;
					}
					this.fireEvent("change", this, this.rule);
				},
				scope: this
			}
		};
		if (this.symbolType === "Point" && this.pointGraphics) {
			cfg.pointGraphics = this.pointGraphics;
		}
		return cfg;
	},
	
	getSymbolTypeFromRule: function(rule) {
		var candidate, type;
		for (var i = 0, ii = rule.symbolizers.length; i < ii; ++i) {
			candidate = rule.symbolizers[i];
			if (!(candidate instanceof OpenLayers.Symbolizer.Text)) {
				type = candidate.CLASS_NAME.split(".").pop();
				break;
			}
		}
		return type;
	}
	
});
/* 
 Tolomeo is a developing framework for visualization, editing,  
 geoprocessing and decisional support application based on cartography.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 This file is part of Tolomeo.
 
 Tolomeo is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License 
 as published by the Free Software Foundation; either version 3 of the License, 
 or (at your option) any later version.
 
 Tolomeo is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License along with Tolomeo; 
 if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110­1301  USA
 
 Developers Information:
 
 Tolomeo is developed by Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it 
 
 
 Versione in Italiano LGPL
 
 Tolomeo è un framework per lo sviluppo di applicazioni per
 visualizzazione, editing, geoprocessing e supporto alla decisione basate su cartografia.
 
 Tolomeo Copyright 2011 Comune di Prato;
 
 Questo file fa parte di Tolomeo.
 
 Tolomeo è un software libero; è possibile redistribuirlo e / o 
 modificarlo sotto i termini della GNU Lesser General Public License, 
 come pubblicato dalla Free Software Foundation, sia la versione 3 della licenza o (a propria scelta) una versione successiva.
  
 Tolomeo è distribuito nella speranza che possa essere utile,
 ma SENZA ALCUNA GARANZIA, senza neppure la garanzia implicita di COMMERCIABILITÀ o
 IDONEITÀ PER UN PARTICOLARE SCOPO. Vedere la GNU Lesser General Public License per ulteriori dettagli.
 
 Si dovrebbe avere ricevuto una copia della GNU Lesser General Public insieme a Tolomeo, in caso contrario, 
 si scriva alla Free Software  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110­1301 USA
   
 
 Informazioni Sviluppatori:
 
 Tolomeo è sviluppato dal Comune di Prato
 
 Alessandro Radaelli
 Federico Nieri
 Mattia Gennari
 
 sit@comune.prato.it
*/


/**
 * Class: TolomeoExt.ToloButtonPanelExt
 * 
 * Inherits from:
 *  - <Ext.Toolbar>
 *
 */

Ext.define('TolomeoExt.Styler.RuleGeneratorPanel', { 

	extend: 'Ext.Panel',

	fonts: undefined,
	symbolType : "Point",
	rule : null,
	attributes : null,
	nestedFilters : true,
	minScaleDenominatorLimit : Math.pow(0.5, 19) * 40075016.68 * 39.3701 * OpenLayers.DOTS_PER_INCH / 256,
	maxScaleDenominatorLimit : 40075016.68 * 39.3701 * OpenLayers.DOTS_PER_INCH / 256,
	scaleLevels : 20,
	scaleSliderTemplate : "{type} Scala 1:{scale}",
	modifyScaleTipContext : Ext.emptyFn,
	height : 300,
	width : 500,
		
	nomeCampoTematizzazione : "",
	numeroClassiTematizzazione : 2,
	precisioneDecimale : 0,
	tipoCreazione: null,
	
	statics: {
		TIPOGENERAZIONE : {
				SEMPLICE : {
					'nome': 'Semplice',
					'titoloFinestra': "Creazione nuova stilizzazione di tipo Semplice",
					'codice': 0},
				NATURAL_BREAK : {
					'nome': 'Natural Break',
					'titoloFinestra': "Creazione nuova stilizzazione di tipo Natural Break",
					'mostraPrecisioneDecimale': false,
					'mostraNumeroClassi': true,
					'codice': 1},
				EQUAL_COUNT : {
					'nome': 'Equal Count',
					'titoloFinestra': "Creazione nuova stilizzazione di tipo Equal Count",
					'mostraNumeroClassi': true,
					'mostraPrecisioneDecimale': true,
					'codice': 2},
				EQUAL_RANGE : {
					'nome': 'Equal Range',
					'titoloFinestra': "Creazione nuova stilizzazione di tipo Equal Range",
					'mostraNumeroClassi': true,
					'mostraPrecisioneDecimale': true,
					'codice': 3},
				VALORI_UNIVOCI : {
					'nome': 'Valori Univoci',
					'titoloFinestra': "Creazione nuova stilizzazione con Valori Univoci",
					'mostraNumeroClassi': false,
					'mostraPrecisioneDecimale': false,
					'codice': 4}
			}
	},
	
	initComponent : function() {		
		
		// Applico i default
		TolomeoExt.Vars.ApplyIfDefaults(this);

		var defConfig = {
			plain : true,
			border : false
		};		
		Ext.applyIf(this, defConfig);
		if (!this.rule) {
			this.rule = new OpenLayers.Rule({
				name : this.uniqueRuleName()
			});
		} else {
            if (!this.initialConfig.symbolType) {
                this.symbolType = this.getSymbolTypeFromRule(this.rule) || this.symbolType;
            }
        }		
		this.activeTab = 0;
		//TODO ALE
		/*this.textSymbolizer = new gxp.TextSymbolizer({
            symbolizer: this.getTextSymbolizer(),
            attributes: this.attributes,
            fonts: this.fonts,
            listeners: {
                change: function (symbolizer) {
                    this.fireEvent("change", this, this.rule);
                },
                scope: this
            }
        });		
		this.scaleLimitPanel = new gxp.ScaleLimitPanel({
            maxScaleDenominator: this.rule.maxScaleDenominator || undefined,
            limitMaxScaleDenominator: !! this.rule.maxScaleDenominator,
            maxScaleDenominatorLimit: this.maxScaleDenominatorLimit,
            minScaleDenominator: this.rule.minScaleDenominator || undefined,
            limitMinScaleDenominator: !! this.rule.minScaleDenominator,
            minScaleDenominatorLimit: this.minScaleDenominatorLimit,
            scaleLevels: this.scaleLevels,
            scaleSliderTemplate: this.scaleSliderTemplate,
            modifyScaleTipContext: this.modifyScaleTipContext,
            listeners: {
                change: function (comp, min, max) {
                    this.rule.minScaleDenominator = min;
                    this.rule.maxScaleDenominator = max;
                    this.fireEvent("change", this, this.rule);
                },
                scope: this
            }
        });        
        this.filterBuilder = new gxp.FilterBuilder({
            allowGroups: this.nestedFilters,
            filter: this.rule && this.rule.filter && this.rule.filter.clone(),
            attributes: this.attributes,
            listeners: {
                change: function (builder) {
                    var filter = builder.getFilter();
                    this.rule.filter = filter;
                    this.fireEvent("change", this, this.rule)
                },
                scope: this
            }
        });*/				
		this.items = [ {
			title : "Etichettatura",
			autoScroll : true,
			bodyStyle : {
				"padding" : "10px"
			},
			items : [ {
				xtype : "fieldset",
				title : "Etichettatura Geometrie",
				autoHeight : true,
				width : 385,  // Dimensione del box che contiene le informazioni sulle etichette
				checkboxToggle : true,
				collapsed : !this.hasTextSymbolizer(),
				items : [ this.textSymbolizer ],
				listeners : {
                    collapse: function () {
                        OpenLayers.Util.removeItem(this.rule.symbolizers, this.getTextSymbolizer());
                        this.fireEvent("change", this, this.rule);
                    },
                    expand: function () {
                        this.setTextSymbolizer(this.textSymbolizer.symbolizer);
                        this.fireEvent("change", this, this.rule);
                    },
					scope : this
				}
			}]
		}];
		this.items = [{
			title : "Configurazione",
			autoScroll : true,
			defaults : {
				style : { margin : "7px" }
			},
				items : [{										
					xtype : "fieldset",
					title : "Propriet&agrave; della tematizzazione",
					collapsed : false,
					autoHeight : true,
					items : [{
						xtype : "combo",
						fieldLabel : "Campo valori",
						store : this.attributes,
						queryMode: "local",
						editable : false,
						triggerAction : "all",
						allowBlank : false,
						displayField : "name",
						valueField : "name",
					    forceSelection: true,
					    width : 250,
					    listWidth : 300,
						value : "",
						listeners : {
							select : function(combo, records) { 
										this.nomeCampoTematizzazione = records[0].get("name"); 
									},
							scope : this
						}
					}, {
						xtype : "numberfield",
						name : "numero_classi",
						fieldLabel : "Numero Classi",
						value : 1,
						allowBlank: false,
				        decimalPrecision: 0,
				        allowDecimals: false,
				        minValue: 1,
				        hidden: !this.tipoCreazione.mostraNumeroClassi,
				        hideLabel : !this.tipoCreazione.mostraNumeroClassi,
						//width : 80,
						//labelWidth : 90,
						listeners : {
							change : function(field, value) { numeroClassiTematizzazione = value; },
							scope : this
						}												
					}, {
						xtype : "numberfield",
						name : "numero_precisione_decimale",
						fieldLabel : this.tipoCreazione.mostraPrecisioneDecimale ? "Precisione decimale" : "",
						value : 0,
						allowBlank: false,
						hideLabel : !this.tipoCreazione.mostraPrecisioneDecimale,
				        decimalPrecision: 0,
				        minValue: 0,
				        allowDecimals: false,
				       	hidden : !this.tipoCreazione.mostraPrecisioneDecimale,
						//width : 80,
						//labelWidth : 90,
						listeners : {
							change : function(field, value) { this.precisioneDecimale = value; },
							scope : this
						}												
					}]
				}]
			}, {
				title : "Aspetto",
				autoScroll : true,
				//this.createHeaderPanel()
				items : [this.createSymbolizerPanel() ]
			},
			//this.items[0],
			{
				title : "Scala",
				defaults : {
					style : { margin : "7px" }
				},
				autoScroll : true,
				items : [{
                xtype: "fieldset",
                title: "Limite di scala",
                checkboxToggle: true,
                collapsed: !(this.rule && (this.rule.minScaleDenominator || this.rule.maxScaleDenominator)),
                autoHeight: true,
                items: [this.scaleLimitPanel],
                listeners: {
                    collapse: function () {
                        delete this.rule.minScaleDenominator;
                        delete this.rule.maxScaleDenominator;
                        this.fireEvent("change", this, this.rule)
                    },
                    expand: function () {
                        var tab = this.getActiveTab();
                        this.activeTab = null;
                        this.setActiveTab(tab);
                        var changed = false;
                        if (this.scaleLimitPanel.limitMinScaleDenominator) {
                            this.rule.minScaleDenominator = this.scaleLimitPanel.minScaleDenominator;
                            changed = true;
                        }
                        if (this.scaleLimitPanel.limitMaxScaleDenominator) {
                            this.rule.maxScaleDenominator = this.scaleLimitPanel.maxScaleDenominator;
                            changed = true;
                        }
                        if (changed) { this.fireEvent("change", this, this.rule) }
                    },
                    scope: this
                }
            }]
		}]

		this.items[0].autoHeight = true;
		
		this.bbar = [
				"->",
				{
					text : "Annulla",
					iconCls : "cancel",
					handler : function() {
						this.fireEvent('cancelPressed');
					},
					scope : this
				}, {
					text : "Genera",
					iconCls : "save",
					scope : this,
					handler : function() {
						Ext.getBody().mask('Elaborazione in corso...');

						//this.rule = this.finestraGenerazioneStileDlg.items.items[0].rule.clone();
															
						new TolomeoExt.ToloCrossAjax().request({
							url: this.TOLOMEOServer + this.TOLOMEOContext + "/AjaxGeneraStiliServlet",
							method: 'POST',
							params: {
								//url: this.urlWFS,
								tipoCreazione: this.tipoCreazione.codice,
								precisione: this.precisioneDecimale,
								classi : this.numeroClassiTematizzazione,
								campo : this.nomeCampoTematizzazione,
								codTPN: this.layer.codTPN,
								// TODO
								//codice_comune: codiceComuneParamJsLocal,
								codice_comune: ""
							},
							failure : this.generaStiliFailure,
							success : this.generaStiliSuccess,
							scope : this
						});
					}
				} ];
		
		
		this.addEvents("change");
		this.on({
			tabchange : function(panel, tab) { tab.doLayout(); },
			scope : this
		});
		this.callParent();
	},
	
	uniqueRuleName : function() {
		return OpenLayers.Util.createUniqueID("rule_");
	},
	
	createHeaderPanel : function() {	
		return {
			xtype : "form",
			border : false,
			labelAlign : "top",
			defaults : { border : false },
			style : { "padding" : "0.3em 0 0 1em" },
			items : [{
				layout : "column",
				defaults : {
					border : false,
					style : { "padding-right" : "1em" }
				}
			}]
		};
	},
	
	createSymbolizerPanel: function () {
        var candidate, symbolizer;
        var Type = OpenLayers.Symbolizer[this.symbolType];
        var existing = false;
        if (Type) {
        	if (this.rule.symbolizers) {
        		for (var i = 0, ii = this.rule.symbolizers.length; i < ii; ++i) {
                    candidate = this.rule.symbolizers[i];
                    if (candidate instanceof Type) {
                        existing = true;
                        symbolizer = candidate;
                        break;
                    }
                }
        	}
            
            if (!symbolizer) {
                symbolizer = new Type({
                    fill: false,
                    stroke: false
                });
            }
        } else {
            throw new Error("Impossibile trovare un form di tematizzazione per il tipo di geometria: " + this.symbolType);
        }
        
        var cfg = {
        	xtype: 'styler_' + this.symbolType.toLowerCase() + 'symbolizer',
            //xtype: "gx_genera" + this.symbolType.toLowerCase() + "symbolizer",
            symbolizer: symbolizer,
            withColor: false, 
            bodyStyle: { padding: "10px" },
            border: false,
            labelWidth: 70,
            defaults: { labelWidth: 70 },
            listeners: {
                change: function (symbolizer) {
                    this.symbolizerSwatch.setSymbolizers([symbolizer], {
                        draw: this.symbolizerSwatch.rendered
                    });
                    if (!existing) {
                        this.rule.symbolizers.push(symbolizer);
                        existing = true;
                    }
                    this.fireEvent("change", this, this.rule);
                },
                scope: this
            }
        };
        if (this.symbolType === "Point" && this.pointGraphics) {
            cfg.pointGraphics = this.pointGraphics;
        }
        
        return cfg;
    },
	hasTextSymbolizer: function () {
        var candidate, symbolizer;
        if (this.rule.symbolizers) {
        	for (var i = 0, ii = this.rule.symbolizers.length; i < ii; ++i) {
                candidate = this.rule.symbolizers[i];
                if (candidate instanceof OpenLayers.Symbolizer.Text) {
                    symbolizer = candidate;
                    break;
                }
            }
            return symbolizer;
        } else {
        	return null;
        }
        
    },
    getTextSymbolizer: function () {
        var symbolizer = this.hasTextSymbolizer();
        if (!symbolizer) {
            symbolizer = new OpenLayers.Symbolizer.Text();
        }
        return symbolizer;
    },
    setTextSymbolizer: function (symbolizer) {
        var found;
        for (var i = 0, ii = this.rule.symbolizers.length; i < ii; ++i) {
            candidate = this.rule.symbolizers[i];
            if (this.rule.symbolizers[i] instanceof OpenLayers.Symbolizer.Text) {
                this.rule.symbolizers[i] = symbolizer;
                found = true;
                break;
            }
        }
        if (!found) {
            this.rule.symbolizers.push(symbolizer);
        }
    },
    uniqueRuleName: function () {
        return OpenLayers.Util.createUniqueID("rule_");
    },
    createHeaderPanel: function () {
        this.symbolizerSwatch = Ext.create('TolomeoExt.Styler.FeatureRenderer', {
            symbolType: this.symbolType,
            symbolizers: this.rule.symbolizers,
            isFormField: true,
            fieldLabel: "Anteprima"
        });
        return {
            xtype: "form",
            border: false,
            labelAlign: "top",
            defaults: {
                border: false
            },
            style: {
                "padding": "0.3em 0 0 1em"
            },
            items: [{
                layout: "column",
                defaults: {
                    border: false,
                    style: {
                        "padding-right": "1em"
                    }
                },
                items: [{
                    layout: "form",
                    width: 70,
                    items: [this.symbolizerSwatch]
                }]
            }]
        };
    },
    getSymbolTypeFromRule: function (rule) {
        var candidate, type;
        for (var i = 0, ii = rule.symbolizers.length; i < ii; ++i) {
            candidate = rule.symbolizers[i];
            if (!(candidate instanceof OpenLayers.Symbolizer.Text)) {
                type = candidate.CLASS_NAME.split(".").pop();
                break;
            }
        }
        return type;
    },
	
	generaStiliSuccess: function (results, store){
		
	/*
		var ajaxOptions = { method: 'post',
				url: this.TOLOMEOServer + this.TOLOMEOContext + '/AjaxSpatialQueryServlet',
				params: {
					dtInizioFiltro: this.temporalFilterDtInizio,
					dtFineFiltro: this.temporalFilterDtFine,
					coordX: point.x, 
					coordY: point.y,
					codTPN: codTPN,
					range: tolleranceRange,
					SRID: this.paramsJS.mappe.SRID,
					format: 'ext',
					selectionMode: selectionMode,
					//Parametri aggiunti per GetFeatureInfo
					bbox:  bounds.left+","+bounds.bottom+","+bounds.right+","+bounds.top ,
					mapwidth:  this.viewer.pluginGetMapWidth()  ,
					mapheight: this.viewer.pluginGetMapViewerHeight() ,
					X: mapXPixel,
					Y: mapYPixel,
					additionalWMSLayers: (additionalWMSLayers && additionalWMSLayers.length > 0) ? Ext.JSON.encode(additionalWMSLayers) : undefined,
					paramPreset: this.paramsJS.nomePreset
				},
				success: function(results, store){this.ajaxQuerySelectOK(results, store, addToSelected, visualize);},
				failure: function(transport){
					this.fireEvent('selectRequestEnd',{
						ok:false,
						nResults:0,
						errText:transport.responseText?transport.responseText:""+transport
					});
					this.showAjaxError(transport);
				}, 
				scope: this
			}
			
			// Aggiunta parametri WMS da url
			Ext.apply(ajaxOptions.params, this.paramsJS.urlAdditionalParams);
					
			if(this.fireEvent('selectRequestBeforeStart')){
				new TolomeoExt.ToloCrossAjax().request(ajaxOptions);
				this.fireEvent('selectRequestStart');
				*/
		
		var classiGenerate;
		if(Ext.isEmpty(response.responseText)) return;
		else classiGenerate = Ext.decode(response.responseText);

		var panel = this.getLegend();													
		// Rimuovo le regole presenti											
		while(panel.rules.length > 0)
		{
			for ( var h = 0; h < panel.rules.length; h++) {
				panel.rules.remove(panel.rules[h]);		
				this.fireEvent("ruleremoved", panel.rules[h]);
			}
		}
		
		this.saving = true;
		this.finestraGenerazioneStileDlg.disable();
		// INIZIO - Per ogni classe generata
		var classeCorrente = 0;
		var trovataAlmenoUnaClasse = false;
		
		while(classiGenerate[classeCorrente] != null && classiGenerate[classeCorrente] != undefined)
		{	
			var nuovaRegola = this.rule.clone();
			
			var masterFilter = new OpenLayers.Filter.Logical();
			masterFilter.type  = classiGenerate[classeCorrente].tipofiltro;
			
			for(filtroCorrente in classiGenerate[classeCorrente].filtri)
			{
				var emptyFilter = new OpenLayers.Filter.Comparison();
				
				if(!Ext.isEmpty(classiGenerate[classeCorrente].filtri[filtroCorrente].valoreFiltro) &&
				   !Ext.isEmpty(classiGenerate[classeCorrente].filtri[filtroCorrente].tipoFiltro))
				{
					emptyFilter.value = classiGenerate[classeCorrente].filtri[filtroCorrente].valoreFiltro;
					emptyFilter.type  = classiGenerate[classeCorrente].filtri[filtroCorrente].tipoFiltro;
					emptyFilter.property = classiGenerate[classeCorrente].filtri[filtroCorrente].colonna;
					masterFilter.filters[filtroCorrente] = emptyFilter;
				}
			}

			if(panel.symbolType == "Line")
			{
				nuovaRegola.symbolizers[0].strokeColor = classiGenerate[classeCorrente].colore;
			}
			else
			{
				nuovaRegola.symbolizers[0].fillColor = classiGenerate[classeCorrente].colore;
			}
			nuovaRegola.name = classiGenerate[classeCorrente].nome;
			nuovaRegola.title = classiGenerate[classeCorrente].nome;
			nuovaRegola.filter = masterFilter;
			nuovaRegola.id =  "OpenLayers.Rule_" + classeCorrente;
			panel.rules.push(nuovaRegola);
			trovataAlmenoUnaClasse = true;
			classeCorrente = classeCorrente + 1;
			this.fireEvent("ruleadded", nuovaRegola);
		}// FINE - Per ogni classe generata

		if(!trovataAlmenoUnaClasse)
		{
			Ext.MessageBox.show({
				title: 'Errore',
				msg: "Impossibile generare delle classi per con le impostazioni correnti",
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.ERROR
			});
			Ext.getBody().unmask();
			return;
		}
		
		this.sldManager.saveSld(this.currentLayer, function(){
			this.repaint();
		}, this);
				
		this.saving = false;
		
		this.repaint();
		panel.update();
		
		//this.currentLayer.display(false);
		//this.currentLayer.clearGrid();
		//this.currentLayer.mergeNewParams({'random':Math.random()});
		//this.currentLayer.redraw(true);
		//this.currentLayer.display(true);
		
		if(this.finestraGenerazioneStileDlg)
		{
			this.finestraGenerazioneStileDlg.close();
			this.finestraGenerazioneStileDlg.destroy();
		}
		
		
	},
	
	generaStiliFailure: function (){
		Ext.MessageBox.show({
			title: 'Errore',
			msg: "Errore nella generazione della stilizzazione",
			buttons: Ext.MessageBox.OK,
			icon: Ext.MessageBox.ERROR
		});
	}
});

/**
 * Plugin per la gestione di richieste e operazioni 
 * che coinvolgono la form codeless.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.ToloCodelessManager', {
	
	extend: 'Ext.util.Observable',
	
	id: "cl_manager",
	
	/**
	 * @cfg {String} TOLOMEOServer
	 * URL di base del contesto di Tolomeo.
	 */
	TOLOMEOServer: null,
	
	/**
	 * @cfg {String} TOLOMEOContext
	 * Contesto di Tolomeo.
	 */
	TOLOMEOContext: null,
	
	/**
	 * @property {TolomeoExt.ToloMapAPIExt} mapApiExt
	 * Oggetto di controllo della mappa.
	 */
	
	/**
	 * @cfg {Ext.data.Store} store
	 * Store dei dati su cui agisce il manager.
	 */
	
	/**
	 * @property {String} updateDialogTitle
	 * Titolo da mostrare per la dialog di aggiornemnto dati.
	 */
    updateDialogTitle: "Aggiornamento",
    
	/**
	 * @property {String} updateDialogText
	 * Testo da mostrare all'interno della dialog di aggiornemnto dati.
	 */
    updateDialogText: "Non sono presenti dati da aggiornare",
    
	/**
	 * @property {String} deleteDialogTitle
	 * Titolo da mostrare per la dialog di cancellazione dati.
	 */
    deleteDialogTitle: "Cancellazione",
    
	/**
	 * @property {String} deleteDialogText
	 * Testo da mostrare all'interno della dialog di cancellazione dati.
	 */
    deleteDialogText: "Procedere con la cancellazione dei dati?",

	/**
     * Crea un nuovo TolomeoExt.ToloCodelessManager.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	constructor: function(config) {
		this.callParent(arguments);
		
		Ext.apply(this, config);
		
		this.addEvents(
	        /**
			 * @event
			 * Lanciato alla fallimento della richiesta.
			 */
			"loaddatafailure",
	        /**
			 * @event
			 * Lanciato prima di effettuare la richiesta di aggiornamento dati.
			 */
			"beforeupdatedata",
	        /**
			 * @event
			 * Lanciato prima di effettuare la richiesta di caricamento dati.
			 */
			"beforeloaddata",
	        /**
			 * @event
			 * Lanciato al termine della richiesta di caricamento se questa è andata a buon fine.
			 */
			"loaddata",
	        /**
			 * @event
			 * Lanciato al termine della richiesta di aggiornamento se questa è andata a buon fine.
			 */
			"updatedata",
	        /**
			 * @event
			 * Lanciato prima di effettuare la richiesta di cancellazione dei dati.
			 */
			"beforedeletedata",
	        /**
			 * @event
			 * Lanciato al termine della richiesta di cancellazione se questa è andata a buon fine.
			 */
			"deletedata",
	        /**
			 * @event
			 * Lanciato prima di effettuare la richiesta di creazione di una feature.
			 */
			"beforecreatedata",
	        /**
			 * @event
			 * Lanciato al termine della richiesta di creazione di una nuova feature se questa è andata a buon fine.
			 */
			"createdata",
	        /**
			 * @event
			 * Lanciato a seguito della selezione di una feature in mappa.
			 */
			"objectselected",
	        /**
			 * @event
			 * Lanciato a seguito del cambio di modalità di funzionamento (edit, view o new)
			 */
			"changemode",
	        /**
			 * @event
			 * Lanciato a seguito del ripristino dello store ai valori iniziali
			 */
			"restore"
		);	
	},
	
	/**
     * Imposta l'oggetto di controllo della mappa per intercettare le 
     * operazioni utente (eventi tolomeo) e procedere con la gestione della form.
     * @param {TolomeoExt.ToloMapAPIExt} mapApiExt oggetto di controllo della mappa.
     */
	setMapApiExt: function(mapApiExt){
		this.mapApiExt = mapApiExt;
		this.mapApiExt.on({
			codelessaction: function(eventoLayer, tipoEvento, object){
				this.selection = object;
				
				switch(tipoEvento){
					case 0: // Identify
						// /////////////////////////////////////////////
						// Caricamento di tutte le informazioni al 
						// fine di preparare la griglia per la modalità
						// di visualizzazione
						// /////////////////////////////////////////////
						var fparams = {
							codTPN: this.selection.codTPN,
							command: "view",
							IDTPN: this.selection.key
						}; 
						
						this.loadViewMode(fparams);
						break;
					case 1: // Delete
			    		Ext.MessageBox.confirm(
		    				this.deleteDialogTitle, 
		    				this.deleteDialogText, 
		    				function(btn){
			    			   if(btn === 'yes'){
									// ///////////////////////////////////////
									// Cancella l'oggetto selezionato
									// ///////////////////////////////////////
									var fparams = {
										codTPN: this.selection.codTPN,
										command: "delete",
										IDTPN: this.selection.key
									}; 
									
									this.cancel(fparams);
			    			   }
		    				}, 
		    				this
						);
						break;
					case 3: // Edit
						// /////////////////////////////////////////////
						// Caricamento di tutte le informazioni al 
						// fine di preparare la griglia per la modalità 
						// di modifica
						// /////////////////////////////////////////////
						var fparams = {
							codTPN: this.selection.codTPN,
							command: "edit",
							IDTPN: this.selection.key
						}; 
						
						this.loadEditMode(fparams);
						break;
					case 4: // New
						// ////////////////////////////////////////////
						// Caricamento di tutte le informazioni al 
						// fine di preparare la griglia per l'aggiunta 
						// di un nuovo elemento 
						// ////////////////////////////////////////////
						if(!this.selection){
							this.selection = this.mapApiExt.geoCoordField.getValue();
							this.selection = Ext.decode(this.selection);
						}
						
						var fparams = {
							codTPN: this.selection.codTPN,
							command: "edit"
						}; 
						
						this.loadEditMode(fparams, true);
						break;
				}
			},	
			onObjectSelect: function(){
				this.fireEvent("objectselected");
			},
			scope: this
		});
	},
	
	/**
     * Imposta la modalità  di funzionamento del manager.
     * @param {String} mode Modalità  di configurazione (view, edit, new).
     * @param {Ext.Data.Store} store Store da usare per componenti di tipo UI
     */
	setMode: function(mode, store){
		this.store = store;
		this.fireEvent("changemode", mode, store);
	},
	
	/**
     * Handler invocato in caso di fallimento della richiesta Ajax.
     * @param {Ext.Data.Store} store Oggetto rappresentante lo store dei dati. 
     *
     */
	doAjaxFailure: function (store) {
		this.fireEvent("loaddatafailure", store);
    },
    
    /**
     * Metodo di caricamento dello store delle features per la modalità 'view'.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    loadViewMode: function(fparams){       	
    	this.fireEvent("beforeloaddata");
    	
		this.request(
			fparams,
			"GET",
    		function(results, store){
				this.setMode("view", store);
    			this.fireEvent("loaddata", store);
    		},
    		this.doAjaxFailure,
    		this
		);
    },
    
    /**
     * Metodo di caricamento dello store delle features per la modalità 'edit'.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    loadEditMode: function(fparams, isNew){       	
    	this.fireEvent("beforeloaddata");
    	
		this.request(
			fparams,
			"GET",
    		function(results, store){
				var mode = "edit";
				// //////////////////////////////////////////////////////////////
				// Controlla se stiamo cercando di creare un nuovo elemento o no 
				// //////////////////////////////////////////////////////////////
				if(isNew){
					mode = "new";
				}
				
				this.setMode(mode, store);
    			this.fireEvent("loaddata", store);
    		},
    		this.doAjaxFailure,
    		this
		);
    },
    
    /**
     * Metodo di aggiornamento dei dati.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    update: function(fparams){
    	this.fireEvent("beforeupdatedata");
    	
    	var updatedRecords = this.store.getUpdatedRecords();
    	
    	if(updatedRecords.length > 0){
    		var params = Ext.applyIf(
				{
					codTPN: this.selection.codTPN,
					command: "update",
					IDTPN: this.selection.key
				}, 
				fparams
			); 
    		
    		var data = "[";
    		for(var i=0; i<updatedRecords.length; i++){
    			var record = updatedRecords[i];
    			var values = record.data;
    			data += Ext.encode(values);
    			
    			if(i+1 != updatedRecords.length){
    				data += ",";
    			}
    		}
    		data += "]";
    		
    		params.data = data;
    		
    		this.request(
				params,
				"POST",
				function(results, store){
					// /////////////////////////////////////////////////
					// Ridisegna il layer per aggiornare lo stato di 
					// eventiali etichette che sono state modificate 
					// durante la procedura di aggiornamento.
					// /////////////////////////////////////////////////
					this.mapApiExt.viewer.pluginRefreshMap();
					
					this.setMode("edit", store);
	    			this.fireEvent("updatedata", store);
	    		},
	    		this.doAjaxFailure,
	    		this
    		);
    	}else{
            Ext.Msg.show({
                title: this.updateDialogTitle,
                msg: this.updateDialogText,
                buttons: Ext.Msg.OK,
                icon: Ext.MessageBox.INFO
            }); 
    	}
    },
    
    /**
     * Metodo di cancellazione dei dati.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    cancel: function(fparams){
    	this.fireEvent("beforedeletedata");
    	
		this.request(
			fparams,
			"POST",
    		function(results){
				if(results){
					// /////////////////////////////////////////////////
					// Ridisegna il layer per aggiornare lo stato di 
					// eventiali etichette che sono state modificate 
					// durante la procedura di aggiornamento.
					// /////////////////////////////////////////////////
					this.mapApiExt.viewer.pluginRefreshMap();
					
					this.fireEvent("deletedata", results);
					
					// ////////////////////////////////////
					// Informa l'utente che l'operazione è 
					// stata completata con successo.
					// ////////////////////////////////////
		            Ext.Msg.show({
		                title: this.deleteDialogTitle,
		                msg: results[0].data.Descrizione,
		                buttons: Ext.Msg.OK,
		                icon: Ext.MessageBox.INFO
		            }); 
				}			
    		},
    		this.doAjaxFailure,
    		this
		);
    },
    
    /**
     * Metodo per la raccolta dati al fine di creare un nuovo elemento.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    create: function(fparams){
    	this.fireEvent("beforecreatedata");
    	var updatedRecords = this.store.getUpdatedRecords();
    	
    	if(updatedRecords.length > 0){
    		var params = Ext.applyIf(
				{
					codTPN: this.selection.codTPN,
					command: "new",
					SRID: this.selection.SRID,
					geometry: this.selection.geometry
				}, 
				fparams
			); 
    		
    		var data = "[";
    		for(var i=0; i<updatedRecords.length; i++){
    			var record = updatedRecords[i];
    			var values = record.data;
    			data += Ext.encode(values);
    			
    			if(i+1 != updatedRecords.length){
    				data += ",";
    			}
    		}
    		data += "]";
    		
    		params.data = data;
    		
    		this.request(
				params,
				"POST",
				function(results, store){
					// /////////////////////////////////////////////////
					// Ridisegna il layer per aggiornare lo stato di 
					// eventiali etichette che sono state modificate 
					// durante la procedura di aggiornamento.
					// /////////////////////////////////////////////////
					this.mapApiExt.viewer.pluginRefreshMap();
					
					this.setMode("edit", store);
	    			this.fireEvent("createdata", store);
	    		},
	    		this.doAjaxFailure,
	    		this
    		);
    	}else{
            Ext.Msg.show({
                title: this.updateDialogTitle,
                msg: this.updateDialogText,
                buttons: Ext.Msg.OK,
                icon: Ext.MessageBox.INFO
            }); 
    	}
    },
    
    /**
     * Metodo di gestione della richiesta Ajax.
     */    
    request: function(params, method, success, failure, scope){
    	var submitOpt = {
    		url: this.TOLOMEOServer + this.TOLOMEOContext + '/LayerItemServlet',
    		method: method,
    		params: params,
    		success: success,
    		failure: failure ? failure : this.doAjaxFailure,
    		scope: scope
    	};
    	
		new TolomeoExt.ToloCrossAjax().request(submitOpt);
    },
    
    /**
     * Restituisce i metadati della richiesta.
     */  
    getRequestMetadata: function(store){
    	var gridStore = store || this.store;
		var proxy = gridStore.getProxy();
		var reader = proxy.getReader();
		var metadata = reader.metaData;
		
		return metadata;
    },
    
    /**
     * Ripristina i dati nello store eliminando le modifiche.
     *
     */
    restore: function(){
    	this.store.rejectChanges();
    	this.fireEvent("restore");
    }
	
});
/**
 * Strumento di visualizzazione ed editing dei dati configurabile, 
 * senza la necessità di scrivere codice (“codeless”) per Tolomeo. 
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.ToloCodeLessPanel', {

	extend: 'Ext.Panel',

	/**
	 * @cfg {Object} paramsJS
	 * Configurazioni specifiche del file di preset.
	 */
	paramsJS: null,

	/**
	 * @cfg {String} TOLOMEOServer
	 * URL di base del contesto di Tolomeo.
	 */
	TOLOMEOServer: null,

	/**
	 * @cfg {String} TOLOMEOContext
	 * Contesto di Tolomeo.
	 */
	TOLOMEOContext: null,
	
	/**
	 * @cfg {String} viewFieldSetTitle
	 * Testo del field set in modalità  di visualizzazione.
	 */
	viewFieldSetTitle: null, //"Visualizzazione Dati",
	
	/**
	 * @cfg {String} editFieldSetTitle
	 * Testo del field set in modalità  di modifica.
	 */
	editFieldSetTitle: "Modifica Dati",
	
	/**
	 * @cfg {String} newFieldSetTitle
	 * Testo del field set in modalità  di nuovo elemento.
	 */
	newFieldSetTitle: "Nuovo Elemento",
	
	/**
	 * @cfg {String} applyModsTooltip
	 * Testo da mostrare per il tootlip del pulsante di salvataggio delle modifiche.
	 */
	applyModsTooltip: "Applica Modifiche",
	
	/**
	 * @cfg {String} resetTooltip
	 * Testo da mostrare per il tootlip del pulsante di reset delle modifiche.
	 */
	resetTooltip: "Reimposta Campi",
	
	/**
	 * @cfg {String} restoreBoxTitle
	 * Titolo da mostrare per la dialog di conferma per il reset delle modiche.
	 */
	restoreBoxTitle: "Ripristino", 
	
	/**
	 * @cfg {String} restoreBoxText
	 * Testo da mostrare per la dialog di conferma per il reset delle modiche.
	 */
	restoreBoxText: "Procedere con il ripristino dei dati allo stato iniziale?", 

	/**
	 * @cfg {String} saveBoxTitle
	 * Titolo da mostrare per la dialog di conferma per il salvataggio delle modiche.
	 */
	saveBoxTitle: "Salvataggio", 
	
	/**
	 * @cfg {String} saveBoxText
	 * Testo da mostrare per la dialog di conferma per il salvataggio delle modiche.
	 */
	saveBoxText: "Procedere con il salvataggio?", 
	
	/**
	 * @cfg {String} missingIDTPNTitle
	 * Titolo da mostrare in fase di salvataggio se il campo relativo all'IDTPN non risulta valorizzato.
	 */
	missingIDTPNTitle: "IDTPN Mancante",
	
	/**
	 * @cfg {String} missingIDTPNText
	 * Testo da mostrare per la dialog di avviso in fase di salvataggio se il campo relativo all'IDTPN non risulta valorizzato.
	 */
	missingIDTPNText: "Il campo relativo all'IDTPN deve essere inserito prima di procedere",
	
	/**
     * @cfg {Object} autoCompleteCfg [autoCompleteCfg="{}"]
	 * Stabilisce la configurazione da usare per la funzionalità di autocompletamento.
	 *
	 * @example
	 * autoCompleteCfg: {
	 *  	url: 'http://localhost:8080/tolomeobinj/UniqueValueServlet',
	 *		pageSize: 10
	 * }
     */
    autoCompleteCfg: {},
    
	/**
	 * @cfg {Number} pageSize
	 * Indica il massimo numero di elementi per pagina per la combo di autocompletamento usato per 
	 * gli editor di FKs.
	 */
    pageSize: 10,
    
	/**
	 * @cfg {String} dateFormat [dateFormat="c"]
	 * Indica il formato per i campi editor di tipo "data" (il valore predefinito e 'c' che corrispondea 'ISO 8601 date').
	 */
    dateFormat: "c",
    
	/**
	 * @cfg {Array} dateFormats
	 * Formati di data ExtJS disponibili per gli editor di campi data di questo componente.
	 */
    dateFormats: [
        {java: "yyyy-MM-dd'T'HH:mm:ss", ext: "Y-m-d H:i:s"},
        {java: "yyyy-MM-dd", ext: "Y-m-d"},
        {java: "dd-MM-yyyy", ext: "d-m-Y"},
        {java: "dd-MM-yyyy", ext: "d-m-Y"},
        {java: "MM-dd-yyyy", ext: "m-d-Y"}		
    ],
    
	/**
	 * @property {TolomeoExt.ToloCodelessManager} codelessManager
	 * Gestore delle operazioni del componente.
	 */
	
	/**
	 * @property {Ext.grid.Panel} propertyGrid
	 * Griglia ExtJs per la presentazione e la modifica dei dati richiesti.
	 */
	
	/**
	 * @property {Ext.grid.Panel} decodeGrid
	 * Griglia ExtJs per la presentazione e la modifica dei dati relativi alle tabelle di decodifica.
	 */
	
	/**
	 * @property {String} mode
	 * Stringa che indica la modalità di funzionamento corrente della form.
	 */

	/**
     * Inizializza un nuovo TolomeoExt.ToloCodeLessPanel.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	initComponent: function(config){
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
		// ///////////////////// //
		// FEATURE DI SELEZIONE  //
		// ///////////////////// //
		
		var gridStore = Ext.create('Ext.data.Store', {
		    fields: ["name", "nl", "type", "editable", "validation", "value", "format"],
		    proxy: {
		        type: 'memory',
		        reader: {
		            type: 'json',
		            root: 'rows'
		        }
		    }
		});
		
	    var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
	    	clicksToEdit : 1,
	        autoCancel: false
	    });
	    
	    var me = this;
		this.propertyGrid = Ext.create('Ext.grid.Panel', {
			margin: "5 0 5 0",
			hideHeaders: true,
			store: gridStore,
		    columns: [
              {
            	  header: 'Nome',  
            	  dataIndex: 'name',
            	  tdCls: 'codelessnomecampo'
        //    	  flex: 50/100
              },              {
            	  header: 'NL',  
            	  dataIndex: 'nl',
                  hidden: true
              },
              {
            	  header: 'Tipo',  
            	  dataIndex: 'type',
                  hidden: true
              },
              {
            	  header: 'Editabile',  
            	  dataIndex: 'editable',
                  hidden: true
              },
              {
            	  header: 'Validation',  
            	  dataIndex: 'validation',
                  hidden: true
              }, 
              {
            	  header: 'Valore', 
            	  dataIndex: 'value',
            	  tdCls: 'codelessvalore',
            	  //flex: 50/100,
            	  renderer: function(value, metaData, record, rowIndex, store, view){
            		  var type = record.get("type"); 
	  	              switch (type) {
	                    case "java.util.Date":
	                    case "java.util.Calendar":
	                    	var format =  me.getDateFormat();
	                    	var date = Ext.util.Format.date(value, format);
	                    	return date;
	                        break;
	                    default:
	                    	var v;
	                    	if(value.value || value.value == ""){
	                    		// /////////////////////////////////////////
	                    		// Questo imposta il valore della combo di 
	                    		// autocompletamento (usato per le FK)
	                    		// /////////////////////////////////////////
	                    		v = value.value;
	                    	}else{
	                    		v = value;
	                    	}
	                		return v;
	  	              }
            	  },
            	  getEditor: function(record) {
            		  	var editable = record.get("editable");
            		  	if(editable){
                		    var type = record.get("type");
                		    
                		    // ///////////////////////////////////////////////////////
                		    // Imposta la regola di validazione usando la Regular 
                		    // Expression se esiste.
                		    // ///////////////////////////////////////////////////////
                		    var validation = record.get("validation");
                		    var baseConfig = {};
                		    if(validation != "undefined"){
                		    	var valueTest = new RegExp(validation);
                		    	baseConfig = {
    	                            validator: function(value) {
    	                                return valueTest.test(value) ? true : "Valore campo non valido";
    	                            }
    	                		}
                		    }
                		    
    	  	                switch (type) {
    		                    case "java.util.Date":
    		                		var format =  me.getDateFormat();
    		                		var config = Ext.apply({
		            		            allowBlank: false,
		            		            format: format
		            		        }, baseConfig);
    		                		
    		            		    return Ext.create('Ext.grid.CellEditor', { 
    		            		        field: Ext.create('Ext.form.field.Date', config)
    		            		    });
    		                        break;
    		                    case "java.lang.Boolean":
    		                		var config = Ext.apply({
		            		            allowBlank: false
		            		        }, baseConfig);
    		                		
    		            		    return Ext.create('Ext.grid.CellEditor', { 
    		            		        field: Ext.create('Ext.form.field.Checkbox', config)
    		            		    });
    		                        break;
    		                    case "net.sf.json.JSONObject":
    		                    	var value = record.get("value");
    		                    	if(value.codTPN){
    		                            var uniqueValuesStore = new TolomeoExt.data.ToloUniqueValuesStore({
    		                                pageSize: me.autoCompleteCfg.pageSize || me.pageSize,
    		                    			TOLOMEOServer: me.TOLOMEOServer,
    		                    			TOLOMEOContext: me.TOLOMEOContext
    		                            });
    		                            
    		                            me.initUniqueValuesStore(uniqueValuesStore, value.codTPN, value.property);
    		                            
        		                		var config = {
    		                                queryMode: "remote",
    		                                store: uniqueValuesStore,
    		                                pageSize: me.autoCompleteCfg.pageSize || me.pageSize,
    		                                typeAhead: false,
    		                                forceSelection: false,
    		                                remoteSort: true,
    		                                triggerAction: "all",
    		                                allowBlank: false,
    		                                displayField: "value",
    		                                valueField: "value",
    		                                minChars: 1,
       		                                matchFieldWidth: true,
       		                                listConfig:{
       		                                	minWidth: 100,
       		                                	width: 200
       		                                },
		       		                        listeners: {
	       		                              beforequery: function(evt) {
	       		                                  evt.combo.store.baseParams.start = 0;
	       		                                  evt.combo.store.baseParams.query = evt.combo.getValue();
	       		                              },
	       		                              scope: me
		       		                        }
    		                            };
        		                		
        		            		    return Ext.create('Ext.grid.CellEditor', { 
        		            		        field: Ext.create('TolomeoExt.widgets.form.ToloUniqueValuesCombo', config)
        		            		    });
        		            		    
        		            		    break;
    		                    	}
    		                    case "java.lang.String":
    		                		var config = Ext.apply({
		            		            allowBlank: false
		            		        }, baseConfig);
    		                		
    		            		    return Ext.create('Ext.grid.CellEditor', { 
    		            		        field: Ext.create('Ext.form.field.Text', config)
    		            		    });
    		                        break;
    		                    default:
    		                		var config = Ext.apply({
		            		            allowBlank: false
		            		        }, baseConfig);
    		                    
    		            		    return Ext.create('Ext.grid.CellEditor', { 
    		            		        field: Ext.create('Ext.form.field.Number', config)
    		            		    });
    		                }
            		  	}
            	  }
              }
            ],
            plugins: [cellEditing],
			listeners: {
				scope: this,
				edit: function(grid, cell){
					this.setBtnStatus(true);
				},
				beforeEdit: function(editor, context, eOpts){
            		// /////////////////////////////////////////
            		// Questo imposta il valore della combo di 
            		// autocompletamento (usato per le FK)
            		// /////////////////////////////////////////
					var value = editor.context.value;
					if(value.value || value.value == ""){
						editor.context.value = value.value;
					}
				}
			},
			// Autosize delle colonne
			viewConfig: {
		        listeners: {
		            refresh: function(dataview) {
		            	setTimeout(function(){ 
		            		dataview.panel.columns[0].autoSize(); 
		            		}, 1000);
		            	
		                /*Ext.each(dataview.panel.columns, function(column) {
		                    if (column.autoSizeColumn === true)
		                        column.autoSize();
		                })*/
		            	
		            }
		        }
		    }
		
		});
		
		
		this.fieldSet = Ext.create('Ext.form.FieldSet', {
			title: "",
			border: 0,
			anchor: "-1",
			//autoWidth: true,
			//autoHeight: true,
			hidden: true,
			items:[this.propertyGrid]
		});
		
		// ///////////////////// //
		// FORM DI PRESENTAZIONE //
		// ///////////////////// //
		
		this.formPanel = Ext.create('Ext.form.Panel', {
		    border: 0,	
		    cls: "tolomeo-formcodelessbbar",
		    layout: 'anchor',
		    hidden: true,
		    defaults: {
		        anchor: '100%'
		    },
		    items: [
		        this.fieldSet
		    ],
		    buttons: [{
		    	xtype: "button", 
		    	minWidth: '20',
		    	ref: "linkButton",
		    	text: "Il mio testo",
		    	href: "http://mappe.regione.toscana.it",
		    	hrefTarget: "_blank",
		    	disabled: false,
		    	hidden: true,
		    	scope: this
		    },{ 
		    	xtype: 'tbfill'
		    },{
		    	xtype: "button", 
		    	minWidth: '20',
		    	ref: "resetButton",
		    	tooltip: this.resetTooltip,
		    	iconCls: "reset",
		    	disabled: true,
		    	hidden: true,
		    	scope: this,
		    	handler: function(button){
		    		Ext.MessageBox.confirm(
	    				this.restoreBoxTitle,
	    				this.restoreBoxText,
	    				function(btn){
		    			   if(btn === 'yes'){
		    				   this.codelessManager.restore();
		    			   }
	    				}, 
	    				this
    				);
		    	}
		    },{
		    	xtype: "button", 
		    	minWidth: '20',
		    	ref: "saveButton",
		    	tooltip: this.applyModsTooltip,
		    	iconCls: "save",
		    	disabled: true,
		    	hidden: true,
		    	scope: this,
		    	handler: function(button){
		    		
		    		// ////////////////////////////////////////////////
		    		// La Griglia ExtJS esegue già il controllo di 
		    		// validità di un campo ripristinando il valore 
		    		// precedente nel caso in cui tale valore inserito 
		    		// sia invalido.
		    		// ////////////////////////////////////////////////
		    		
		    		Ext.MessageBox.confirm(
	    				this.saveBoxTitle,
	    				this.saveBoxText, 
	    				function(btn){
		    			   if(btn === 'yes'){
		    				   if(this.mode == "new"){
		    					   var record = this.propertyGrid.getStore().findRecord("nl", "NL_IDTPN");
		    					   var recordValue = record.get("value");
		    					   var editable = record.get("editable");
		    					   
		    					   // //////////////////////////////////////////////////////////
		    					   // Si controlla se il campo relativo all'IDTPN è valorizzato 
		    					   // nel caso in cui debba esserlo secondo le impostazioni del 
		    					   // server.
		    					   // //////////////////////////////////////////////////////////
		    					   if((!recordValue || recordValue == "") && editable){
		    				            Ext.Msg.show({
		    				                title: this.missingIDTPNTitle,
		    				                msg: this.missingIDTPNText,
		    				                buttons: Ext.Msg.OK,
		    				                icon: Ext.MessageBox.WARNING
		    				            }); 
		    					   }else{
		    						   this.codelessManager.create();
		    					   }
		    				   }else{
		    					   this.codelessManager.update();
		    				   }
		    			   }
	    				}, 
	    				this
    				);
		    	}
		    }]
		});
		
		this.callParent();
		this.buildManager(gridStore);
		this.codelessManager.on("loaddata", this.dataLoaded, this);
		
		this.add([this.formPanel]);
	},
	
	dataLoaded: function()
	{
		
		var linkButton = this.formPanel.query("button[ref=linkButton]")[0];
		var metadata = this.codelessManager.getRequestMetadata();
		
		if (metadata.layerURL != null && metadata.layerURL != "") {
			linkButton.setText(metadata.layerURLLabel);
			linkButton.setHref(metadata.layerURL);
			linkButton.show();
		} else {
			linkButton.hide();
		}
	},
	
	/**
     * Restituisce il formato data da usare per l'editor. 
     * 
     */
	getDateFormat: function(){
		var metadata = this.codelessManager.getRequestMetadata();
    	
		// ////////////////////////////////////////////////////////////
		// Usa ISO-8601 come formato data se non viene trovata nessuna 
		// corrispondenza attraverso l'invocazione di getExtDateFormat.
		// ////////////////////////////////////////////////////////////
		var format =  metadata ? (this.getExtDateFormat(metadata.dateFormat) || this.dateFormat) : this.dateFormat;
		return format;
	},
	
	/**
     * Converte il formato Data fornito dal server sulla base di quelli disponibili 
     * in configurazione. 
     * @param {String} format formato da usare per il campo editor della data.
     * 
     */
	getExtDateFormat: function(format){
		var date_format;
		
		if(format){
			for(var i=0; i<this.dateFormats.length; i++){
				var df = this.dateFormats[i];
				if(format == df.java){
					date_format = df.ext;
					break;
				}
			}
		}
		
		return date_format;
	},
	
	/**
     * Crea il componente Ext destinato a contenere il valore delle proprietà .
     * @param {TolomeoExt.data.ToloUniqueValuesStore} store Store della combo box di auto completamento.
     * @param {String} url Url del servizio remoto di auto completamento.
     * @param {String} layerName codTPN da usare com eparametro della richiesta.
     * @param {String} fieldName Nome della proprietà di cui ritornare i suggerimenti.
     * 
     */
    initUniqueValuesStore: function(store, layerName, fieldName) {
        var params = {
            inputs: {
            	featureTypeName: layerName,
                fieldName: fieldName
            },
            start: 0,
            limit: this.autoCompleteCfg.pageSize || this.pageSize
        };
        
        store.setParams(params);
    },
	
	/**
     * Crea il Gestore delle richieste se assente e registra gli eventi necessari.
     * @param {Ext.Data.Store} store Store che il Manager deve gestire.
     */
	buildManager: function(store){
		this.codelessManager = Ext.create('TolomeoExt.ToloCodelessManager', {
			TOLOMEOServer : this.TOLOMEOServer,
			TOLOMEOContext: this.TOLOMEOContext,
			store: store
		});
		
		this.codelessManager.on({
			changemode: function(mode, store){
				this.setFormMode(mode, store);
			},
			objectselected: function(){
				this.hideForm();
			},
			restore: function(){
				this.setBtnStatus(false);
			},
			updatedata: function(){
				this.setBtnStatus(false);
			},
			createdata: function(){
				this.setBtnStatus(false);
			},
			deletedata: function(){
				this.setBtnStatus(false);
				this.setBtnVisibility(false);
				this.hideForm();
			},
			scope: this
		});
	},
	
	/**
     * Imposta sul Manager l'oggetto di controllo della mappa per intercettare le 
     * operazioni utente (eventi tolomeo) e procedere con la gestione della form.
     * @param {TolomeoExt.ToloMapAPIExt} mapApiExt Oggetto di controllo della mappa.
     */
	setMapApiExt: function(mapApiExt){
		this.codelessManager.setMapApiExt(mapApiExt);
	},
	
	/**
     * Imposta la modalità  con cui configurare la form.
     * @param {String} mode Modalità  di configurazione (view, edit, new).
     * @param {Ext.Data.Store} store Store con gui configurare la griglia dei dati
     */
	setFormMode: function(mode, store){
		this.mode = mode;
		
		if(mode == "view"){
			this.fieldSet.setTitle(this.viewFieldSetTitle);	
			this.setBtnVisibility(false);
		}else if(mode == "edit"){
			this.fieldSet.setTitle(this.editFieldSetTitle);
			this.setBtnVisibility(true);
		}if(mode == "new"){
			this.fieldSet.setTitle(this.newFieldSetTitle);
			this.setBtnVisibility(true);
		}
		
		this.propertyGrid.reconfigure(store);
		
		// ///////////////////////////////////////////////////////////
		// Se sono stati forniti dal server valori di default per i 
		// campi della form allora le rispettive celle delle griglia 
		// devono essere marcate come valorizzate (DIRTY).
		// ///////////////////////////////////////////////////////////
		if(mode == "new"){
			var gridStore = this.propertyGrid.getStore();
			var isDirty = false;
			gridStore.each(function(r) {
				var value = r.get("value");
				var nl = r.get("nl");
                if (value != "" && nl != "") {
                   r.setDirty();
                   isDirty = true;
                }
            });
            
			// Se alcuni campi sono valorizzati abilitare i pulsanti della toolbar.
			this.setBtnStatus(isDirty);
		}
		
		this.showForm();
	},
	
	/**
     * Visualizza la form dei dati.
     * @param {boolean} withDecode Stabilisce se visualizzare anche la form di decodifica.
     */
	showForm: function(){
		this.fieldSet.show();		
		this.formPanel.show();
	},
	
	/**
     * Nasconde la form dei dati.
     */
	hideForm: function(){
		this.fieldSet.hide();
		this.formPanel.hide();
	},
	
	/**
     * Imposta lo stato dei pulsanti presenti nella toolbar del pannello di modifica.
     */
	setBtnStatus: function(enabled){
		if(enabled === true){
			this.formPanel.query("button[ref=resetButton]")[0].enable();
			this.formPanel.query("button[ref=saveButton]")[0].enable();
		}else{
			this.formPanel.query("button[ref=resetButton]")[0].disable();
			this.formPanel.query("button[ref=saveButton]")[0].disable();
		}
	},
	
	/**
     * Imposta la visibilità dei pulsanti presenti nella toolbar del pannello di modifica.
     */
	setBtnVisibility: function(visible){
		if(visible === true){
			this.formPanel.query("button[ref=saveButton]")[0].show();
			this.formPanel.query("button[ref=resetButton]")[0].show();
		}else{
			this.formPanel.query("button[ref=saveButton]")[0].hide();
			this.formPanel.query("button[ref=resetButton]")[0].hide();
		}
	}
    
});

/**
 * Plugin per la gestione di richieste e operazioni 
 * che coinvolgono la form codeless.
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.ToloViewCodelessManager', {
	
	extend: 'Ext.util.Observable',
	
	id: "cl_manager",
	
	/**
	 * @cfg {String} TOLOMEOServer
	 * URL di base del contesto di Tolomeo.
	 */
	TOLOMEOServer: null,
	
	/**
	 * @cfg {String} TOLOMEOContext
	 * Contesto di Tolomeo.
	 */
	TOLOMEOContext: null,
	
	/**
	 * @property {TolomeoExt.ToloMapAPIExt} mapApiExt
	 * Oggetto di controllo della mappa.
	 */
	
	/**
	 * @cfg {Ext.data.Store} store
	 * Store dei dati su cui agisce il manager.
	 */
	
	/**
	 * @property {String} updateDialogTitle
	 * Titolo da mostrare per la dialog di aggiornemnto dati.
	 */
    updateDialogTitle: null,
    
	/**
	 * @property {String} updateDialogText
	 * Testo da mostrare all'interno della dialog di aggiornemnto dati.
	 */
    updateDialogText: null,
    
	/**
	 * @property {String} deleteDialogTitle
	 * Titolo da mostrare per la dialog di cancellazione dati.
	 */
    deleteDialogTitle: null,
    
	/**
	 * @property {String} deleteDialogText
	 * Testo da mostrare all'interno della dialog di cancellazione dati.
	 */
    deleteDialogText: null,

	/**
     * Crea un nuovo TolomeoExt.ToloViewCodelessManager.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	constructor: function(config) {
		
	    updateDialogTitle = ToloI18n.getMsg("ToloViewCodelessManager.updateDialogTitle");
	    updateDialogText = ToloI18n.getMsg("ToloViewCodelessManager.updateDialogText");
	    deleteDialogTitle = ToloI18n.getMsg("ToloViewCodelessManager.deleteDialogTitle");
	    deleteDialogText = ToloI18n.getMsg("ToloViewCodelessManager.deleteDialogText");

		this.callParent(arguments);
		
		Ext.apply(this, config);
		
		this.addEvents(
	        /**
			 * @event
			 * Lanciato alla fallimento della richiesta.
			 */
			"loaddatafailure",
	        /**
			 * @event
			 * Lanciato prima di effettuare la richiesta di aggiornamento dati.
			 */
			"beforeupdatedata",
	        /**
			 * @event
			 * Lanciato prima di effettuare la richiesta di caricamento dati.
			 */
			"beforeloaddata",
	        /**
			 * @event
			 * Lanciato al termine della richiesta di caricamento se questa è andata a buon fine.
			 */
			"loaddata",
	        /**
			 * @event
			 * Lanciato al termine della richiesta di aggiornamento se questa è andata a buon fine.
			 */
			"updatedata",
	        /**
			 * @event
			 * Lanciato prima di effettuare la richiesta di cancellazione dei dati.
			 */
			"beforedeletedata",
	        /**
			 * @event
			 * Lanciato al termine della richiesta di cancellazione se questa è andata a buon fine.
			 */
			"deletedata",
	        /**
			 * @event
			 * Lanciato prima di effettuare la richiesta di creazione di una feature.
			 */
			"beforecreatedata",
	        /**
			 * @event
			 * Lanciato al termine della richiesta di creazione di una nuova feature se questa è andata a buon fine.
			 */
			"createdata",
	        /**
			 * @event
			 * Lanciato a seguito della selezione di una feature in mappa.
			 */
			"objectselected",
	        /**
			 * @event
			 * Lanciato a seguito del cambio di modalità di funzionamento (edit, view o new)
			 */
			"changemode",
	        /**
			 * @event
			 * Lanciato a seguito del ripristino dello store ai valori iniziali
			 */
			"restore"
		);	
	},
	
	/**
     * Imposta l'oggetto di controllo della mappa per intercettare le 
     * operazioni utente (eventi tolomeo) e procedere con la gestione della form.
     * @param {TolomeoExt.ToloMapAPIExt} mapApiExt oggetto di controllo della mappa.
     */
	setMapApiExt: function(mapApiExt){
		this.mapApiExt = mapApiExt;

		this.mapApiExt.on({
			viewcodelessaction: function(eventoLayer, object){
				this.selection = object;
				
				// /////////////////////////////////////////////
				// Caricamento di tutte le informazioni al 
				// fine di preparare la griglia per la modalità
				// di visualizzazione
				// /////////////////////////////////////////////
				var fparams = {
					codTPN: this.selection.codTPN,
					command: "view",
					IDTPN: this.selection.key,
					paramPreset: TolomeoExt.Vars.paramsJS.nomePreset
				}; 
				
				this.loadViewMode(fparams);
			},	
			onObjectSelect: function(){
				this.fireEvent("objectselected");
			},
			scope: this
		});
	},
	
	/**
     * Imposta la modalità  di funzionamento del manager.
     * @param {String} mode Modalità  di configurazione (view, edit, new).
     * @param {Ext.Data.Store} store Store da usare per componenti di tipo UI
     */
	setMode: function(mode, store){
		this.store = store;
		this.fireEvent("changemode", mode, store);
	},
	
	/**
     * Handler invocato in caso di fallimento della richiesta Ajax.
     * @param {Ext.Data.Store} store Oggetto rappresentante lo store dei dati. 
     *
     */
	doAjaxFailure: function (store) {
		this.fireEvent("loaddatafailure", store);
    },
    
    /**
     * Metodo di caricamento dello store delle features per la modalità 'view'.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    loadViewMode: function(fparams){       	
    	this.fireEvent("beforeloaddata");
    	
		this.request(
			fparams,
			"GET",
    		function(results, store){
				this.setMode("view", store);
    			this.fireEvent("loaddata", store);
    		},
    		this.doAjaxFailure,
    		this
		);
    },
    
    /**
     * Metodo di caricamento dello store delle features per la modalità 'edit'.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    loadEditMode: function(fparams, isNew){       	
    	this.fireEvent("beforeloaddata");
    	
		this.request(
			fparams,
			"GET",
    		function(results, store){
				var mode = "edit";
				// //////////////////////////////////////////////////////////////
				// Controlla se stiamo cercando di creare un nuovo elemento o no 
				// //////////////////////////////////////////////////////////////
				if(isNew){
					mode = "new";
				}
				
				this.setMode(mode, store);
    			this.fireEvent("loaddata", store);
    		},
    		this.doAjaxFailure,
    		this
		);
    },
    
    /**
     * Metodo di aggiornamento dei dati.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    update: function(fparams){
    	this.fireEvent("beforeupdatedata");
    	
    	var updatedRecords = this.store.getUpdatedRecords();
    	
    	if(updatedRecords.length > 0){
    		var params = Ext.applyIf(
				{
					codTPN: this.selection.codTPN,
					command: "update",
					IDTPN: this.selection.key
				}, 
				fparams
			); 
    		
    		var data = "[";
    		for(var i=0; i<updatedRecords.length; i++){
    			var record = updatedRecords[i];
    			var values = record.data;
    			data += Ext.encode(values);
    			
    			if(i+1 != updatedRecords.length){
    				data += ",";
    			}
    		}
    		data += "]";
    		
    		params.data = data;
    		
    		this.request(
				params,
				"POST",
				function(results, store){
					// /////////////////////////////////////////////////
					// Ridisegna il layer per aggiornare lo stato di 
					// eventiali etichette che sono state modificate 
					// durante la procedura di aggiornamento.
					// /////////////////////////////////////////////////
					this.mapApiExt.viewer.pluginRefreshMap();
					
					this.setMode("edit", store);
	    			this.fireEvent("updatedata", store);
	    		},
	    		this.doAjaxFailure,
	    		this
    		);
    	}else{
            Ext.Msg.show({
                title: this.updateDialogTitle,
                msg: this.updateDialogText,
                buttons: Ext.Msg.OK,
                icon: Ext.MessageBox.INFO
            }); 
    	}
    },
    
    /**
     * Metodo di cancellazione dei dati.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    cancel: function(fparams){
    	this.fireEvent("beforedeletedata");
    	
		this.request(
			fparams,
			"POST",
    		function(results){
				if(results){
					// /////////////////////////////////////////////////
					// Ridisegna il layer per aggiornare lo stato di 
					// eventiali etichette che sono state modificate 
					// durante la procedura di aggiornamento.
					// /////////////////////////////////////////////////
					this.mapApiExt.viewer.pluginRefreshMap();
					
					this.fireEvent("deletedata", results);
					
					// ////////////////////////////////////
					// Informa l'utente che l'operazione è 
					// stata completata con successo.
					// ////////////////////////////////////
		            Ext.Msg.show({
		                title: this.deleteDialogTitle,
		                msg: results[0].data.Descrizione,
		                buttons: Ext.Msg.OK,
		                icon: Ext.MessageBox.INFO
		            }); 
				}			
    		},
    		this.doAjaxFailure,
    		this
		);
    },
    
    /**
     * Metodo per la raccolta dati al fine di creare un nuovo elemento.
     * @param {Object} fparams Oggetto contenente i parametri che saranno usati nella richista. 
     *
     */
    create: function(fparams){
    	this.fireEvent("beforecreatedata");
    	var updatedRecords = this.store.getUpdatedRecords();
    	
    	if(updatedRecords.length > 0){
    		var params = Ext.applyIf(
				{
					codTPN: this.selection.codTPN,
					command: "new",
					SRID: this.selection.SRID,
					geometry: this.selection.geometry
				}, 
				fparams
			); 
    		
    		var data = "[";
    		for(var i=0; i<updatedRecords.length; i++){
    			var record = updatedRecords[i];
    			var values = record.data;
    			data += Ext.encode(values);
    			
    			if(i+1 != updatedRecords.length){
    				data += ",";
    			}
    		}
    		data += "]";
    		
    		params.data = data;
    		
    		this.request(
				params,
				"POST",
				function(results, store){
					// /////////////////////////////////////////////////
					// Ridisegna il layer per aggiornare lo stato di 
					// eventiali etichette che sono state modificate 
					// durante la procedura di aggiornamento.
					// /////////////////////////////////////////////////
					this.mapApiExt.viewer.pluginRefreshMap();
					
					this.setMode("edit", store);
	    			this.fireEvent("createdata", store);
	    		},
	    		this.doAjaxFailure,
	    		this
    		);
    	}else{
            Ext.Msg.show({
                title: this.updateDialogTitle,
                msg: this.updateDialogText,
                buttons: Ext.Msg.OK,
                icon: Ext.MessageBox.INFO
            }); 
    	}
    },
    
    /**
     * Metodo di gestione della richiesta Ajax.
     */    
    request: function(params, method, success, failure, scope){
    	var submitOpt = {
    		url: this.TOLOMEOServer + this.TOLOMEOContext + '/LayerItemViewServlet',
    		method: method,
    		params: params,
    		success: success,
    		failure: failure ? failure : this.doAjaxFailure,
    		scope: scope
    	};
    	
		new TolomeoExt.ToloCrossAjax().request(submitOpt);
    },
    
    /**
     * Restituisce i metadati della richiesta.
     */  
    getRequestMetadata: function(store){
    	var gridStore = store || this.store;
		var proxy = gridStore.getProxy();
		var reader = proxy.getReader();
		var metadata = reader.metaData;
		
		return metadata;
    },
    
    /**
     * Ripristina i dati nello store eliminando le modifiche.
     *
     */
    restore: function(){
    	this.store.rejectChanges();
    	this.fireEvent("restore");
    }
	
});
/**
 * Strumento di visualizzazione ed editing dei dati configurabile, 
 * senza la necessità di scrivere codice (“codeless”) per Tolomeo. 
 *
 * @author Tobia Di Pisa at tobia.dipisa@geo-solutions.it
 */
Ext.define('TolomeoExt.ToloViewCodeLessPanel', {

	extend: 'Ext.Panel',

	/**
	 * @cfg {Object} paramsJS
	 * Configurazioni specifiche del file di preset.
	 */
	paramsJS: null,

	/**
	 * @cfg {String} TOLOMEOServer
	 * URL di base del contesto di Tolomeo.
	 */
	TOLOMEOServer: null,

	/**
	 * @cfg {String} TOLOMEOContext
	 * Contesto di Tolomeo.
	 */
	TOLOMEOContext: null,
	
	/**
	 * @cfg {String} viewFieldSetTitle
	 * Testo del field set in modalit� di visualizzazione.
	 */
	viewFieldSetTitle: null, //"Visualizzazione Dati",
	
	/**
	 * @cfg {String} editFieldSetTitle
	 * Testo del field set in modalità di modifica.
	 */
	editFieldSetTitle: null,
	
	/**
	 * @cfg {String} newFieldSetTitle
	 * Testo del field set in modalità di nuovo elemento.
	 */
	newFieldSetTitle: null,
	
	/**
	 * @cfg {String} applyModsTooltip
	 * Testo da mostrare per il tootlip del pulsante di salvataggio delle modifiche.
	 */
	applyModsTooltip: null,
	
	/**
	 * @cfg {String} resetTooltip
	 * Testo da mostrare per il tootlip del pulsante di reset delle modifiche.
	 */
	resetTooltip: null,
	
	/**
	 * @cfg {String} restoreBoxTitle
	 * Titolo da mostrare per la dialog di conferma per il reset delle modiche.
	 */
	restoreBoxTitle: null, 
	
	/**
	 * @cfg {String} restoreBoxText
	 * Testo da mostrare per la dialog di conferma per il reset delle modiche.
	 */
	restoreBoxText: null, 

	/**
	 * @cfg {String} saveBoxTitle
	 * Titolo da mostrare per la dialog di conferma per il salvataggio delle modiche.
	 */
	saveBoxTitle: null, 
	
	/**
	 * @cfg {String} saveBoxText
	 * Testo da mostrare per la dialog di conferma per il salvataggio delle modiche.
	 */
	saveBoxText: null, 
	
	/**
	 * @cfg {String} missingIDTPNTitle
	 * Titolo da mostrare in fase di salvataggio se il campo relativo all'IDTPN non risulta valorizzato.
	 */
	missingIDTPNTitle: null, 
	
	/**
	 * @cfg {String} missingIDTPNText
	 * Testo da mostrare per la dialog di avviso in fase di salvataggio se il campo relativo all'IDTPN non risulta valorizzato.
	 */
	missingIDTPNText: null, 
	
	/**
     * @cfg {Object} autoCompleteCfg [autoCompleteCfg="{}"]
	 * Stabilisce la configurazione da usare per la funzionalità di autocompletamento.
	 *
	 * @example
	 * autoCompleteCfg: {
	 *  	url: 'http://localhost:8080/tolomeobinj/UniqueValueServlet',
	 *		pageSize: 10
	 * }
     */
    autoCompleteCfg: {},
    
	/**
	 * @cfg {Number} pageSize
	 * Indica il massimo numero di elementi per pagina per la combo di autocompletamento usato per 
	 * gli editor di FKs.
	 */
    pageSize: 10,
    
	/**
	 * @cfg {String} dateFormat [dateFormat="c"]
	 * Indica il formato per i campi editor di tipo "data" (il valore predefinito e 'c' che corrispondea 'ISO 8601 date').
	 */
    dateFormat: "c",
    
	/**
	 * @cfg {Array} dateFormats
	 * Formati di data ExtJS disponibili per gli editor di campi data di questo componente.
	 */
    dateFormats: [
        {java: "yyyy-MM-dd'T'HH:mm:ss", ext: "Y-m-d H:i:s"},
        {java: "yyyy-MM-dd", ext: "Y-m-d"},
        {java: "dd-MM-yyyy", ext: "d-m-Y"},
        {java: "dd-MM-yyyy", ext: "d-m-Y"},
        {java: "MM-dd-yyyy", ext: "m-d-Y"}		
    ],
    
	/**
	 * @property {TolomeoExt.ToloViewCodelessManager} codelessManager
	 * Gestore delle operazioni del componente.
	 */
	
	/**
	 * @property {Ext.grid.Panel} propertyGrid
	 * Griglia ExtJs per la presentazione e la modifica dei dati richiesti.
	 */
	
	/**
	 * @property {Ext.grid.Panel} decodeGrid
	 * Griglia ExtJs per la presentazione e la modifica dei dati relativi alle tabelle di decodifica.
	 */
	
	/**
	 * @property {String} mode
	 * Stringa che indica la modalità di funzionamento corrente della form.
	 */

	/**
     * Inizializza un nuovo TolomeoExt.ToloNewCodeLessPanel.
     * @param {Object} [config] Un opzionale oggetto di configurazione per il componente ExtJs.
     */
	initComponent: function(config){
		TolomeoExt.Vars.ApplyIfDefaults(this);
		
		editFieldSetTitle = ToloI18n.getMsg("ToloViewCodeLessPanel.editFieldSetTitle");
		newFieldSetTitle = ToloI18n.getMsg("ToloViewCodeLessPanel.newFieldSetTitle");
		applyModsTooltip = ToloI18n.getMsg("ToloViewCodeLessPanel.applyModsTooltip");
		resetTooltip = ToloI18n.getMsg("ToloViewCodeLessPanel.resetTooltip");
		restoreBoxTitle = ToloI18n.getMsg("ToloViewCodeLessPanel.restoreBoxTitle"); 
		restoreBoxText = ToloI18n.getMsg("ToloViewCodeLessPanel.restoreBoxText"); 
		saveBoxTitle = ToloI18n.getMsg("ToloViewCodeLessPanel.saveBoxTitle"); 
		saveBoxText = ToloI18n.getMsg("ToloViewCodeLessPanel.saveBoxText");
		missingIDTPNTitle = ToloI18n.getMsg("ToloViewCodeLessPanel.missingIDTPNTitle"); 
		missingIDTPNText = ToloI18n.getMsg("ToloViewCodeLessPanel.missingIDTPNText"); 

		
		// ///////////////////// //
		// FEATURE DI SELEZIONE  //
		// ///////////////////// //
		
		var gridStore = Ext.create('Ext.data.Store', {
		    fields: ["name", "nl", "type", "editable", "validation", "value", "value2", "format", "width"],
		    proxy: {
		        type: 'memory',
		        reader: {
		            type: 'json',
		            root: 'rows'
		        }
		    }
		});
		
	    var me = this;
		this.propertyGrid = Ext.create('Ext.grid.Panel', {
			margin: "5 5 5 5",
			hideHeaders: true,
			store: gridStore,
			overflowY: 'auto',
		    columns: [
              {
            	  header: ToloI18n.getMsg("ToloViewCodeLessPanel.colNome"),  
            	  dataIndex: 'name',
            	  tdCls: 'codelessnomecampo',
            	  flex: 1,
            	  renderer: function(value, metaData, record, rowIndex, colIndex, store, view)
            	  {
            		  var type = record.get("type");
            		  
	  	              switch (type) {
	                    case "VALORE":
	                    	return value;
	                        break;
	                    case "COSTANTE":
						    metaData.tdAttr = ' colspan="2"';
	                    	return value;
	                        break;
	                    case "URL":
						    metaData.tdAttr = ' colspan="2"';
	                    	return "<a href=\"" + record.get("value") + "\" target=\"_blank\">" + value + "</a>";
	                        break;
	                    case "IMG":
						    metaData.tdAttr = ' colspan="2"';
	                    	return "<img src=\"" + record.get("value") + "\" alt=\"" + value + "\" width=\"" + record.get("width") + "\"></img>";
	                        break;
	                    case "IMGURL":
						    metaData.tdAttr = ' colspan="2"';
	                    	return "<a href=\"" + record.get("value2") + "\" target=\"_blank\"><img src=\"" + record.get("value") + "\" alt=\"" + value + "\" width=\"" + record.get("width") + "\"></img></a>";
	                        break;
	                    case "DETAIL":
	                    	return value;
	                        break;
	                    default:
	                		return value;
	  	              }
            	  }
              },
              {
            	  header: 'NL',  
            	  dataIndex: 'nl',
                  hidden: true
              },
              {
            	  header: 'Tipo',  
            	  dataIndex: 'type',
                  hidden: true
              },
              {
            	  header: 'Editabile',  
            	  dataIndex: 'editable',
                  hidden: true
              },
              {
            	  header: 'Validation',  
            	  dataIndex: 'validation',
                  hidden: true
              }, 
              {
            	  header: ToloI18n.getMsg("ToloViewCodeLessPanel.colValore"), 
            	  dataIndex: 'value',
            	  tdCls: 'codelessvalore',
            	  //flex: 50/100,
            	  flex: 2,
            	  renderer: function(value, metaData, record, rowIndex, store, view) {
            		  var type = record.get("type"); 
	  	              switch (type) {
	                    case "VALORE":
	                    	return value;
	                        break;
	                    case "COSTANTE":
	                    case "URL":
	                    case "IMG":
	                    case "IMGURL":
	                    	return null;
	                        break;
	                    case "DETAIL":
	                    	return value;
	                        break;
	                    default:
	                		return value;
	  	              }
            	  }
              }
            ]
		});
		
	    this.layout = 'fit';
		
		this.callParent();
		this.buildManager(gridStore);
//		this.codelessManager.on("loaddata", this.dataLoaded, this);
		
		this.add([this.propertyGrid]);
	},
	
//	dataLoaded: function()
//	{
//		
//		var linkButton = this.formPanel.query("button[ref=linkButton]")[0];
//		var metadata = this.codelessManager.getRequestMetadata();
//		
//		if (metadata.layerURL != null && metadata.layerURL != "") {
//			linkButton.setText(metadata.layerURLLabel);
//			linkButton.setHref(metadata.layerURL);
//			linkButton.show();
//		} else {
//			linkButton.hide();
//		}
//	},
	
	/**
     * Restituisce il formato data da usare per l'editor. 
     * 
     */
	getDateFormat: function(){
		var metadata = this.codelessManager.getRequestMetadata();
    	
		// ////////////////////////////////////////////////////////////
		// Usa ISO-8601 come formato data se non viene trovata nessuna 
		// corrispondenza attraverso l'invocazione di getExtDateFormat.
		// ////////////////////////////////////////////////////////////
		var format =  metadata ? (this.getExtDateFormat(metadata.dateFormat) || this.dateFormat) : this.dateFormat;
		return format;
	},
	
	/**
     * Converte il formato Data fornito dal server sulla base di quelli disponibili 
     * in configurazione. 
     * @param {String} format formato da usare per il campo editor della data.
     * 
     */
	getExtDateFormat: function(format){
		var date_format;
		
		if(format){
			for(var i=0; i<this.dateFormats.length; i++){
				var df = this.dateFormats[i];
				if(format == df.java){
					date_format = df.ext;
					break;
				}
			}
		}
		
		return date_format;
	},
	
	/**
     * Crea il componente Ext destinato a contenere il valore delle propriet�.
     * @param {TolomeoExt.data.ToloUniqueValuesStore} store Store della combo box di auto completamento.
     * @param {String} url Url del servizio remoto di auto completamento.
     * @param {String} layerName codTPN da usare com eparametro della richiesta.
     * @param {String} fieldName Nome della proprietà di cui ritornare i suggerimenti.
     * 
     */
    initUniqueValuesStore: function(store, layerName, fieldName) {
        var params = {
            inputs: {
            	featureTypeName: layerName,
                fieldName: fieldName
            },
            start: 0,
            limit: this.autoCompleteCfg.pageSize || this.pageSize
        };
        
        store.setParams(params);
    },
	
	/**
     * Crea il Gestore delle richieste se assente e registra gli eventi necessari.
     * @param {Ext.Data.Store} store Store che il Manager deve gestire.
     */
	buildManager: function(store){
		this.codelessManager = Ext.create('TolomeoExt.ToloViewCodelessManager', {
			TOLOMEOServer : this.TOLOMEOServer,
			TOLOMEOContext: this.TOLOMEOContext,
			store: store
		});
		
		this.codelessManager.on({
			changemode: function(mode, store){
				this.setFormMode(mode, store);
			},
			scope: this
		});
	},
	
	/**
     * Imposta sul Manager l'oggetto di controllo della mappa per intercettare le 
     * operazioni utente (eventi tolomeo) e procedere con la gestione della form.
     * @param {TolomeoExt.ToloMapAPIExt} mapApiExt Oggetto di controllo della mappa.
     */
	setMapApiExt: function(mapApiExt){
		this.codelessManager.setMapApiExt(mapApiExt);
	},
	
	/**
     * Imposta la modalit� con cui configurare la form.
     * @param {String} mode Modalit� di configurazione (view, edit, new).
     * @param {Ext.Data.Store} store Store con gui configurare la griglia dei dati
     */
	setFormMode: function(mode, store){
		this.mode = mode;
		
		this.propertyGrid.reconfigure(store);
		
		// ///////////////////////////////////////////////////////////
		// Se sono stati forniti dal server valori di default per i 
		// campi della form allora le rispettive celle delle griglia 
		// devono essere marcate come valorizzate (DIRTY).
		// ///////////////////////////////////////////////////////////
		if(mode == "new"){
			var gridStore = this.propertyGrid.getStore();
			var isDirty = false;
			gridStore.each(function(r) {
				var value = r.get("value");
				var nl = r.get("nl");
                if (value != "" && nl != "") {
                   r.setDirty();
                   isDirty = true;
                }
            });
		}
	}
});