/*!
 * Red5 Analytics Demo
 * Copyright(c) 2009, Nick Brown
 * 
 * This code is licensed under Open Source GPL License Version 3.0.
 * http://www.gnu.org/copyleft/gpl.html
 * 
 * Use it as you wish, 
 * but keep this copyright intact.
 * 
 * Included User Extensions:
 * 
 * ********************************************************************************************
 * Ext.ux.IconMenu
 * 
 * Ext.ux.IconMenu Plugin 
 *
 * Adds user defined menu to Window/Panel icon and adds dbl click close action
 *
 * @author    Ing. Jozef Sakáloš
 * @date      19. March 2008
 * @version   $Id: Ext.ux.IconMenu.js 114 2008-03-29 00:45:38Z jozo $
 *
 * @license Ext.ux.IconMenu is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * License details: http://www.gnu.org/licenses/lgpl.html
 * 
 * ********************************************************************************************
 * Ext.ux.GUID
 * 
 * Original version copyright (C) 2006, Erik Giberti (AF-Design), and licensed GNU GPL
 * 
 * ********************************************************************************************
 * 
 *  @class Ext.ux.ManagedIFrame
 * Version:  1.2
 * Author: Doug Hendricks. doug[always-At]theactivegroup.com
 * Copyright 2007-2008, Active Group, Inc.  All rights reserved.
 *
 ************************************************************************************
 *   This file is distributed on an AS IS BASIS WITHOUT ANY WARRANTY;
 *   without even the implied warranty of MERCHANTABILITY or
 *   FITNESS FOR A PARTICULAR PURPOSE.
 ************************************************************************************
 *
 * License: ux.ManagedIFrame and ux.ManagedIFramePanel 1.2 are licensed under the terms of
 * the Open Source LGPL 3.0 license: http://www.gnu.org/licenses/lgpl.html
 *
 * Commercial use is prohibited without a Commercial License. See http://licensing.theactivegroup.com.
 *
 * Donations are welcomed: http://donate.theactivegroup.com
 *
 * Release: 1.2  (8/22/2008) FF3 Compatibility Fixes, loadMask tweaks
 * 
 */

String.prototype.endsWith = function(s){
	var reg = new RegExp(s + "$");
	return reg.test(this);
};
Ext.override(Ext.tree.TreeEditor,{
	triggerEdit : function(node, defer){
        this.completeEdit();
		if(node.attributes.editable !== false){
	       /**
	        * The tree node this editor is bound to. Read-only.
	        * @type Ext.tree.TreeNode
	        * @property editNode
	        */
			this.editNode = node;
            if(this.tree.autoScroll){
                node.ui.getEl().scrollIntoView(this.tree.body);
            }
            this.autoEditTimer = this.startEdit.defer(this.editDelay, this, [node.ui.textNode, node.attributes.tabtext]);
            return false;
        }
    }
});
Ext.override(Ext.grid.GridView, {
	doRender : function(cs, rs, ds, startRow, colCount, stripe){
        var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1, tstyle = 'width:'+this.getTotalWidth()+';',buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r, j, i, rowIndex, alt = [], len;
        for(j = 0, len = rs.length; j < len; j++){
            r = rs[j]; cb = [];
            rowIndex = (j+startRow);
            for(i = 0; i < colCount; i++){
                c = cs[i];
                p.id = c.id;
                p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
                p.attr = p.cellAttr = "";
                p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
                p.style = c.style;
                if(p.value == undefined || p.value === "") p.value = "&#160;";
//                if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
//                    p.css += ' x-grid3-dirty-cell';
//                }
                cb[cb.length] = ct.apply(p);
            }
            if(stripe && ((rowIndex+1) % 2 == 0)){
                alt[0] = "x-grid3-row-alt";
            }
            if(r.dirty){
                alt[1] = " x-grid3-dirty-row";
            }
            rp.cols = colCount;
            if(this.getRowClass){
                alt[2] = this.getRowClass(r, rowIndex, rp, ds);
            }
            rp.alt = alt.join(" ");
            rp.cells = cb.join("");
            buf[buf.length] =  rt.apply(rp);
        }
        return buf.join("");
    }
});

/**
 * @author nick_brown
 */

Ext.ux.base64 = {

    base64s : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
    
    encode: function(decStr){
        if (typeof btoa === 'function') {
             return btoa(decStr);            
        }
        var base64s = this.base64s, 
        	bits,
        	dual,
        	i = 0,
        	encOut = "";
        while(decStr.length >= i + 3){
            bits = (decStr.charCodeAt(i++) & 0xff) <<16 | (decStr.charCodeAt(i++) & 0xff) <<8 | decStr.charCodeAt(i++) & 0xff;
            encOut += base64s.charAt((bits & 0x00fc0000) >>18) + base64s.charAt((bits & 0x0003f000) >>12) + base64s.charAt((bits & 0x00000fc0) >> 6) + base64s.charAt((bits & 0x0000003f));
        }
        if(decStr.length -i > 0 && decStr.length -i < 3){
            dual = Boolean(decStr.length -i -1);
            bits = ((decStr.charCodeAt(i++) & 0xff) <<16) |    (dual ? (decStr.charCodeAt(i) & 0xff) <<8 : 0);
            encOut += base64s.charAt((bits & 0x00fc0000) >>18) + base64s.charAt((bits & 0x0003f000) >>12) + (dual ? base64s.charAt((bits & 0x00000fc0) >>6) : '=') + '=';
        }
        return(encOut);
    },
    
    decode: function(encStr){
        if (typeof atob === 'function') {
            return atob(encStr); 
        }
        var base64s = this.base64s,        
            bits,
            decOut = "",
            i = 0;
        for(; i<encStr.length; i += 4){
            bits = (base64s.indexOf(encStr.charAt(i)) & 0xff) <<18 | (base64s.indexOf(encStr.charAt(i +1)) & 0xff) <<12 | (base64s.indexOf(encStr.charAt(i +2)) & 0xff) << 6 | base64s.indexOf(encStr.charAt(i +3)) & 0xff;
            decOut += String.fromCharCode((bits & 0xff0000) >>16, (bits & 0xff00) >>8, bits & 0xff);
        }
        if(encStr.charCodeAt(i -2) == 61){
            return(decOut.substring(0, decOut.length -2));
        }
        else if(encStr.charCodeAt(i -1) == 61){
            return(decOut.substring(0, decOut.length -1));
        }
        else {
            return(decOut);
        }
    }

}; 

/*!
 * ux.ManagedIFrame for ExtJS Library 3.1+
 * Copyright(c) 2008-2009 Active Group, Inc.
 * licensing@theactivegroup.com
 * http://licensing.theactivegroup.com
 */
     
 Ext.namespace('Ext.ux.plugin');
 Ext.onReady(function(){
    
   /* This important rule solves many of the <object/iframe>.reInit issues encountered
    * when setting display:none on an upstream(parent) element (on all Browsers except IE).
    * This default rule enables the new Panel:hideMode 'nosize'. The rule is designed to
    * set height/width to 0 cia CSS if hidden or collapsed.
    * Additional selectors also hide 'x-panel-body's within layouts to prevent
    * container and <object, img, iframe> bleed-thru.
    */
    var CSS = Ext.util.CSS;
    if(CSS){ 
        CSS.getRule('.x-hide-nosize') || //already defined?
            CSS.createStyleSheet('.x-hide-nosize{height:0px!important;width:0px!important;border:none!important;zoom:1;}.x-hide-nosize * {height:0px!important;width:0px!important;border:none!important;zoom:1;}');
        CSS.refreshCache();
    }
    
});

(function(){

      var El = Ext.Element, A = Ext.lib.Anim, supr = El.prototype; 
      var VISIBILITY = "visibility",
        DISPLAY = "display",
        HIDDEN = "hidden",
        NONE = "none";
        
      var fx = {};
    
      fx.El = {
	      	     
            /**
	         * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
	         * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
	         * @return {Ext.Element} this
	         */
	       setDisplayed : function(value) {
                var me=this;
                me.visibilityCls ? (me[value !== false ?'removeClass':'addClass'](me.visibilityCls)) :
	                supr.setDisplayed.call(me, value);
                return me;
	        },
            
            /**
	         * Returns true if display is not "none"
	         * @return {Boolean}
	         */
	        isDisplayed : function() {
	            return !(this.hasClass(this.visibilityCls) || this.dom.style[DISPLAY] == NONE);
	        },
	        // private
	        fixDisplay : function(){
	            var me = this;
	            supr.fixDisplay.call(me);
                me.visibilityCls && me.removeClass(me.visibilityCls); 
	        },
	
	        /**
	         * Checks whether the element is currently visible using both visibility, display, and nosize class properties.
	         * @return {Boolean} True if the element is currently visible, else false
	         */
	        isVisible : function(deep) {
	            return this.visible || 
                   (!this.isStyle(VISIBILITY, HIDDEN) && 
                       this.visibilityCls ? !this.hasClass(this.visibilityCls) :!this.isStyle(DISPLAY, NONE));
	        }
	    };
        
        //Add basic capabilities to the Ext.Element.Flyweight class
        Ext.override(El.Flyweight, fx.El);

     /**
      * @class Ext.ux.plugin.VisibilityMode
      * @version 1.3.1
      * @author Doug Hendricks. doug[always-At]theactivegroup.com
      * @copyright 2007-2009, Active Group, Inc.  All rights reserved.
      * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a>
      * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
      * @singleton
      * @static
      * @desc This plugin provides an alternate mechanism for hiding Ext.Elements and a new hideMode for Ext.Components.<br />
      * <p>It is generally designed for use with all browsers <b>except</b> Internet Explorer, but may used on that Browser as well.
      * <p>If included in a Component as a plugin, it sets it's hideMode to 'nosize' and provides a new supported
      * CSS rule that sets the height and width of an element and all child elements to 0px (rather than
      * 'display:none', which causes DOM reflow to occur and re-initializes nested OBJECT, EMBED, and IFRAMES elements)
      * @example 
       var div = Ext.get('container');
       new Ext.ux.plugin.VisibilityMode().extend(div);
       //You can override the Element (instance) visibilityCls to any className you wish at any time
       div.visibilityCls = 'my-hide-class';
       div.hide() //or div.setDisplayed(false);
      
       // In Ext Layouts:      
       someContainer.add({
         xtype:'flashpanel',
         plugins: [new Ext.ux.plugin.VisibilityMode() ],
         ...
        });
    
       // or, Fix a specific Container only and all of it's child items:
       // Note: An upstream Container may still cause Reflow issues when hidden/collapsed
    
        var V = new Ext.ux.plugin.VisibilityMode({ bubble : false }) ;
        new Ext.TabPanel({
         plugins     : V,
         defaults    :{ plugins: V },
         items       :[....]
        });
     */
 Ext.ux.plugin.VisibilityMode = function(opt) {

    Ext.apply(this, opt||{});
    
    var CSS = Ext.util.CSS;

    if(CSS && !Ext.isIE && this.fixMaximizedWindow !== false && !Ext.ux.plugin.VisibilityMode.MaxWinFixed){
        //Prevent overflow:hidden (reflow) transitions when an Ext.Window is maximize.
        CSS.updateRule ( '.x-window-maximized-ct', 'overflow', '');
        Ext.ux.plugin.VisibilityMode.MaxWinFixed = true;  //only updates the CSS Rule once.
    }
    
   };


  Ext.extend(Ext.ux.plugin.VisibilityMode , Object, {

       /**
        * @cfg {Boolean} bubble If true, the VisibilityMode fixes are also applied to parent Containers which may also impact DOM reflow.
        * @default true
        */
      bubble              :  true,

      /**
      * @cfg {Boolean} fixMaximizedWindow If not false, the ext-all.css style rule 'x-window-maximized-ct' is disabled to <b>prevent</b> reflow
      * after overflow:hidden is applied to the document.body.
      * @default true
      */
      fixMaximizedWindow  :  true,
     
      /**
       *
       * @cfg {array} elements (optional) A list of additional named component members to also adjust visibility for.
       * <br />By default, the plugin handles most scenarios automatically.
       * @default null
       * @example ['bwrap','toptoolbar']
       */

      elements       :  null,

      /**
       * @cfg {String} visibilityCls A specific CSS classname to apply to Component element when hidden/made visible.
       * @default 'x-hide-nosize'
       */

      visibilityCls   : 'x-hide-nosize',

      /**
       * @cfg {String} hideMode A specific hideMode value to assign to affected Components.
       * @default 'nosize'
       */
      hideMode  :   'nosize' ,

      ptype     :  'uxvismode', 
      /**
      * Component plugin initialization method.
      * @param {Ext.Component} c The Ext.Component (or subclass) for which to apply visibilityMode treatment
      */
      init : function(c) {

        var hideMode = this.hideMode || c.hideMode,
            plugin = this,
            bubble = Ext.Container.prototype.bubble,
            changeVis = function(){

	            var els = [this.collapseEl, this.actionMode].concat(plugin.elements||[]);
	
	            Ext.each(els, function(el){
		            plugin.extend( this[el] || el );
	            },this);
	
	            var cfg = {
                    visFixed  : true,
                    animCollapse : false,
                    animFloat   : false,
		            hideMode  : hideMode,
		            defaults  : this.defaults || {}
	            };
	
	            cfg.defaults.hideMode = hideMode;
	            
	            Ext.apply(this, cfg);
	            Ext.apply(this.initialConfig || {}, cfg);
            
            };

         c.on('render', function(){

            // Bubble up the layout and set the new
            // visibility mode on parent containers
            // which might also cause DOM reflow when
            // hidden or collapsed.
            if(plugin.bubble !== false && this.ownerCt){

               bubble.call(this.ownerCt, function(){
                  this.visFixed || this.on('afterlayout', changeVis, this, {single:true} );
               });
             }

             changeVis.call(this);

          }, c, {single:true});

     },
     /**
      * @param {Element/Array} el The Ext.Element (or Array of Elements) to extend visibilityCls handling to.
      * @param {String} visibilityCls The className to apply to the Element when hidden.
      * @return this
      */
     extend : function(el, visibilityCls){
        el && Ext.each([].concat(el), function(e){
            
	        if(e && e.dom){
                 if('visibilityCls' in e)return;  //already applied or defined?
	             Ext.apply(e, fx.El);
	             e.visibilityCls = visibilityCls || this.visibilityCls;
	        }
        },this);
        return this;
     }

  });
  
  Ext.preg && Ext.preg('uxvismode', Ext.ux.plugin.VisibilityMode );
  /** @sourceURL=<uxvismode.js> */
  Ext.provide && Ext.provide('uxvismode');
})();/* global Ext El ElFrame ELD*/
/*
 * ******************************************************************************
 * This file is distributed on an AS IS BASIS WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * ***********************************************************************************
 * License: multidom.js is offered under an MIT License.
 * Donations are welcomed: http://donate.theactivegroup.com
 */

 /**
  * @class multidom
  * @version 2.0
  * @license MIT
  * @author Doug Hendricks. Forum ID: <a href="http://extjs.com/forum/member.php?u=8730">hendricd</a>
  * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
  * @copyright 2007-2009, Active Group, Inc. All rights reserved.
  * @description [Designed For Ext Core and ExtJs Frameworks (using ext-base adapter only) 3.0 or higher ONLY]
  * The multidom library extends (overloads) Ext Core DOM methods and functions to
  * provide document-targeted access to the documents loaded in external (FRAME/IFRAME)
  * documents.
  * <p>It maintains seperate DOM Element caches (and more) for each document instance encountered by the
  * framework, permitting safe access to DOM Elements across document instances that may share
  * the same Element id or name.  In essence, multidom extends the functionality provided by Ext Core
  * into any child document without having to load the Core library into the frame's global context.
  * <h3>Custom Element classes.</h3>
  * The Ext.get method is enhanced to support resolution of the custom Ext.Element implementations.
  * (The ux.ManagedIFrame 2.0 Element class is an example of such a class.)
  * <p>For example: If you were retrieving the Ext.Element instance for an IFRAME and the class
  * Ext.Element.IFRAME were defined:
  * <pre><code>Ext.get('myFrame')</pre></code>
  * would return an instance of Ext.Element.IFRAME for 'myFrame' if it were found.
  * @example
   // return the Ext.Element with an id 'someDiv' located in external document hosted by 'iframe'
   var iframe = Ext.get('myFrame');
   var div = Ext.get('someDiv', iframe.getFrameDocument()); //Firefox example
   if(div){
     div.center();
    }
   Note: ux.ManagedIFrame provides an equivalent 'get' method of it's own to access embedded DOM Elements
   for the document it manages.
   <pre><code>iframe.get('someDiv').center();</pre></code>

   Likewise, you can retrieve the raw Element of another document with:
   var el = Ext.getDom('myDiv', iframe.getFrameDocument());
 */

 (function(){

    /*
     * Ext.Element and Ext.lib.DOM enhancements.
     * Primarily provides the ability to interact with any document context
     * (not just the one Ext was loaded into).
     */
   var El = Ext.Element,
       ElFrame,
       ELD = Ext.lib.Dom,
       A = Ext.lib.Anim,
       Evm = Ext.EventManager,
       E = Ext.lib.Event,
       DOC = document,
       emptyFn = function(){},
       OP = Object.prototype,
       OPString = OP.toString,
       HTMLDoc = '[object HTMLDocument]';
       
   if(!Ext.elCache) throw 'Ext Release is not supported';

   /**
    * @private
    */
   Ext._documents= {}; 
   Ext._documents[Ext.id(document,'_doc')]=Ext.elCache;

    /**
    * @private
    * Resolve the Element cache for a given element/window/document context.
    */
    var resolveCache = ELD.resolveDocumentCache = function(el, cacheId){
        var doc = GETDOC(el),
            c = Ext.isDocument(doc) ? Ext.id(doc) : cacheId,
            cache = Ext._documents[c] || null, d, win;

         //see if the document instance is managed by FRAME
         if(!cache && doc && (win = doc.parentWindow || doc.defaultView)){  //Is it a frame document
              if(d = win.frameElement){
                   c = d.id || d.name;  //the id of the frame is the cacheKey
                }
         }
         return cache ||
            Ext._documents[c] ||
            (c ? Ext._documents[c] = {}: null);
     },
     clearCache = ELD.clearDocumentCache = function(cacheId){
       delete  Ext._documents[cacheId];
     };

   El.addMethods || ( El.addMethods = function(ov){ Ext.apply(El.prototype, ov||{}); });

   Ext.removeNode =  function(n){
         var dom = n ? n.dom || n : null;
         if(dom && dom.parentNode && dom.tagName != 'BODY'){
            var el, elc, D = ELD.getDocument(dom), elCache = resolveCache(dom);

            //clear out any references if found in the El.cache(s)
            if((elc = elCache[dom.id]) && (el = elc.el) ){

                if(el.dom){
                    (Ext.enableNestedListenerRemoval) ? Evm.purgeElement(el.dom, true) : Evm.removeAll(el.dom);
                }
                delete elCache[dom.id];
                delete el.dom;
                delete el._context;
                el = null;
            }

            if(Ext.isIE && !Ext.isIE8){
                var d = D.createElement('div');
                d.appendChild(dom);
                d.removeChild(dom);
                d = null;  
            } else {
                var p = dom.parentElement || dom.parentNode;
                p && p.removeChild(dom);
                p = null;
            }
          }
          dom = null;
    };

     var overload = function(pfn, fn ){
           var f = typeof pfn === 'function' ? pfn : function t(){};
           var ov = f._ovl; //call signature hash
           if(!ov){
               ov = { base: f};
               ov[f.length|| 0] = f;
               f= function t(){  //the proxy stub
                  var o = arguments.callee._ovl;
                  var fn = o[arguments.length] || o.base;
                  //recursion safety
                  return fn && fn != arguments.callee ? fn.apply(this,arguments): undefined;
               };
           }
           var fnA = [].concat(fn);
           for(var i=0,l=fnA.length; i<l; ++i){
             //ensures no duplicate call signatures, but last in rules!
             ov[fnA[i].length] = fnA[i];
           }
           f._ovl= ov;
           var t = null;
           return f;
       };

    Ext.applyIf( Ext, {
        overload : overload( overload,
           [
             function(fn){ return overload(null, fn);},
             function(obj, mname, fn){
                 return obj[mname] = overload(obj[mname],fn);}
          ]),

        isArray : function(v){
           return !!v && OPString.apply(v) == '[object Array]';
        },

        isObject:function(obj){
            return !!obj && typeof obj == 'object';
        },

        /**
         * HTMLDocument assertion with optional accessibility testing
         * @param {HTMLELement} el The DOM Element to test
         * @param {Boolean} testOrigin (optional) True to test "same-origin" access
         *
         */
        isDocument : function(el, testOrigin){
            var elm = el ? el.dom || el : null;
            var test = OPString.apply(elm) == HTMLDoc || (elm && elm.nodeType == 9);
            if(test && testOrigin){
                try{
                    test = !!elm.location;
                }
                catch(e){return false;}
            }
            return test;
        },

        isWindow : function(el){
          var elm = el ? el.dom || el : null;
          return elm ? !!elm.navigator || OPString.apply(elm) == "[object Window]" : false;
        },

        isIterable : function(obj){
            //check for array or arguments
            if( obj === null || obj === undefined )return false;
            if(Ext.isArray(obj) || !!obj.callee || Ext.isNumber(obj.length) ) return true;

            return !!((/NodeList|HTMLCollection/i).test(OPString.call(obj)) || //check for node list type
              //NodeList has an item and length property
              //IXMLDOMNodeList has nextNode method, needs to be checked first.
             obj.nextNode || obj.item || false);
        },
        isElement : function(obj){
            return obj && Ext.type(obj)== 'element';
        },

        isEvent : function(obj){
            return OPString.apply(obj) == '[object Event]' || (Ext.isObject(obj) && !Ext.type(o.constructor) && (window.event && obj.clientX && obj.clientX == window.event.clientX));
        },

        isFunction: function(obj){
            return !!obj && typeof obj == 'function';
        },

        /**
         * Determine whether a specified DOMEvent is supported by a given HTMLElement or Object.
         * @param {String} type The eventName (without the 'on' prefix)
         * @param {HTMLElement/Object/String} testEl (optional) A specific HTMLElement/Object to test against, otherwise a tagName to test against.
         * based on the passed eventName is used, or DIV as default.
         * @return {Boolean} True if the passed object supports the named event.
         */
        isEventSupported : function(evName, testEl){
             var TAGNAMES = {
                  'select':'input',
                  'change':'input',
                  'submit':'form',
                  'reset':'form',
                  'load':'img',
                  'error':'img',
                  'abort':'img'
                },
                //Cached results
                cache = {},
                onPrefix = /^on/i,
                //Get a tokenized string of the form nodeName:type
                getKey = function(type, el){
                    var tEl = Ext.getDom(el);
                    return (tEl ?
                           (Ext.isElement(tEl) || Ext.isDocument(tEl) ?
                                tEl.nodeName.toLowerCase() :
                                    el.self ? '#window' : el || '#object')
                       : el || 'div') + ':' + type;
                };

            return function (evName, testEl) {
              evName = (evName || '').replace(onPrefix,'');
              var el, isSupported = false;
              var eventName = 'on' + evName;
              var tag = (testEl ? testEl : TAGNAMES[evName]) || 'div';
              var key = getKey(evName, tag);

              if(key in cache){
                //Use a previously cached result if available
                return cache[key];
              }

              el = Ext.isString(tag) ? DOC.createElement(tag): testEl;
              isSupported = (!!el && (eventName in el));

              isSupported || (isSupported = window.Event && !!(String(evName).toUpperCase() in window.Event));

              if (!isSupported && el) {
                el.setAttribute && el.setAttribute(eventName, 'return;');
                isSupported = Ext.isFunction(el[eventName]);
              }
              //save the cached result for future tests
              cache[key] = isSupported;
              el = null;
              return isSupported;
            };

        }()
    });


    /**
     * @private
     * Determine Ext.Element[tagName] or Ext.Element (default)
     */
    var assertClass = function(el){

        return El[(el.tagName || '-').toUpperCase()] || El;

      };

    var libFlyweight;
    function fly(el, doc) {
        if (!libFlyweight) {
            libFlyweight = new Ext.Element.Flyweight();
        }
        libFlyweight.dom = Ext.getDom(el, doc);
        return libFlyweight;
    }


    Ext.apply(Ext, {
    /*
     * Overload Ext.get to permit Ext.Element access to other document objects
     * This implementation maintains safe element caches for each document queried.
     *
     */

      get : El.get = function(el, doc){         //document targeted
            if(!el ){ return null; }

            Ext.isDocument(doc) || (doc = DOC);
            var ex, elm, id, cache = resolveCache(doc);
            if(typeof el == "string"){ // element id
                elm = Ext.getDom(el,doc);
                if(!elm) return null;
                if(cache[el] && cache[el].el){
                    ex = cache[el].el;
                    ex.dom = elm;
                }else{
                    ex = El.addToCache(new (assertClass(elm))(elm, null, doc));
                }
                return ex;
             }else if(el.tagName || Ext.isWindow(el)){ // dom element

                cache = resolveCache(el);
                id = el.id || (id = Ext.id(el));
                if(cache[id] && (ex = cache[id].el)){
                    ex.dom = el;
                }else{
                    ex = El.addToCache(new (assertClass(el))(el, null, doc), null, cache); 
                    el.navigator && (cache[id].skipGC = true);
                }
                return ex;
            }else if( el instanceof El ){ 

                cache = resolveCache(el);
                el.dom = el.getDocument().getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
                                                              // catch case where it hasn't been appended
                if(el.dom){
                    (cache[el.id] || 
                       (cache[el.id] = {data : {}, events : {}}
                       )).el = el; // in case it was created directly with Element(), let's cache it
                }
                return el;

            }else if(Ext.isDocument(el)){

                if(!Ext.isDocument(el, true)){ return false; }  //is it accessible
                cache = resolveCache(el);

                if(cache[Ext.id(el)] && cache[el.id].el){
                    return cache[el.id].el;
                }
                // create a bogus element object representing the document object
                var f = function(){};
                f.prototype = El.prototype;
                var docEl = new f();
                docEl.dom = el;
                docEl.id = Ext.id(el,'_doc');
                docEl._isDoc = true;

                El.addToCache( docEl, null, cache);
                cache[docEl.id].skipGC = true;
                return docEl;
                        
             }else if(el.isComposite){
                return el;

            }else if(Ext.isArray(el)){
                return Ext.get(doc,doc).select(el);
            }
           return null;

    },

     /**
      * Ext.getDom to support targeted document contexts
      */
     getDom : function(el, doc){
            if(!el){ return null;}
            var D = doc || DOC;
            return el.dom ? el.dom : (typeof el == 'string'  && D.getElementById ? D.getElementById(el) : el);
        },
     /**
     * Returns the current/specified document body as an {@link Ext.Element}.
     * @param {HTMLDocument} doc (optional)
     * @return Ext.Element The document's body
     */
     getBody : function(doc){
            var D = ELD.getDocument(doc) || DOC;
            return Ext.get(D.body || D.documentElement);
       },

     getDoc :Ext.overload([
       Ext.getDoc,
       function(doc){ return Ext.get(doc,doc); }
       ])
   });

   // private method for getting and setting element data
    El.data = function(el, key, value){
        el = El.get(el);
        if (!el) {
            return null;
        }
        var c = resolveCache(el)[el.id].data;
        if(arguments.length == 2){
            return c[key];
        }else{
            return (c[key] = value);
        }
    };
    
    El.addToCache = function(el, id, cache ){
	    id = id || el.id;    
        var C = cache || resolveCache(el);
	    C[id] = {
	        el:  el,
	        data: {},
	        events: {}
	    };
	    return el;
	};

    var propCache = {},
        camelRe = /(-[a-z])/gi,
        camelFn = function(m, a){ return a.charAt(1).toUpperCase(); },
        opacityRe = /alpha\(opacity=(.*)\)/i,
        trimRe = /^\s+|\s+$/g,
        propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
        view = DOC.defaultView,
        VISMODE = 'visibilityMode',
        ELDISPLAY = El.DISPLAY,
        ORIGINALDISPLAY = 'originalDisplay',
        PADDING = "padding",
        MARGIN = "margin",
        BORDER = "border",
        LEFT = "-left",
        RIGHT = "-right",
        TOP = "-top",
        BOTTOM = "-bottom",
        WIDTH = "-width",
        MATH = Math,
        VISIBILITY = "visibility",
        DISPLAY = "display",
        HIDDEN = "hidden",
        NONE = "none", 
        ISCLIPPED = 'isClipped',
        OVERFLOW = 'overflow',
        OVERFLOWX = 'overflow-x',
        OVERFLOWY = 'overflow-y',
        ORIGINALCLIP = 'originalClip',
        XMASKED = "x-masked",
        XMASKEDRELATIVE = "x-masked-relative",
        // special markup used throughout Ext when box wrapping elements
        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},
        data = El.data,
        GETDOM = Ext.getDom,
        GET = Ext.get,
        DH = Ext.DomHelper,
        propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
        CSS = Ext.util.CSS,  //Not available in Ext Core.
        getDisplay = function(dom){
            var d = data(dom, ORIGINALDISPLAY);
            if(d === undefined){
                data(dom, ORIGINALDISPLAY, d = '');
            }
            return d;
        },
        getVisMode = function(dom){
            var m = data(dom, VISMODE);
            if(m === undefined){
                data(dom, VISMODE, m = 1)
            }
            return m;
        };

    function chkCache(prop) {
        return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
    };


    El.addMethods({
        /**
         * Resolves the current document context of this Element
         */
        getDocument : function(){
           return this._context || (this._context = GETDOC(this));
        },

        /**
      * Removes this element from the DOM and deletes it from the cache
      * @param {Boolean} cleanse (optional) Perform a cleanse of immediate childNodes as well.
      * @param {Boolean} deep (optional) Perform a deep cleanse of all nested childNodes as well.
      */

        remove : function(cleanse, deep){
          var dom = this.dom;
          this.isMasked() && this.unmask();
          if(dom){
            cleanse && this.cleanse(true, deep);
            Ext.removeNode(dom);
            delete this._context;
            delete this.dom;
          }
        },

        /**
         * Deep cleansing childNode Removal
         * @param {Boolean} forceReclean (optional) By default the element
         * keeps track if it has been cleansed already so
         * you can call this over and over. However, if you update the element and
         * need to force a reclean, you can pass true.
         * @param {Boolean} deep (optional) Perform a deep cleanse of all childNodes as well.
         */
        cleanse : function(forceReclean, deep){
            if(this.isCleansed && forceReclean !== true){
                return this;
            }
            var d = this.dom, n = d.firstChild, nx;
            while(d && n){
                 nx = n.nextSibling;
                 deep && Ext.fly(n, '_cleanser').cleanse(forceReclean, deep);
                 Ext.removeNode(n);
                 n = nx;
             }
             delete El._flyweights['_cleanser']; //orphan reference cleanup
             this.isCleansed = true;
             return this;
         },

         /**
         * Appends the passed element(s) to this element
         * @param {String/HTMLElement/Array/Element/CompositeElement} el
         * @param {Document} doc (optional) specific document context for the Element search
         * @return {Ext.Element} this
         */
        appendChild: function(el, doc){
            return GET(el, doc || this.getDocument()).appendTo(this);
        },

        /**
         * Appends this element to the passed element
         * @param {Mixed} el The new parent element
         * @param {Document} doc (optional) specific document context for the Element search
         * @return {Ext.Element} this
         */
        appendTo: function(el, doc){
            GETDOM(el, doc || this.getDocument()).appendChild(this.dom);
            return this;
        },

        /**
         * Inserts this element before the passed element in the DOM
         * @param {Mixed} el The element before which this element will be inserted
         * @param {Document} doc (optional) specific document context for the Element search
         * @return {Ext.Element} this
         */
        insertBefore: function(el, doc){
            (el = GETDOM(el, doc || this.getDocument())).parentNode.insertBefore(this.dom, el);
            return this;
        },

        /**
         * Inserts this element after the passed element in the DOM
         * @param {Mixed} el The element to insert after
         * @param {Document} doc (optional) specific document context for the Element search
         * @return {Ext.Element} this
         */
        insertAfter: function(el, doc){
            (el = GETDOM(el, doc || this.getDocument())).parentNode.insertBefore(this.dom, el.nextSibling);
            return this;
        },

        /**
         * Inserts (or creates) an element (or DomHelper config) as the first child of this element
         * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
         * @param {Document} doc (optional) specific document context for the Element search
         * @return {Ext.Element} The new child
         */
        insertFirst: function(el, returnDom){
            el = el || {};
            if(el.nodeType || el.dom || typeof el == 'string'){ // element
                el = GETDOM(el);
                this.dom.insertBefore(el, this.dom.firstChild);
                return !returnDom ? GET(el) : el;
            }else{ // dh config
                return this.createChild(el, this.dom.firstChild, returnDom);
            }
        },

        /**
         * Replaces the passed element with this element
         * @param {Mixed} el The element to replace
         * @param {Document} doc (optional) specific document context for the Element search
         * @return {Ext.Element} this
         */
        replace: function(el, doc){
            el = GET(el, doc || this.getDocument());
            this.insertBefore(el);
            el.remove();
            return this;
        },

        /**
         * Replaces this element with the passed element
         * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
         * @param {Document} doc (optional) specific document context for the Element search
         * @return {Ext.Element} this
         */
        replaceWith: function(el, doc){
            var me = this;
            if(el.nodeType || el.dom || typeof el == 'string'){
                el = GETDOM(el, doc || me.getDocument());
                me.dom.parentNode.insertBefore(el, me.dom);
            }else{
                el = DH.insertBefore(me.dom, el);
            }
            var C = resolveCache(me)||{};
            Ext.removeNode(me.dom);
            me.id = Ext.id(me.dom = el);
            return (C[me.id]={
              el: me,
              events : {},
              data : {}
            }).el;
        },


        /**
         * Inserts an html fragment into this element
         * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
         * @param {String} html The HTML fragment
         * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
         * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
         */
        insertHtml : function(where, html, returnEl){
            var el = DH.insertHtml(where, this.dom, html);
            return returnEl ? Ext.get(el, GETDOC(el)) : el;
        },
        
        /**
         * Sets the element's visibility mode. When setVisible() is called it
         * will use this to determine whether to set the visibility or the display property.
         * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
         * @return {Ext.Element} this
         */
        setVisibilityMode : function(visMode){  
            data(this.dom, VISMODE, visMode);
            return this;
        },
        
        /**
         * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
         * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
         * @param {Boolean} visible Whether the element is visible
         * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
         * @return {Ext.Element} this
         */
         setVisible : function(visible, animate){
            var me = this,
                dom = me.dom,
                isDisplay = getVisMode(this.dom) == ELDISPLAY;
                
            if (!animate || !me.anim) {
                if(isDisplay){
                    me.setDisplayed(visible);
                }else{
                    me.fixDisplay();
                    dom.style.visibility = visible ? "visible" : HIDDEN;
                }
            }else{
                // closure for composites            
                if(visible){
                    me.setOpacity(.01);
                    me.setVisible(true);
                }
                me.anim({opacity: { to: (visible?1:0) }},
                        me.preanim(arguments, 1),
                        null,
                        .35,
                        'easeIn',
                        function(){
                             if(!visible){
                                 dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;                     
                                 Ext.fly(dom).setOpacity(1);
                             }
                        });
            }
            return me;
        },
        /**
         * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
         * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
         * @return {Ext.Element} this
         */
        setDisplayed : function(value) {            
            if(typeof value == "boolean"){
               value = value ? getDisplay(this.dom) : NONE;
            }
            this.setStyle(DISPLAY, value);
            return this;
        },
        
        // private
        fixDisplay : function(){
            var me = this;
            if(me.isStyle(DISPLAY, NONE)){
                me.setStyle(VISIBILITY, HIDDEN);
                me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
                if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
                    me.setStyle(DISPLAY, "block");
                }
            }
        },
        
        /**
         * Convenience method for setVisibilityMode(Element.DISPLAY)
         * @param {String} display (optional) What to set display to when visible
         * @return {Ext.Element} this
         */
        enableDisplayMode : function(display){      
            this.setVisibilityMode(El.DISPLAY);
            if(!Ext.isEmpty(display)){
                data(this.dom, ORIGINALDISPLAY, display);
            }
            return this;
        },
        
        scrollIntoView : function(container, hscroll){
                var d = this.getDocument();
                var c = Ext.getDom(container, d) || Ext.getBody(d).dom;
                var el = this.dom;
                var o = this.getOffsetsTo(c),
                    s = this.getScroll(),
                    l = o[0] + s.left,
                    t = o[1] + s.top,
                    b = t + el.offsetHeight,
                    r = l + el.offsetWidth;
                var ch = c.clientHeight;
                var ct = parseInt(c.scrollTop, 10);
                var cl = parseInt(c.scrollLeft, 10);
                var cb = ct + ch;
                var cr = cl + c.clientWidth;
                if(el.offsetHeight > ch || t < ct){
                    c.scrollTop = t;
                }else if(b > cb){
                    c.scrollTop = b-ch;
                }
                c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore
                if(hscroll !== false){
                    if(el.offsetWidth > c.clientWidth || l < cl){
                        c.scrollLeft = l;
                    }else if(r > cr){
                        c.scrollLeft = r-c.clientWidth;
                    }
                    c.scrollLeft = c.scrollLeft;
                }
                return this;
        },

        contains : function(el){
            try {
                return !el ? false : ELD.isAncestor(this.dom, el.dom ? el.dom : el);
            } catch(e) {
                return false;
            }
        },

        /**
         * Returns the current scroll position of the element.
         * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
         */
        getScroll : function(){
            var d = this.dom,
            doc = this.getDocument(),
            body = doc.body,
            docElement = doc.documentElement,
            l,
            t,
            ret;

            if(Ext.isDocument(d) || d == body){
                if(Ext.isIE && ELD.docIsStrict(doc)){
                    l = docElement.scrollLeft;
                    t = docElement.scrollTop;
                }else{
                    l = window.pageXOffset;
                    t = window.pageYOffset;
                }
                ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
            }else{
                ret = {left: d.scrollLeft, top: d.scrollTop};
            }
            return ret;
        },
        /**
         * Normalizes currentStyle and computedStyle.
         * @param {String} property The style property whose value is returned.
         * @return {String} The current value of the style property for this element.
         */
        getStyle : function(){
            var getStyle =
             view && view.getComputedStyle ?
                function GS(prop){
                    var el = !this._isDoc ? this.dom : null,
                        v,
                        cs,
                        out;

                    if(!el || el == DOC || Ext.isDocument(el)) return null;
                    prop = chkCache(prop);
                    out =  (v = el.style[prop]) ? v :
                           (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
                     // Webkit returns rgb values for transparent.
                    if(Ext.isWebKit && out == 'rgba(0, 0, 0, 0)'){
                        out = 'transparent';
                    }
                    return out;
                } :
                function GS(prop){
                   var el = !this._isDoc ? this.dom : null,
                        m,
                        cs;
                    if(!el || el == DOC || Ext.isDocument(el)) return null;
                    if (prop == 'opacity') {
                        if (el.style.filter.match) {
                            if(m = el.style.filter.match(opacityRe)){
                                var fv = parseFloat(m[1]);
                                if(!isNaN(fv)){
                                    return fv ? fv / 100 : 0;
                                }
                            }
                        }
                        return 1;
                    }
                    prop = chkCache(prop);
                    return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
                };
                var GS = null;
                return getStyle;
        }(),
        /**
         * Wrapper for setting style properties, also takes single object parameter of multiple styles.
         * @param {String/Object} property The style property to be set, or an object of multiple styles.
         * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
         * @return {Ext.Element} this
         */
        setStyle : function(prop, value){
            if(this._isDoc || Ext.isDocument(this.dom)) return this;
            var tmp,
                style,
                camel;
            if (!Ext.isObject(prop)) {
                tmp = {};
                tmp[prop] = value;
                prop = tmp;
            }
            for (style in prop) {
                value = prop[style];
                style == 'opacity' ?
                    this.setOpacity(value) :
                    this.dom.style[chkCache(style)] = value;
            }
            return this;
        },
        /**
        * Centers the Element in either the viewport, or another Element.
        * @param {Mixed} centerIn (optional) The element in which to center the element.
        */
        center : function(centerIn){
            return this.alignTo(centerIn || this.getDocument(), 'c-c');
        },
        
        /**
         * Puts a mask over this element to disable user interaction. Requires core.css.
         * This method can only be applied to elements which accept child nodes.
         * @param {String} msg (optional) A message to display in the mask
         * @param {String} msgCls (optional) A css class to apply to the msg element
         * @return {Element} The mask element
         */
        mask : function(msg, msgCls){
            var me = this,
                dom = me.dom,
                dh = Ext.DomHelper,
                EXTELMASKMSG = "ext-el-mask-msg",
                el, 
                mask;
                
            if(me.getStyle("position") == "static"){
                me.addClass(XMASKEDRELATIVE);
            }
            if((el = data(dom, 'maskMsg'))){
                el.remove();
            }
            if((el = data(dom, 'mask'))){
                el.remove();
            }
    
            mask = dh.append(dom, {cls : "ext-el-mask"}, true);
            data(dom, 'mask', mask);
    
            me.addClass(XMASKED);
            mask.setDisplayed(true);
            if(typeof msg == 'string'){
                var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
                data(dom, 'maskMsg', mm);
                mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
                mm.dom.firstChild.innerHTML = msg;
                mm.setDisplayed(true);
                mm.center(me);
            }
            if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
                mask.setSize(undefined, me.getHeight());
            }
            return mask;
        },
    
        /**
         * Removes a previously applied mask.
         */
        unmask : function(){
            var me = this,
                dom = me.dom,
                mask = data(dom, 'mask'),
                maskMsg = data(dom, 'maskMsg');
            if(mask){
                if(maskMsg){
                    maskMsg.remove();
                    data(dom, 'maskMsg', undefined);
                }
                mask.remove();
                data(dom, 'mask', undefined);
            }
            me.removeClass([XMASKED, XMASKEDRELATIVE]);
        },
        
        /**
         * Returns true if this element is masked
         * @return {Boolean}
         */
        isMasked : function(){
            var m = data(this.dom, 'mask');
            return m && m.isVisible();
        },

        /**
        * Calculates the x, y to center this element on the screen
        * @return {Array} The x, y values [x, y]
        */
        getCenterXY : function(){
            return this.getAlignToXY(this.getDocument(), 'c-c');
        },
        /**
         * Gets the x,y coordinates specified by the anchor position on the element.
         * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
         * for details on supported anchor positions.
         * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
         * of page coordinates
         * @param {Object} size (optional) An object containing the size to use for calculating anchor position
         * {width: (target width), height: (target height)} (defaults to the element's current size)
         * @return {Array} [x, y] An array containing the element's x and y coordinates
         */
        getAnchorXY : function(anchor, local, s){
            //Passing a different size is useful for pre-calculating anchors,
            //especially for anchored animations that change the el size.
            anchor = (anchor || "tl").toLowerCase();
            s = s || {};

            var me = this,  doc = this.getDocument(),
                vp = me.dom == doc.body || me.dom == doc,
                w = s.width || vp ? Ext.lib.Dom.getViewWidth(false,doc) : me.getWidth(),
                h = s.height || vp ? Ext.lib.Dom.getViewHeight(false,doc) : me.getHeight(),
                xy,
                r = Math.round,
                o = me.getXY(),
                scroll = me.getScroll(),
                extraX = vp ? scroll.left : !local ? o[0] : 0,
                extraY = vp ? scroll.top : !local ? o[1] : 0,
                hash = {
                    c  : [r(w * .5), r(h * .5)],
                    t  : [r(w * .5), 0],
                    l  : [0, r(h * .5)],
                    r  : [w, r(h * .5)],
                    b  : [r(w * .5), h],
                    tl : [0, 0],
                    bl : [0, h],
                    br : [w, h],
                    tr : [w, 0]
                };

            xy = hash[anchor];
            return [xy[0] + extraX, xy[1] + extraY];
        },

        /**
         * Anchors an element to another element and realigns it when the window is resized.
         * @param {Mixed} element The element to align to.
         * @param {String} position The position to align to.
         * @param {Array} offsets (optional) Offset the positioning by [x, y]
         * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
         * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
         * is a number, it is used as the buffer delay (defaults to 50ms).
         * @param {Function} callback The function to call after the animation finishes
         * @return {Ext.Element} this
         */
        anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
            var me = this,
                dom = me.dom;

            function action(){
                fly(dom).alignTo(el, alignment, offsets, animate);
                Ext.callback(callback, fly(dom));
            };

            Ext.EventManager.onWindowResize(action, me);

            if(!Ext.isEmpty(monitorScroll)){
                Ext.EventManager.on(window, 'scroll', action, me,
                    {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
            }
            action.call(me); // align immediately
            return me;
        },

        /**
         * Returns the current scroll position of the element.
         * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
         */
        getScroll : function(){
            var d = this.dom,
                doc = this.getDocument(),
                body = doc.body,
                docElement = doc.documentElement,
                l,
                t,
                ret;

            if(d == doc || d == body){
                if(Ext.isIE && ELD.docIsStrict(doc)){
                    l = docElement.scrollLeft;
                    t = docElement.scrollTop;
                }else{
                    l = window.pageXOffset;
                    t = window.pageYOffset;
                }
                ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
            }else{
                ret = {left: d.scrollLeft, top: d.scrollTop};
            }
            return ret;
        },

        /**
         * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
         * supported position values.
         * @param {Mixed} element The element to align to.
         * @param {String} position The position to align to.
         * @param {Array} offsets (optional) Offset the positioning by [x, y]
         * @return {Array} [x, y]
         */
        getAlignToXY : function(el, p, o){
            var doc;
            el = Ext.get(el, doc = this.getDocument());

            if(!el || !el.dom){
                throw "Element.getAlignToXY with an element that doesn't exist";
            }

            o = o || [0,0];
            p = (p == "?" ? "tl-bl?" : (!/-/.test(p) && p != "" ? "tl-" + p : p || "tl-bl")).toLowerCase();

            var me = this,
                d = me.dom,
                a1,
                a2,
                x,
                y,
                //constrain the aligned el to viewport if necessary
                w,
                h,
                r,
                dw = Ext.lib.Dom.getViewWidth(false,doc) -10, // 10px of margin for ie
                dh = Ext.lib.Dom.getViewHeight(false,doc)-10, // 10px of margin for ie
                p1y,
                p1x,
                p2y,
                p2x,
                swapY,
                swapX,
                docElement = doc.documentElement,
                docBody = doc.body,
                scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
                scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
                c = false, //constrain to viewport
                p1 = "",
                p2 = "",
                m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);

            if(!m){
               throw "Element.getAlignToXY with an invalid alignment " + p;
            }

            p1 = m[1];
            p2 = m[2];
            c = !!m[3];

            //Subtract the aligned el's internal xy from the target's offset xy
            //plus custom offset to get the aligned el's new offset xy
            a1 = me.getAnchorXY(p1, true);
            a2 = el.getAnchorXY(p2, false);

            x = a2[0] - a1[0] + o[0];
            y = a2[1] - a1[1] + o[1];

            if(c){
               w = me.getWidth();
               h = me.getHeight();
               r = el.getRegion();
               //If we are at a viewport boundary and the aligned el is anchored on a target border that is
               //perpendicular to the vp border, allow the aligned el to slide on that border,
               //otherwise swap the aligned el to the opposite border of the target.
               p1y = p1.charAt(0);
               p1x = p1.charAt(p1.length-1);
               p2y = p2.charAt(0);
               p2x = p2.charAt(p2.length-1);
               swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
               swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));


               if (x + w > dw + scrollX) {
                    x = swapX ? r.left-w : dw+scrollX-w;
               }
               if (x < scrollX) {
                   x = swapX ? r.right : scrollX;
               }
               if (y + h > dh + scrollY) {
                    y = swapY ? r.top-h : dh+scrollY-h;
                }
               if (y < scrollY){
                   y = swapY ? r.bottom : scrollY;
               }
            }

            return [x,y];
        },
            // private ==>  used outside of core
        adjustForConstraints : function(xy, parent, offsets){
            return this.getConstrainToXY(parent || this.getDocument(), false, offsets, xy) ||  xy;
        },

        // private ==>  used outside of core
        getConstrainToXY : function(el, local, offsets, proposedXY){
            var os = {top:0, left:0, bottom:0, right: 0};

            return function(el, local, offsets, proposedXY){
                var doc = this.getDocument();
                el = Ext.get(el, doc);
                offsets = offsets ? Ext.applyIf(offsets, os) : os;

                var vw, vh, vx = 0, vy = 0;
                if(el.dom == doc.body || el.dom == doc){
                    vw = Ext.lib.Dom.getViewWidth(false,doc);
                    vh = Ext.lib.Dom.getViewHeight(false,doc);
                }else{
                    vw = el.dom.clientWidth;
                    vh = el.dom.clientHeight;
                    if(!local){
                        var vxy = el.getXY();
                        vx = vxy[0];
                        vy = vxy[1];
                    }
                }

                var s = el.getScroll();

                vx += offsets.left + s.left;
                vy += offsets.top + s.top;

                vw -= offsets.right;
                vh -= offsets.bottom;

                var vr = vx+vw;
                var vb = vy+vh;

                var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
                var x = xy[0], y = xy[1];
                var w = this.dom.offsetWidth, h = this.dom.offsetHeight;

                // only move it if it needs it
                var moved = false;

                // first validate right/bottom
                if((x + w) > vr){
                    x = vr - w;
                    moved = true;
                }
                if((y + h) > vb){
                    y = vb - h;
                    moved = true;
                }
                // then make sure top/left isn't negative
                if(x < vx){
                    x = vx;
                    moved = true;
                }
                if(y < vy){
                    y = vy;
                    moved = true;
                }
                return moved ? [x, y] : false;
            };
        }(),
        /**
        * Calculates the x, y to center this element on the screen
        * @return {Array} The x, y values [x, y]
        */
        getCenterXY : function(){
            return this.getAlignToXY(Ext.getBody(this.getDocument()), 'c-c');
        },
       
        /**
        * Centers the Element in either the viewport, or another Element.
        * @param {Mixed} centerIn (optional) The element in which to center the element.
        */
        center : function(centerIn){
            return this.alignTo(centerIn || Ext.getBody(this.getDocument()), 'c-c');
        } ,

        /**
         * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
         * @param {String} selector The simple selector to test
         * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
         * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
         * @return {HTMLElement} The matching DOM node (or null if no match was found)
         */
        findParent : function(simpleSelector, maxDepth, returnEl){
            var p = this.dom,
                D = this.getDocument(),
                b = D.body,
                depth = 0,
                stopEl;
            if(Ext.isGecko && OPString.call(p) == '[object XULElement]') {
                return null;
            }
            maxDepth = maxDepth || 50;
            if (isNaN(maxDepth)) {
                stopEl = Ext.getDom(maxDepth, D);
                maxDepth = Number.MAX_VALUE;
            }
            while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
                if(Ext.DomQuery.is(p, simpleSelector)){
                    return returnEl ? Ext.get(p, D) : p;
                }
                depth++;
                p = p.parentNode;
            }
            return null;
        },
        /**
         *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
         * @return {Ext.Element} this
         */
        clip : function(){
            var me = this,
                dom = me.dom;
                
            if(!data(dom, ISCLIPPED)){
                data(dom, ISCLIPPED, true);
                data(dom, ORIGINALCLIP, {
                    o: me.getStyle(OVERFLOW),
                    x: me.getStyle(OVERFLOWX),
                    y: me.getStyle(OVERFLOWY)
                });
                me.setStyle(OVERFLOW, HIDDEN);
                me.setStyle(OVERFLOWX, HIDDEN);
                me.setStyle(OVERFLOWY, HIDDEN);
            }
            return me;
        },
    
        /**
         *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
         * @return {Ext.Element} this
         */
        unclip : function(){
            var me = this,
                dom = me.dom;
                
            if(data(dom, ISCLIPPED)){
                data(dom, ISCLIPPED, false);
                var o = data(dom, ORIGINALCLIP);
                if(o.o){
                    me.setStyle(OVERFLOW, o.o);
                }
                if(o.x){
                    me.setStyle(OVERFLOWX, o.x);
                }
                if(o.y){
                    me.setStyle(OVERFLOWY, o.y);
                }
            }
            return me;
        }
    });

    Ext.isDefined(El.collectorThreadId) && clearInterval(El.collectorThreadId);
    // private
	// Garbage collection - uncache elements/purge listeners on orphaned elements
	// so we don't hold a reference and cause the browser to retain them
	function garbageCollect(){
	    if(!Ext.enableGarbageCollector){
	        clearInterval(El.collectorThreadId);
	    } else {
	        var eid,
	            el,
	            d,
                o,
                EC = Ext.elCache;
	
	        for(eid in EC){
                o = EC[eid];
                if(o.skipGC){
	                continue;
	            }
	            el = o.el;
	            d = el.dom;
	            // -------------------------------------------------------
	            // Determining what is garbage:
	            // -------------------------------------------------------
	            // !d
	            // dom node is null, definitely garbage
	            // -------------------------------------------------------
	            // !d.parentNode
	            // no parentNode == direct orphan, definitely garbage
	            // -------------------------------------------------------
	            // !d.offsetParent && !document.getElementById(eid)
	            // display none elements have no offsetParent so we will
	            // also try to look it up by it's id. However, check
	            // offsetParent first so we don't do unneeded lookups.
	            // This enables collection of elements that are not orphans
	            // directly, but somewhere up the line they have an orphan
	            // parent.
	            // -------------------------------------------------------
	            
	            if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
	                if(Ext.enableListenerCollection){
	                    Ext.EventManager.removeAll(d);
	                }
	                delete EC[eid];
	            }
	        }
            
	        // Cleanup IE COM Object Hash reference leaks 
	        if (Ext.isIE) {
	            var t = {};
	            for (eid in EC) {
	                t[eid] = EC[eid];
	            }
	            Ext.elCache = Ext._documents[Ext.id(document)] = t;
                t = null;
	        }
	    }
	}
	El.collectorThreadId = setInterval(garbageCollect, 30000);

    Ext.apply(ELD , {
        /**
         * Resolve the current document context of the passed Element
         */
        getDocument : function(el, accessTest){
          var dom= null;
          try{
            dom = Ext.getDom(el, null); //will fail if El.dom is non "same-origin" document
          }catch(ex){}

          var isDoc = Ext.isDocument(dom);
          if(isDoc){
            if(accessTest){
                return Ext.isDocument(dom, accessTest) ? dom : null;
            }
            return dom;
          }
          return dom ?
                dom.ownerDocument ||  //Element
                dom.document //Window
                : null;
        },

        /**
         * Return the Compatability Mode of the passed document or Element
         */
        docIsStrict : function(doc){
            return (Ext.isDocument(doc) ? doc : this.getDocument(doc)).compatMode == "CSS1Compat";
        },

        getViewWidth : Ext.overload ([
           ELD.getViewWidth || function(full){},
            function() { return this.getViewWidth(false);},
            function(full, doc) {
                return full ? this.getDocumentWidth(doc) : this.getViewportWidth(doc);
            }]
         ),

        getViewHeight : Ext.overload ([
            ELD.getViewHeight || function(full){},
            function() { return this.getViewHeight(false);},
            function(full, doc) {
                return full ? this.getDocumentHeight(doc) : this.getViewportHeight(doc);
            }]),

        getDocumentHeight: Ext.overload([
           ELD.getDocumentHeight || emptyFn,
           function(doc) {
            if(doc=this.getDocument(doc)){
              return Math.max(
                 !this.docIsStrict(doc) ? doc.body.scrollHeight : doc.documentElement.scrollHeight
                 , this.getViewportHeight(doc)
                 );
            }
            return undefined;
           }
         ]),

        getDocumentWidth: Ext.overload([
           ELD.getDocumentWidth || emptyFn,
           function(doc) {
              if(doc=this.getDocument(doc)){
                return Math.max(
                 !this.docIsStrict(doc) ? doc.body.scrollWidth : doc.documentElement.scrollWidth
                 , this.getViewportWidth(doc)
                 );
              }
              return undefined;
            }
        ]),

        getViewportHeight: Ext.overload([
           ELD.getViewportHeight || emptyFn,
           function(doc){
             if(doc=this.getDocument(doc)){
                if(Ext.isIE){
                    return this.docIsStrict(doc) ? doc.documentElement.clientHeight : doc.body.clientHeight;
                }else{
                    return doc.defaultView.innerHeight;
                }
             }
             return undefined;
           }
        ]),

        getViewportWidth: Ext.overload([
           ELD.getViewportWidth || emptyFn,
           function(doc) {
              if(doc=this.getDocument(doc)){
                return !this.docIsStrict(doc) && !Ext.isOpera ? doc.body.clientWidth :
                   Ext.isIE ? doc.documentElement.clientWidth : doc.defaultView.innerWidth;
              }
              return undefined;
            }
        ]),

        getXY : Ext.overload([
            ELD.getXY || function(el){},
            function(el, doc) {

                el = Ext.getDom(el, doc);
                var D= this.getDocument(el);
                var p, pe, b, scroll;
                var bd = D ? (D.body || D.documentElement): null;

                if(!el || !bd || el == bd){ return [0, 0]; }

                if (el.getBoundingClientRect) {
                    b = el.getBoundingClientRect();
                    scroll = fly(D).getScroll();
                    return [b.left + scroll.left, b.top + scroll.top];
                }
                var x = 0, y = 0;

                p = el;

                var hasAbsolute = fly(el).getStyle("position") == "absolute";

                while (p) {

                    x += p.offsetLeft;
                    y += p.offsetTop;

                    if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
                        hasAbsolute = true;
                    }

                    if (Ext.isGecko) {
                        pe = fly(p);

                        var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
                        var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;


                        x += bl;
                        y += bt;


                        if (p != el && pe.getStyle('overflow') != 'visible') {
                            x += bl;
                            y += bt;
                        }
                    }
                    p = p.offsetParent;
                }

                if (Ext.isSafari && hasAbsolute) {
                    x -= bd.offsetLeft;
                    y -= bd.offsetTop;
                }

                if (Ext.isGecko && !hasAbsolute) {
                    var dbd = fly(bd);
                    x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
                    y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
                }

                p = el.parentNode;
                while (p && p != bd) {
                    if (!Ext.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
                        x -= p.scrollLeft;
                        y -= p.scrollTop;
                    }
                    p = p.parentNode;
                }
                return [x, y];
            }])
    });

    var GETDOC = ELD.getDocument,
        flies = El._flyweights;

    /**
     * @private
     * Add Ext.fly support for targeted document contexts
     */

    Ext.fly = El.fly = function(el, named, doc){
        var ret = null;
        named = named || '_global';

        if (el = Ext.getDom(el, doc)) {
            (ret = flies[named] = (flies[named] || new El.Flyweight())).dom = el;
            Ext.isDocument(el) && (ret._isDoc = true);
        }
        return ret;
    };

    var flyFn = function(){};
    flyFn.prototype = El.prototype;

    // dom is optional
    El.Flyweight = function(dom){
        this.dom = dom;
    };

    El.Flyweight.prototype = new flyFn();
    El.Flyweight.prototype.isFlyweight = true;
    
    function addListener(el, ename, fn, wrap, scope){
        el = Ext.getDom(el);
        if(!el){ return; }

        var id = Ext.get(el).id,
            es = (resolveCache(el)[id]||{}).events || {},
            wfn;

        wfn = E.on(el, ename, wrap);
        es[ename] = es[ename] || [];
        es[ename].push([fn, wrap, scope, wfn]);

        // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
        // without breaking ExtJS.
        if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
            var args = ["DOMMouseScroll", wrap, false];
            el.addEventListener.apply(el, args);
            Ext.EventManager.addListener(window, 'unload', function(){
                el.removeEventListener.apply(el, args);
            });
        }
        if(ename == "mousedown" && el == DOC){ // fix stopped mousedowns on the document
            Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
        }
    };

    function createTargeted(h, o){
        return function(){
            var args = Ext.toArray(arguments);
            if(o.target == Ext.EventObject.setEvent(args[0]).target){
                h.apply(this, args);
            }
        };
    };

    function createBuffered(h, o, fn){
        fn.task = new Ext.util.DelayedTask(h);
        var w = function(e){
            // create new event object impl so new events don't wipe out properties
            fn.task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
        };
        return w;
    };

    function createSingle(h, el, ename, fn, scope){
        return function(e){
            Ext.EventManager.removeListener(el, ename, fn, scope);
            h(e);
        };
    };

    function createDelayed(h, o, fn){
        return function(e){
            var task = new Ext.util.DelayedTask(h);
            if(!fn.tasks) {
                fn.tasks = [];
            }
            fn.tasks.push(task);
            task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
        };
    };

    function listen(element, ename, opt, fn, scope){
        var o = !Ext.isObject(opt) ? {} : opt,
            el = Ext.getDom(element);

        fn = fn || o.fn;
        scope = scope || o.scope;

        if(!el){
            throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
        }
        function h(e){
            // prevent errors while unload occurring
            if(typeof Ext == 'undefined'){
                return;
            }
            e = Ext.EventObject.setEvent(e);
            var t;
            if (o.delegate) {
                if(!(t = e.getTarget(o.delegate, el))){
                    return;
                }
            } else {
                t = e.target;
            }
            if (o.stopEvent) {
                e.stopEvent();
            }
            if (o.preventDefault) {
               e.preventDefault();
            }
            if (o.stopPropagation) {
                e.stopPropagation();
            }
            if (o.normalized) {
                e = e.browserEvent;
            }

            fn.call(scope || el, e, t, o);
        };
        if(o.target){
            h = createTargeted(h, o);
        }
        if(o.delay){
            h = createDelayed(h, o, fn);
        }
        if(o.single){
            h = createSingle(h, el, ename, fn, scope);
        }
        if(o.buffer){
            h = createBuffered(h, o, fn);
        }

        addListener(el, ename, fn, h, scope);
        return h;
    };

    Ext.apply(Evm ,{
         addListener : Evm.on = function(element, eventName, fn, scope, options){
            if(Ext.isObject(eventName)){
                var o = eventName, e, val;
                for(e in o){
                    val = o[e];
                    if(!propRe.test(e)){
                        if(Ext.isFunction(val)){
                            // shared options
                            listen(element, e, o, val, o.scope);
                        }else{
                            // individual options
                            listen(element, e, val);
                        }
                    }
                }
            } else {
                listen(element, eventName, options, fn, scope);
            }
        },

        /**
         * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
         * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
         * @param {String/HTMLElement} el The id or html element from which to remove the listener.
         * @param {String} eventName The name of the event.
         * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
         * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
         * then this must refer to the same object.
         */
        removeListener : Evm.un = function(element, eventName, fn, scope){
            var el = Ext.getDom(element);
            el && Ext.get(el);
            var elCache = el ? resolveCache(el) : {},
                f = el && ((elCache[el.id]||{events:{}}).events)[eventName] || [],
                wrap, i, l, k, wf;

            for (i = 0, len = f.length; i < len; i++) {
                if (Ext.isArray(f[i]) && f[i][0] == fn && (!scope || f[i][2] == scope)) {
                    if(fn.task) {
                        fn.task.cancel();
                        delete fn.task;
                    }
                    k = fn.tasks && fn.tasks.length;
                    if(k) {
                        while(k--) {
                            fn.tasks[k].cancel();
                        }
                        delete fn.tasks;
                    }
                    wf = wrap = f[i][1];
                    if (E.extAdapter) {
                        wf = f[i][3];
                    }
                    E.un(el, eventName, wf);
                    f.splice(i,1);
                    if (f.length === 0) {
                        delete elCache[el.id].events[eventName];
                    }
                    for (k in elCache[el.id].events) {
                        return false;
                    }
                    elCache[el.id].events = {};
                    return false;
                }
            }

            // jQuery workaround that should be removed from Ext Core
            if(eventName == "mousewheel" && el.addEventListener && wrap){
                el.removeEventListener("DOMMouseScroll", wrap, false);
            }

            if(eventName == "mousedown" && el == DOC && wrap){ // fix stopped mousedowns on the document
                Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
            }
        },

        /**
         * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}
         * directly on an Element in favor of calling this version.
         * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
         */
        removeAll : function(el){
            el = Ext.getDom(el);
            if (!el) {
                return;
            }
            var id = el.id,
                elCache = resolveCache(el)||{},
                es = elCache[id] || {},
                ev = es.events || {},
                f, i, len, ename, fn, k;

            for(ename in ev){
                if(ev.hasOwnProperty(ename)){
                    f = ev[ename];
                    for (i = 0, len = f.length; i < len; i++) {
                        fn = f[i][0];
                        if(fn.task) {
                            fn.task.cancel();
                            delete fn.task;
                        }
                        if(fn.tasks && (k = fn.tasks.length)) {
                            while(k--) {
                                fn.tasks[k].cancel();
                            }
                            delete fn.tasks;
                        }
                        E.un(el, ename, E.extAdapter ? f[i][3] : f[i][1]);
                    }
                }
            }
            elCache[id] && (elCache[id].events = {});
        },

        getListeners : function(el, eventName) {
            el = Ext.getDom(el);
            if (!el) {
                return;
            }
            var id = (Ext.get(el)||{}).id,
                elCache = resolveCache(el),
                es = ( elCache[id] || {} ).events || {};

            return es[eventName] || null;
        },

        purgeElement : function(el, recurse, eventName) {
            el = Ext.getDom(el);
            var id = el.id,
                elCache = resolveCache(el),
                es = (elCache[id] || {}).events || {},
                i, f, len;
            if (eventName) {
                if (es.hasOwnProperty(eventName)) {
                    f = es[eventName];
                    for (i = 0, len = f.length; i < len; i++) {
                        Ext.EventManager.removeListener(el, eventName, f[i][0]);
                    }
                }
            } else {
                Ext.EventManager.removeAll(el);
            }
            if (recurse && el && el.childNodes) {
                for (i = 0, len = el.childNodes.length; i < len; i++) {
                    Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
                }
            }
        }
    });
    
    // deprecated, call from EventManager
    E.getListeners = function(el, eventName) {
       return Ext.EventManager.getListeners(el, eventName);
    };

    /** @sourceURL=<multidom.js> */
    Ext.provide && Ext.provide('multidom');
 })();/* global Ext */
/*
 * Copyright 2007-2009, Active Group, Inc.  All rights reserved.
 * ******************************************************************************
 * This file is distributed on an AS IS BASIS WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * ***********************************************************************************
 * @version 2.1
 * [For Ext 3.1 or higher only]
 *
 * License: ux.ManagedIFrame, ux.ManagedIFrame.Panel, ux.ManagedIFrame.Portlet, ux.ManagedIFrame.Window  
 * are licensed under the terms of the Open Source GPL 3.0 license:
 * http://www.gnu.org/licenses/gpl.html
 *
 * Commercial use is prohibited without a Commercial Developement License. See
 * http://licensing.theactivegroup.com.
 *
 * Donations are welcomed: http://donate.theactivegroup.com
 *
 */
 
(function(){
    
    var El = Ext.Element, 
        ElFrame, 
        ELD = Ext.lib.Dom,
        EMPTYFN = function(){},
        OP = Object.prototype,
        addListener = function () {
            var handler;
            if (window.addEventListener) {
                handler = function F(el, eventName, fn, capture) {
                    el.addEventListener(eventName, fn, !!capture);
                };
            } else if (window.attachEvent) {
                handler = function F(el, eventName, fn, capture) {
                    el.attachEvent("on" + eventName, fn);
                };
            } else {
                handler = function F(){};
            }
            var F = null; //Gbg collect
            return handler;
        }(),
       removeListener = function() {
            var handler;
            if (window.removeEventListener) {
                handler = function F(el, eventName, fn, capture) {
                    el.removeEventListener(eventName, fn, (capture));
                };
            } else if (window.detachEvent) {
                handler = function F(el, eventName, fn) {
                    el.detachEvent("on" + eventName, fn);
                };
            } else {
                handler = function F(){};
            }
            var F = null; //Gbg collect
            return handler;
        }();
 
  //assert multidom support: REQUIRED for Ext 3 or higher!
  if(typeof ELD.getDocument != 'function'){
     throw "MIF 2.1 requires multidom support" ;
  }
  //assert Ext 3.0.3 + , SVN
  if(!Ext.isDefined(Ext.elCache)){
     throw "MIF 2.1 requires Ext 3.1 or higher." ;
  }
  
  Ext.ns('Ext.ux.ManagedIFrame', 'Ext.ux.plugin');
  
  var MIM, MIF = Ext.ux.ManagedIFrame, MIFC;
  var frameEvents = ['documentloaded',
                     'domready',
                     'focus',
                     'blur',
                     'resize',
                     'scroll',
                     'unload',
                     'scroll',
                     'exception', 
                     'message',
                     'reset'];
                     
    var reSynthEvents = new RegExp('^('+frameEvents.join('|')+ ')', 'i');

    /**
     * @class Ext.ux.ManagedIFrame.Element
     * @extends Ext.Element
     * @version 2.1 
     * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a> 
     * @author Doug Hendricks. Forum ID: <a href="http://extjs.com/forum/member.php?u=8730">hendricd</a> 
     * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
     * @copyright 2007-2009, Active Group, Inc. All rights reserved.
     * @constructor Create a new Ext.ux.ManagedIFrame.Element directly. 
     * @param {String/HTMLElement} element
     * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
     * @param {DocumentElement} (optional) Document context uses to resolve an Element search by its id.
     */
     
    Ext.ux.ManagedIFrame.Element = Ext.extend(Ext.Element, {
                         
            constructor : function(element, forceNew, doc ){
                var d = doc || document;
                var elCache  = ELD.resolveDocumentCache(d);
                var dom = Ext.getDom(element, d);
                if(!dom || !(/^(iframe|frame)/i).test(dom.tagName)) { // invalid id/element
                    return null;
                }
                var id = Ext.id(dom);
                
                /**
                 * The DOM element
                 * @type HTMLElement
                 */
                this.dom = dom;
                
                /**
                 * The DOM element ID
                 * @type String
                 */
                this.id = id ;
                
                (elCache[id] || 
                   (elCache[id] = {
                     el: this,
                     events : {},
                     data : {}
                    })
                ).el = this;
                
                this.dom.name || (this.dom.name = this.id);
                 
                if(Ext.isIE){
                     document.frames && (document.frames[this.dom.name] || (document.frames[this.dom.name] = this.dom));
                 }
                 
                this.dom.ownerCt = this;
                MIM.register(this);

                if(!this._observable){
	                    (this._observable = new Ext.util.Observable()).addEvents(
	                    
	                    /**
	                     * Fires when the iFrame has reached a loaded/complete state.
	                     * @event documentloaded
	                     * @param {Ext.ux.MIF.Element} this
	                     */
	                    'documentloaded',
	                    
	                    /**
	                     * Fires ONLY when an iFrame's Document(DOM) has reach a
	                     * state where the DOM may be manipulated ('same origin' policy)
	                     * Note: This event is only available when overwriting the iframe
	                     * document using the update or load methods and "same-origin"
	                     * documents. Returning false from the eventHandler stops further event
	                     * (documentloaded) processing.
	                     * @event domready 
	                     * @param {Ext.ux.MIF.Element} this
	                     */
	
	                    'domready',
	                    
	                    /**
	                     * Fires when the frame actions raise an error
	                     * @event exception
	                     * @param {Ext.ux.MIF.Element} this.iframe
	                     * @param {Error/string} exception
	                     */
	                     'exception',
	                     
	                    /**
	                     * Fires when the frame's window is resized.  This event, when raised from a "same-origin" frame,
	                     * will send current height/width reports with the event.
	                     * @event resize
	                     * @param {Ext.ux.MIF.Element} this.iframe
	                     * @param {Object} documentSize A height/width object signifying the new document size
	                     * @param {Object} viewPortSize A height/width object signifying the size of the frame's viewport
	                     * @param {Object} viewSize A height/width object signifying the size of the frame's view
	                     */
	                     'resize',
	                     
	                    /**
	                     * Fires upon receipt of a message generated by window.sendMessage
	                     * method of the embedded Iframe.window object
	                     * @event message
	                     * @param {Ext.ux.MIF} this.iframe
	                     * @param {object}
	                     *            message (members: type: {string} literal "message", data
	                     *            {Mixed} [the message payload], domain [the document domain
	                     *            from which the message originated ], uri {string} the
	                     *            document URI of the message sender source (Object) the
	                     *            window context of the message sender tag {string} optional
	                     *            reference tag sent by the message sender
	                     * <p>Alternate event handler syntax for message:tag filtering Fires upon
	                     * receipt of a message generated by window.sendMessage method which
	                     * includes a specific tag value of the embedded Iframe.window object
	                     */
	                    'message',
	
	                    /**
	                     * Fires when the frame is blurred (loses focus).
	                     * @event blur
	                     * @param {Ext.ux.MIF} this
	                     * @param {Ext.Event}
	                     *            Note: This event is only available when overwriting the
	                     *            iframe document using the update method and to pages
	                     *            retrieved from a "same domain". Returning false from the
	                     *            eventHandler [MAY] NOT cancel the event, as this event is
	                     *            NOT ALWAYS cancellable in all browsers.
	                     */
	                     'blur',
	
	                    /**
	                     * Fires when the frame gets focus. Note: This event is only available
	                     * when overwriting the iframe document using the update method and to
	                     * pages retrieved from a "same domain". Returning false from the
	                     * eventHandler [MAY] NOT cancel the event, as this event is NOT ALWAYS
	                     * cancellable in all browsers.
	                     * @event focus
	                     * @param {Ext.ux.MIF.Element} this
	                     * @param {Ext.Event}
	                     *
	                    */
	                    'focus',
	
	                    /**
	                     * Note: This event is only available when overwriting the iframe
	                     * document using the update method and to pages retrieved from a "same-origin"
	                     * domain. Note: Opera does not raise this event.
	                     * @event unload * Fires when(if) the frames window object raises the unload event
	                     * @param {Ext.ux.MIF.Element} this.
	                     * @param {Ext.Event}
	                     */
	                     'unload',
	                     
	                     /**
	                     * Note: This event is only available when overwriting the iframe
	                     * document using the update method and to pages retrieved from a "same-origin"
	                     * domain.  To prevent numerous scroll events from being raised use the buffer listener 
	                     * option to limit the number of times the event is raised.
	                     * @event scroll 
	                     * @param {Ext.ux.MIF.Element} this.
	                     * @param {Ext.Event}
	                     */
	                     'scroll',
	                     
	                    /**
	                     * Fires when the iFrame has been reset to a neutral domain state (blank document).
	                     * @event reset
	                     * @param {Ext.ux.MIF.Element} this
	                     */
	                    'reset'
	                 );
	                    //  Private internal document state events.
	                 this._observable.addEvents('_docready','_docload');
                 } 
                 // Hook the Iframes loaded and error state handlers
                 this.dom[Ext.isIE?'onreadystatechange':'onload'] =
                    this.dom['onerror'] = this.loadHandler.createDelegate(this);
                
            },

            /** @private
             * Removes the MIFElement interface from the FRAME Element.
             * It does NOT remove the managed FRAME from the DOM.  Use the {@link Ext.#ux.ManagedIFrame.Element-remove} method to perfom both functions.
             */
            destructor   :  function () {
                this.dom[Ext.isIE?'onreadystatechange':'onload'] = this.dom['onerror'] = EMPTYFN;
                MIM.deRegister(this);
                this.removeAllListeners();
                Ext.destroy(this.frameShim, this.DDM);
                this.hideMask(true);
                delete this.loadMask;
                this.reset(); 
                this.manager = null;
                this.dom.ownerCt = null;
            },
            
            /**
             * Deep cleansing childNode Removal
             * @param {Boolean} forceReclean (optional) By default the element
             * keeps track if it has been cleansed already so
             * you can call this over and over. However, if you update the element and
             * need to force a reclean, you can pass true.
             * @param {Boolean} deep (optional) Perform a deep cleanse of all childNodes as well.
             */
            cleanse : function(forceReclean, deep){
                if(this.isCleansed && forceReclean !== true){
                    return this;
                }
                var d = this.dom, n = d.firstChild, nx;
                while(d && n){
                     nx = n.nextSibling;
                     deep && Ext.fly(n).cleanse(forceReclean, deep);
                     Ext.removeNode(n);
                     n = nx;
                }
                this.isCleansed = true;
                return this;
            },

            /** (read-only) The last known URI set programmatically by the Component
             * @property  
             * @type {String|Function}
             */
            src     : null,

            /** (read-only) For "same-origin" frames only.  Provides a reference to
             * the Ext.util.CSS singleton to manipulate the style sheets of the frame's
             * embedded document.
             *
             * @property
             * @type Ext.util.CSS
             */
            CSS     : null,

            /** Provides a reference to the managing Ext.ux.MIF.Manager instance.
             *
             * @property
             * @type Ext.ux.MIF.Manager
             */
            manager : null,

            /**
              * Enables/disables internal cross-frame messaging interface
              * @cfg {Boolean} disableMessaging False to enable cross-frame messaging API
              * Default = true
              *
              */
            disableMessaging  :  true,

             /**
              * Maximum number of domready event detection retries for IE.  IE does not provide
              * a native DOM event to signal when the frames DOM may be manipulated, so a polling process
              * is used to determine when the documents BODY is available. <p> Certain documents may not contain
              * a BODY tag:  eg. MHT(rfc/822), XML, or other non-HTML content. Detection polling will stop after this number of 2ms retries 
              * or when the documentloaded event is raised.</p>
              * @cfg {Integer} domReadyRetries 
              * @default 7500 (* 2 = 15 seconds) 
              */
            domReadyRetries   :  7500,
            
            /**
             * True to set focus on the frame Window as soon as its document
             * reports loaded.  <p>(Many external sites use IE's document.createRange to create 
             * DOM elements, but to be successful, IE requires that the FRAME have focus before
             * such methods are called)</p>
             * @cfg focusOnLoad
             * @default true if IE
             */
            focusOnLoad   : Ext.isIE,
            
            /**
              * Enables/disables internal cross-frame messaging interface
              * @cfg {Boolean} disableMessaging False to enable cross-frame messaging API
              * Default = true
              *
              */
            eventsFollowFrameLinks   : true,

            /** @private */
            _domCache      : null,

            /**
             * Removes the FRAME from the DOM and deletes it from the cache
             */
            remove  : function(){
                this.destructor.apply(this, arguments);
                ElFrame.superclass.remove.apply(this,arguments);
            },
            
            /**
             * Return the ownerDocument property of the IFRAME Element.
             * (Note: This is not the document context of the FRAME's loaded document. 
             * See the getFrameDocument method for that.)
             */
            getDocument :  
                function(){ return this.dom ? this.dom.ownerDocument : document;},
            
            /**
	         * Loads the frame Element with the response from a form submit to the 
	         * specified URL with the ManagedIframe.Element as it's submit target.
	         *
	         * @param {Object} submitCfg A config object containing any of the following options:
	         * <pre><code>
	         *      myIframe.submitAsTarget({
	         *         form : formPanel.form,  //optional Ext.FormPanel, Ext form element, or HTMLFormElement
	         *         url: &quot;your-url.php&quot;,
             *         action : (see url) ,
	         *         params: {param1: &quot;foo&quot;, param2: &quot;bar&quot;}, // or URL encoded string or function that returns either
	         *         callback: yourFunction,  //optional, called with the signature (frame)
	         *         scope: yourObject, // optional scope for the callback
	         *         method: 'POST', //optional form.method 
             *         encoding : "multipart/form-data" //optional, default = HTMLForm default  
	         *      });
	         *
	         * </code></pre>
             * @return {Ext.ux.ManagedIFrame.Element} this
	         *
	         */
            submitAsTarget : function(submitCfg){
                var opt = submitCfg || {}, 
                D = this.getDocument(),
  	            form = Ext.getDom(
                       opt.form ? opt.form.form || opt.form: null, 
                    D) || Ext.DomHelper.append(D.body, { 
                    tag: 'form', 
                    cls : 'x-hidden x-mif-form',
                    encoding : 'multipart/form-data'
                  }),
                formFly = Ext.fly(form, '_dynaForm'),
                formState = {
                    target: form.target || '',
                    method: form.method || '',
                    encoding: form.encoding || '',
                    enctype: form.enctype || '',
                    action: form.action || '' 
                 },
                encoding = opt.encoding || form.encoding,
                method = opt.method || form.method || 'POST';
        
                formFly.set({
                   target  : this.dom.name,
                   method  : method,
                   encoding: encoding,
                   action  : opt.url || opt.action || form.action
                });
                
                if(method == 'POST' || !!opt.enctype){
                    formFly.set({enctype : opt.enctype || form.enctype || encoding});
                }
                
		        var hiddens, hd, ps;
                // add any additional dynamic params
		        if(opt.params && (ps = Ext.isFunction(opt.params) ? opt.params() : opt.params)){ 
		            hiddens = [];
                     
		            Ext.iterate(ps = typeof ps == 'string'? Ext.urlDecode(ps, false): ps, 
                        function(n, v){
		                    Ext.fly(hd = D.createElement('input')).set({
		                     type : 'hidden',
		                     name : n,
		                     value: v
                            });
		                    form.appendChild(hd);
		                    hiddens.push(hd);
		                });
		        }
		
		        opt.callback && 
                    this._observable.addListener('_docready',opt.callback, opt.scope,{single:true});
                     
                this._frameAction = true;
                this._targetURI = location.href;
		        this.showMask();
		        
		        //slight delay for masking
		        (function(){
                    
		            form.submit();
                    // remove dynamic inputs
		            hiddens && Ext.each(hiddens, Ext.removeNode, Ext);

                    //Remove if dynamically generated, restore state otherwise
		            if(formFly.hasClass('x-mif-form')){
                        formFly.remove();
                    }else{
                        formFly.set(formState);
                    }
                    delete El._flyweights['_dynaForm'];
                    formFly = null;
		            this.hideMask(true);
		        }).defer(100, this);
                
                return this;
		    },

            /**
             * @cfg {String} resetUrl Frame document reset string for use with the {@link #Ext.ux.ManagedIFrame.Element-reset} method.
             * Defaults:<p> For IE on SSL domains - the current value of Ext.SSL_SECURE_URL<p> "about:blank" for all others.
             */
            resetUrl : (function(){
                return Ext.isIE && Ext.isSecure ? Ext.SSL_SECURE_URL : 'about:blank';
            })(),

            /**
             * Sets the embedded Iframe src property. Note: invoke the function with
             * no arguments to refresh the iframe based on the current src value.
             *
             * @param {String/Function} url (Optional) A string or reference to a Function that
             *            returns a URI string when called
             * @param {Boolean} discardUrl (Optional) If not passed as <tt>false</tt>
             *            the URL of this action becomes the default SRC attribute
             *            for this iframe, and will be subsequently used in future
             *            setSrc calls (emulates autoRefresh by calling setSrc
             *            without params).
             * @param {Function} callback (Optional) A callback function invoked when the
             *            frame document has been fully loaded.
             * @param {Object} scope (Optional) scope by which the callback function is
             *            invoked.
             */
            setSrc : function(url, discardUrl, callback, scope) {
                var src = url || this.src || this.resetUrl;
                
                var O = this._observable;
                this._unHook();
                Ext.isFunction(callback) && O.addListener('_docload', callback, scope||this, {single:true});
                this.showMask();
                (discardUrl !== true) && (this.src = src);
                var s = this._targetURI = (Ext.isFunction(src) ? src() || '' : src);
                try {
                    this._frameAction = true; // signal listening now
                    this.dom.src = s;
                    this.checkDOM();
                } catch (ex) {
                    O.fireEvent.call(O, 'exception', this, ex);
                }
                return this;
            },

            /**
             * Sets the embedded Iframe location using its replace method (precluding a history update). 
             * Note: invoke the function with no arguments to refresh the iframe based on the current src value.
             *
             * @param {String/Function} url (Optional) A string or reference to a Function that
             *            returns a URI string when called
             * @param {Boolean} discardUrl (Optional) If not passed as <tt>false</tt>
             *            the URL of this action becomes the default SRC attribute
             *            for this iframe, and will be subsequently used in future
             *            setSrc calls (emulates autoRefresh by calling setSrc
             *            without params).
             * @param {Function} callback (Optional) A callback function invoked when the
             *            frame document has been fully loaded.
             * @param {Object} scope (Optional) scope by which the callback function is
             *            invoked.
             *
             */
            setLocation : function(url, discardUrl, callback, scope) {

                var src = url || this.src || this.resetUrl;
                var O = this._observable;
                this._unHook();
                Ext.isFunction(callback) && O.addListener('_docload', callback, scope||this, {single:true});
                this.showMask();
                var s = this._targetURI = (Ext.isFunction(src) ? src() || '' : src);
                if (discardUrl !== true) {
                    this.src = src;
                }
                try {
                    this._frameAction = true; // signal listening now
                    this.getWindow().location.replace(s);
                    this.checkDOM();
                } catch (ex) {
                    O.fireEvent.call(O,'exception', this, ex);
                }
                return this;
            },

            /**
             * Resets the frame to a neutral (blank document) state without
             * loadMasking.
             *
             * @param {String}
             *            src (Optional) A specific reset string (eg. 'about:blank')
             *            to use for resetting the frame.
             * @param {Function}
             *            callback (Optional) A callback function invoked when the
             *            frame reset is complete.
             * @param {Object}
             *            scope (Optional) scope by which the callback function is
             *            invoked.
             */
            reset : function(src, callback, scope) {
                
                this._unHook();
                var loadMaskOff = false;
                var s = src, win = this.getWindow();
                if(this.loadMask){
                    loadMaskOff = this.loadMask.disabled;
                    this.loadMask.disabled = false;
                 }
                this.hideMask(true);
                
                if(win){
                    this.isReset= true;
                    var cb = callback;
	                this._observable.addListener('_docload',
	                  function(frame) {
	                    if(this.loadMask){
	                        this.loadMask.disabled = loadMaskOff;
	                    };
	                    Ext.isFunction(cb) &&  (cb = cb.apply(scope || this, arguments));
                        this._observable.fireEvent("reset", this);
	                }, this, {single:true});
	            
                    Ext.isFunction(s) && ( s = src());
                    s = this._targetURI = Ext.isEmpty(s, true)? this.resetUrl: s;
                    win.location.href = s;
                }
                
                return this;
            },

           /**
            * @private
            * Regular Expression filter pattern for script tag removal.
            * @cfg {regexp} scriptRE script removal RegeXp
            * Default: "/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/gi"
            */
            scriptRE : /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/gi,

            /**
             * Write(replacing) string content into the IFrames document structure
             * @param {String} content The new content
             * @param {Boolean} loadScripts
             * (optional) true to also render and process embedded scripts
             * @param {Function} callback (Optional) A callback function invoked when the
             * frame document has been written and fully loaded. @param {Object}
             * scope (Optional) scope by which the callback function is invoked.
             */
            update : function(content, loadScripts, callback, scope) {
                loadScripts = loadScripts || this.getUpdater().loadScripts || false;
                content = Ext.DomHelper.markup(content || '');
                content = loadScripts === true ? content : content.replace(this.scriptRE, "");
                var doc;
                if ((doc = this.getFrameDocument()) && !!content.length) {
                    this._unHook();
                    this.src = null;
                    this.showMask();
                    Ext.isFunction(callback) &&
                        this._observable.addListener('_docload', callback, scope||this, {single:true});
                    this._targetURI = location.href;
                    doc.open();
                    this._frameAction = true;
                    doc.write(content);
                    doc.close();
                    this.checkDOM();

                } else {
                    this.hideMask(true);
                    Ext.isFunction(callback) && callback.call(scope, this);
                }
                
                return this;
            },
            
            /**
             * Executes a Midas command on the current document, current selection, or the given range.
             * @param {String} command The command string to execute in the frame's document context.
             * @param {Booloean} userInterface (optional) True to enable user interface (if supported by the command)
             * @param {Mixed} value (optional)
             * @param {Boolean} validate If true, the command is validated to ensure it's invocation is permitted.
             * @return {Boolean} indication whether command execution succeeded
             */
            execCommand : function(command, userInterface, value, validate){
               var doc, assert;
               if ((doc = this.getFrameDocument()) && !!command) {
                  try{
                      Ext.isIE && this.getWindow().focus();
	                  assert = validate && Ext.isFunction(doc.queryCommandEnabled) ? 
	                    doc.queryCommandEnabled(command) : true;
                  
                      return assert && doc.execCommand(command, !!userInterface, value);
                  }catch(eex){return false;}
               }
               return false;
                
            },

            /**
             * Sets the current DesignMode attribute of the Frame's document
             * @param {Boolean/String} active True (or "on"), to enable designMode
             * 
             */
            setDesignMode : function(active){
               var doc;
               (doc = this.getFrameDocument()) && 
                 (doc.designMode = (/on|true/i).test(String(active))?'on':'off');
            },
            
            /**
            * Gets this element's Updater
            * 
            * @return {Ext.ux.ManagedIFrame.Updater} The Updater
            */
            getUpdater : function(){
               return this.updateManager || 
                    (this.updateManager = new MIF.Updater(this));
                
            },

            /**
             * Method to retrieve frame's history object.
             * @return {object} or null if permission was denied
             */
            getHistory  : function(){
                var h=null;
                try{ h=this.getWindow().history; }catch(eh){}
                return h;
            },
            
            /**
             * Method to retrieve embedded frame Element objects. Uses simple
             * caching (per frame) to consistently return the same object.
             * Automatically fixes if an object was recreated with the same id via
             * AJAX or DOM.
             *
             * @param {Mixed}
             *            el The id of the node, a DOM Node or an existing Element.
             * @return {Element} The Element object (or null if no matching element
             *         was found)
             */
            get : function(el) {
                var doc = this.getFrameDocument();
                return doc? Ext.get(el, doc) : doc=null;
            },

            /**
             * Gets the globally shared flyweight Element for the frame, with the
             * passed node as the active element. Do not store a reference to this
             * element - the dom node can be overwritten by other code.
             *
             * @param {String/HTMLElement}
             *            el The dom node or id
             * @param {String}
             *            named (optional) Allows for creation of named reusable
             *            flyweights to prevent conflicts (e.g. internally Ext uses
             *            "_internal")
             * @return {Element} The shared Element object (or null if no matching
             *         element was found)
             */
            fly : function(el, named) {
                var doc = this.getFrameDocument();
                return doc ? Ext.fly(el,named, doc) : null;
            },

            /**
             * Return the dom node for the passed string (id), dom node, or
             * Ext.Element relative to the embedded frame document context.
             *
             * @param {Mixed} el
             * @return HTMLElement
             */
            getDom : function(el) {
                var d;
                if (!el || !(d = this.getFrameDocument())) {
                    return (d=null);
                }
                return Ext.getDom(el, d);
            },
            
            /**
             * Creates a {@link Ext.CompositeElement} for child nodes based on the
             * passed CSS selector (the selector should not contain an id).
             *
             * @param {String} selector The CSS selector
             * @param {Boolean} unique (optional) True to create a unique Ext.Element for
             *            each child (defaults to false, which creates a single
             *            shared flyweight object)
             * @return {Ext.CompositeElement/Ext.CompositeElementLite} The composite element
             */
            select : function(selector, unique) {
                var d; return (d = this.getFrameDocument()) ? Ext.Element.select(selector,unique, d) : d=null;
            },

            /**
             * Selects frame document child nodes based on the passed CSS selector
             * (the selector should not contain an id).
             *
             * @param {String} selector The CSS selector
             * @return {Array} An array of the matched nodes
             */
            query : function(selector) {
                var d; return (d = this.getFrameDocument()) ? Ext.DomQuery.select(selector, d): null;
            },
            
            /**
             * Removes a DOM Element from the embedded document
             * @param {Element/String} node The node id or node Element to remove
             */
            removeNode : Ext.removeNode,
            
            /**
             * @private execScript sandbox and messaging interface
             */ 
            _renderHook : function() {
                this._windowContext = null;
                this.CSS = this.CSS ? this.CSS.destroy() : null;
                this._hooked = false;
                try {
                    if (this.writeScript('(function(){(window.hostMIF = parent.document.getElementById("'
                                    + this.id
                                    + '").ownerCt)._windowContext='
                                    + (Ext.isIE
                                            ? 'window'
                                            : '{eval:function(s){return new Function("return ("+s+")")();}}')
                                    + ';})()')) {
                        var w, p = this._frameProxy, D = this.getFrameDocument();
                        if(w = this.getWindow()){
                            p || (p = this._frameProxy = this._eventProxy.createDelegate(this));    
                            addListener(w, 'focus', p);
                            addListener(w, 'blur', p);
                            addListener(w, 'resize', p);
                            addListener(w, 'unload', p);
                            D && addListener(Ext.isIE ? w : D, 'scroll', p);
                        }
                        
                        D && (this.CSS = new Ext.ux.ManagedIFrame.CSS(D));
                       
                    }
                } catch (ex) {}
                return this.domWritable();
            },
            
             /** @private : clear all event listeners and Element cache */
            _unHook : function() {
                if (this._hooked) {
                    var id, el, c = this._domCache;
                    if(c){
                      for ( id in c ) {
                        el = c[id].el;
                        el && el.removeAllListeners && el.removeAllListeners();
                        el && (c[id].el = el = null);
                        delete c[id].data;
                        delete c[id];
                      }
                    }
                    
                    this._windowContext && (this._windowContext.hostMIF = null);
                    this._windowContext = null;
                
                    var w, p = this._frameProxy;
                    if(p && this.domWritable() && (w = this.getWindow())){
                        removeListener(w, 'focus', p);
                        removeListener(w, 'blur', p);
                        removeListener(w, 'resize', p);
                        removeListener(w, 'unload', p);
                        removeListener(Ext.isIE ? w : this.getFrameDocument(), 'scroll', p);
                    }
                }
                MIM._flyweights = {};
                this._domCache = null;
                ELD.clearDocumentCache && ELD.clearDocumentCache(this.id);
                this.CSS = this.CSS ? this.CSS.destroy() : null;
                this.domFired = this._frameAction = this.domReady = this._hooked = false;
            },
            
            /** @private */
            _windowContext : null,

            /**
             * If sufficient privilege exists, returns the frame's current document
             * as an HTMLElement.
             *
             * @return {HTMLElement} The frame document or false if access to document object was denied.
             */
            getFrameDocument : function() {
                var win = this.getWindow(), doc = null;
                try {
                    doc = (Ext.isIE && win ? win.document : null)
                            || this.dom.contentDocument
                            || window.frames[this.dom.name].document || null;
                } catch (gdEx) {
                    this._domCache = null;
                    
                    ELD.clearDocumentCache && ELD.clearDocumentCache(this.id);
                    return false; // signifies probable access restriction
                }
                doc = (doc && Ext.isFunction(ELD.getDocument)) ? ELD.getDocument(doc,true) : doc;
                
                if(doc){
                  this._domCache || (this._domCache = ELD.resolveDocumentCache(doc, this.id));
                }
                
                return doc;
            },

            /**
             * Returns the frame's current HTML document object as an
             * {@link Ext.Element}.
             * @return {Ext.Element} The document
             */
            getDoc : function() {
                var D = this.getFrameDocument();
                return Ext.get(D,D); 
            },
            
            /**
             * If sufficient privilege exists, returns the frame's current document
             * body as an HTMLElement.
             *
             * @return {HTMLElement} The frame document body or Null if access to
             *         document object was denied.
             */
            getBody : function() {
                var d;
                return (d = this.getFrameDocument()) ? this.get(d.body || d.documentElement) : null;
            },

            /**
             * Attempt to retrieve the frames current URI via frame's document object
             * @return {string} The frame document's current URI or the last know URI if permission was denied.
             */
            getDocumentURI : function() {
                var URI, d;
                try {
                    URI = this.src && (d = this.getFrameDocument()) ? d.location.href: null;
                } catch (ex) { // will fail on NON-same-origin domains
                }
                return URI || (Ext.isFunction(this.src) ? this.src() : this.src);
                // fallback to last known
            },

           /**
            * Attempt to retrieve the frames current URI via frame's Window object
            * @return {string} The frame document's current URI or the last know URI if permission was denied.
            */
            getWindowURI : function() {
                var URI, w;
                try {
                    URI = (w = this.getWindow()) ? w.location.href : null;
                } catch (ex) {
                } // will fail on NON-same-origin domains
                return URI || (Ext.isFunction(this.src) ? this.src() : this.src);
                // fallback to last known
            },

            /**
             * Returns the frame's current window object.
             *
             * @return {Window} The frame Window object.
             */
            getWindow : function() {
                var dom = this.dom, win = null;
                try {
                    win = dom.contentWindow || window.frames[dom.name] || null;
                } catch (gwEx) {}
                return win;
            },
            
            /**
             * Scrolls a frame document's child element into view within the passed container.
             * @param {String} child The id of the element to scroll into view. 
             * @param {Mixed} container (optional) The container element to scroll (defaults to the frame's document.body).  Should be a 
             * string (id), dom node, or Ext.Element.
             * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
             * @return {Ext.ux.ManagedIFrame.Element} this 
             */ 
            scrollChildIntoView : function(child, container, hscroll){
                this.fly(child, '_scrollChildIntoView').scrollIntoView(this.getDom(container) || this.getBody().dom, hscroll);
                return this;
            },

            /**
             * Print the contents of the Iframes (if we own the document)
             * @return {Ext.ux.ManagedIFrame.Element} this 
             */
            print : function() {
                try {
                    var win;
                    if( win = this.getWindow()){
                        Ext.isIE && win.focus();
                        win.print();
                    }
                } catch (ex) {
                    throw new MIF.Error('printexception' , ex.description || ex.message || ex);
                }
                return this;
            },

            /**
             * Returns the general DOM modification capability (same-origin status) of the frame. 
             * @return {Boolean} accessible If True, the frame's inner DOM can be manipulated, queried, and
             * Event Listeners set.
             */
            domWritable : function() {
                return !!Ext.isDocument(this.getFrameDocument(),true) //test access
                    && !!this._windowContext;
            },

            /**
             * eval a javascript code block(string) within the context of the
             * Iframes' window object.
             * @param {String} block A valid ('eval'able) script source block.
             * @param {Boolean} useDOM  if true, inserts the function
             * into a dynamic script tag, false does a simple eval on the function
             * definition. (useful for debugging) <p> Note: will only work after a
             * successful iframe.(Updater) update or after same-domain document has
             * been hooked, otherwise an exception is raised.
             * @return {Mixed}  
             */
            execScript : function(block, useDOM) {
                try {
                    if (this.domWritable()) {
                        if (useDOM) {
                            this.writeScript(block);
                        } else {
                            return this._windowContext.eval(block);
                        }
                    } else {
                        throw new MIF.Error('execscript-secure-context');
                    }
                } catch (ex) {
                    this._observable.fireEvent.call(this._observable,'exception', this, ex);
                    return false;
                }
                return true;
            },

            /**
             * Write a script block into the iframe's document
             * @param {String} block A valid (executable) script source block.
             * @param {object} attributes Additional Script tag attributes to apply to the script
             * Element (for other language specs [vbscript, Javascript] etc.) <p>
             * Note: writeScript will only work after a successful iframe.(Updater)
             * update or after same-domain document has been hooked, otherwise an
             * exception is raised.
             */
            writeScript : function(block, attributes) {
                attributes = Ext.apply({}, attributes || {}, {
                            type : "text/javascript",
                            text : block
                        });
                try {
                    var head, script, doc = this.getFrameDocument();
                    if (doc && typeof doc.getElementsByTagName != 'undefined') {
                        if (!(head = doc.getElementsByTagName("head")[0])) {
                            // some browsers (Webkit, Safari) do not auto-create
                            // head elements during document.write
                            head = doc.createElement("head");
                            doc.getElementsByTagName("html")[0].appendChild(head);
                        }
                        if (head && (script = doc.createElement("script"))) {
                            for (var attrib in attributes) {
                                if (attributes.hasOwnProperty(attrib)
                                        && attrib in script) {
                                    script[attrib] = attributes[attrib];
                                }
                            }
                            return !!head.appendChild(script);
                        }
                    }
                } catch (ex) {
                    this._observable.fireEvent.call(this._observable, 'exception', this, ex);

                }finally{
                    script = head = null;
                }
                return false;
            },

            /**
             * Eval a function definition into the iframe window context.
             * @param {String/Object} fn Name of the function or function map
             * object: {name:'encodeHTML',fn:Ext.util.Format.htmlEncode}
             * @param {Boolean} useDOM  if true, inserts the fn into a dynamic script tag,
             * false does a simple eval on the function definition
             * @param {Boolean} invokeIt if true, the function specified is also executed in the
             * Window context of the frame. Function arguments are not supported.
             * @example <pre><code> var trim = function(s){ return s.replace(/^\s+|\s+$/g,''); }; 
             * iframe.loadFunction('trim');
             * iframe.loadFunction({name:'myTrim',fn:String.prototype.trim || trim});</code></pre>
             */
            loadFunction : function(fn, useDOM, invokeIt) {
                var name = fn.name || fn;
                var fnSrc = fn.fn || window[fn];
                name && fnSrc && this.execScript(name + '=' + fnSrc, useDOM); // fn.toString coercion
                invokeIt && this.execScript(name + '()'); // no args only
            },

            /**
             * @private
             * Evaluate the Iframes readyState/load event to determine its
             * 'load' state, and raise the 'domready/documentloaded' event when
             * applicable.
             */
            loadHandler : function(e, target) {
                var rstatus = (e && typeof e.type !== 'undefined' ? e.type: this.dom.readyState);
                //console.log('lh', rstatus, this.isReset, this._frameAction, this.domReady, this.domFired, this.eventsFollowFrameLinks);
                if (this.eventsFollowFrameLinks || this._frameAction || this.isReset ) {
                                    
	                switch (rstatus) {
	                    case 'domready' : // MIF
                        case 'DOMFrameContentLoaded' :
	                    case 'domfail' : // MIF
	                        this._onDocReady (rstatus);
	                        break;
	                    case 'load' : // Gecko, Opera, IE
	                    case 'complete' :
	                        this._onDocLoaded(rstatus);
	                        break;
	                    case 'error':
	                        this._observable.fireEvent.apply(this._observable,['exception', this].concat(arguments));
	                        break;
	                    default :
	                }
                }
                this.frameState = rstatus;
            },

            /**
             * @private
             * @param {String} eventName
             */
            _onDocReady  : function(eventName ){
                var w, obv = this._observable, D;
                if(!this.isReset && this.focusOnLoad && (w = this.getWindow())){
                    w.focus();
                }
                //raise internal event regardless of state.
                obv.fireEvent("_docready", this);
                
                (D = this.getDoc()) && (D.isReady = true);
               
                if ( !this.domFired && 
                     (this._hooked = this._renderHook())) {
                        // Only raise if sandBox injection succeeded (same origin)
                        this.domFired = true;
                        this.isReset || obv.fireEvent.call(obv, 'domready', this);
                }
                
                this.domReady = true;
                this.hideMask();
            },

            /**
             * @private
             * @param {String} eventName
             */
            _onDocLoaded  : function(eventName ){
                var obv = this._observable, w;
                this.domReady || this._onDocReady('domready');
                
                obv.fireEvent("_docload", this);  //invoke any callbacks
                this.isReset || obv.fireEvent("documentloaded", this);
                this.hideMask(true);
                this._frameAction = this.isReset = false;
            },

            /**
             * @private
             * Poll the Iframes document structure to determine DOM ready
             * state, and raise the 'domready' event when applicable.
             */
            checkDOM : function( win) {
                if ( Ext.isGecko ) { return; }  //Ext.isOpera ||
                // initialise the counter
                var n = 0, frame = this, domReady = false,
                    b, l, d, 
                    max = this.domReadyRetries || 2500, //default max 5 seconds 
                    polling = false,
                    startLocation = (this.getFrameDocument() || {location : {}}).location.href;
                (function() { // DOM polling for IE and others
                    d = frame.getFrameDocument() || {location : {}};
                    // wait for location.href transition
                    polling = (d.location.href !== startLocation || d.location.href === frame._targetURI);
                    if ( frame.domReady) { return;}
                    domReady = polling && ((b = frame.getBody()) && !!(b.dom.innerHTML || '').length) || false;
                    // null href is a 'same-origin' document access violation,
                    // so we assume the DOM is built when the browser updates it
                    if (d.location.href && !domReady && (++n < max)) {
                        setTimeout(arguments.callee, 2); // try again
                        return;
                    }
                    frame.loadHandler({ type : domReady ? 'domready' : 'domfail'});
                })();
            },
            
            /**
            * @private 
            */
            filterEventOptionsRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,

           /**
            * @private override to handle synthetic events vs DOM events
            */
            addListener : function(eventName, fn, scope, options){

                if(typeof eventName == "object"){
                    var o = eventName;
                    for(var e in o){
                        if(this.filterEventOptionsRe.test(e)){
                            continue;
                        }
                        if(typeof o[e] == "function"){
                            // shared options
                            this.addListener(e, o[e], o.scope,  o);
                        }else{
                            // individual options
                            this.addListener(e, o[e].fn, o[e].scope, o[e]);
                        }
                    }
                    return;
                }

                if(reSynthEvents.test(eventName)){
                    var O = this._observable; 
                    if(O){
                        O.events[eventName] || (O.addEvents(eventName)); 
                        O.addListener.call(O, eventName, fn, scope || this, options) ;}
                }else {
                    ElFrame.superclass.addListener.call(this, eventName,
                            fn, scope || this, options);
                }
                return this;
            },

            /**
             * @private override
             * Removes an event handler from this element.
             */
            removeListener : function(eventName, fn, scope){
                var O = this._observable;
                if(reSynthEvents.test(eventName)){
                    O && O.removeListener.call(O, eventName, fn, scope || this, options);
                }else {
                  ElFrame.superclass.removeListener.call(this, eventName, fn, scope || this);
              }
              return this;
            },

            /**
             * Removes all previous added listeners from this element
             * @private override
             */
            removeAllListeners : function(){
                Ext.EventManager.removeAll(this.dom);
                var O = this._observable;
                O && O.purgeListeners.call(this._observable);
                return this;
            },
            
            /**
             * Forcefully show the defined loadMask
             * @param {String} msg Mask text to display during the mask operation, defaults to previous defined
             * loadMask config value.
             * @param {String} msgCls The CSS class to apply to the loading message element (defaults to "x-mask-loading")
             * @param {String} maskCls The CSS class to apply to the mask element
             */
            showMask : function(msg, msgCls, maskCls) {
                var lmask = this.loadMask;
                if (lmask && !lmask.disabled ){
                    this.mask(msg || lmask.msg, msgCls || lmask.msgCls, maskCls || lmask.maskCls, lmask.maskEl);
                }
            },
            
            /**
             * Hide the defined loadMask 
             * @param {Boolean} forced True to hide the mask regardless of document ready/loaded state.
             */
            hideMask : function(forced) {
                var tlm = this.loadMask || {};
                if (forced || (tlm.hideOnReady && this.domReady)) {
                     this.unmask();
                }
            },
            
            /**
             * Puts a mask over the FRAME to disable user interaction. Requires core.css.
             * @param {String} msg (optional) A message to display in the mask
             * @param {String} msgCls (optional) A css class to apply to the msg element
             * @param {String} maskCls (optional) A css class to apply to the mask element
             * @param {String/Element} maskEl (optional) A targeted Element (parent of the IFRAME) to use the masking agent
             * @return {Element} The mask element
             */
            mask : function(msg, msgCls, maskCls, maskEl){
                this._mask && this.unmask();
                var p = Ext.get(maskEl) || this.parent('.ux-mif-mask-target') || this.parent();
                if(p.getStyle("position") == "static" && 
                    !p.select('iframe,frame,object,embed').elements.length){
                        p.addClass("x-masked-relative");
                }
                
                p.addClass("x-masked");
                
                this._mask = Ext.DomHelper.append(p, {cls: maskCls || "ux-mif-el-mask"} , true);
                this._mask.setDisplayed(true);
                this._mask._agent = p;
                
                if(typeof msg == 'string'){
                     this._maskMsg = Ext.DomHelper.append(p, {cls: msgCls || "ux-mif-el-mask-msg" , style: {visibility:'hidden'}, cn:{tag:'div', html:msg}}, true);
                     this._maskMsg
                        .setVisibilityMode(Ext.Element.VISIBILITY)
                        .center(p).setVisible(true);
                }
                if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
                    this._mask.setSize(undefined, this._mask.getHeight());
                }
                return this._mask;
            },

            /**
             * Removes a previously applied mask.
             */
            unmask : function(){
                
                var a;
                if(this._mask){
                    (a = this._mask._agent) && a.removeClass(["x-masked-relative","x-masked"]);
                    if(this._maskMsg){
                        this._maskMsg.remove();
                        delete this._maskMsg;
                    }
                    this._mask.remove();
                    delete this._mask;
                }
             },

             /**
              * Creates an (frontal) transparent shim agent for the frame.  Used primarily for masking the frame during drag operations.
              * @return {Ext.Element} The new shim element.
              * @param {String} imgUrl Optional Url of image source to use during shimming (defaults to Ext.BLANK_IMAGE_URL).
              * @param {String} shimCls Optional CSS style selector for the shimming agent. (defaults to 'ux-mif-shim' ).
              * @return (HTMLElement} the shim element
              */
             createFrameShim : function(imgUrl, shimCls ){
                 this.shimCls = shimCls || this.shimCls || 'ux-mif-shim';
                 this.frameShim || (this.frameShim = this.next('.'+this.shimCls) ||  //already there ?
                  Ext.DomHelper.append(
                     this.dom.parentNode,{
                         tag : 'img',
                         src : imgUrl|| Ext.BLANK_IMAGE_URL,
                         cls : this.shimCls ,
                         galleryimg : "no"
                    }, true)) ;
                 this.frameShim && (this.frameShim.autoBoxAdjust = false); 
                 return this.frameShim;
             },
             
             /**
              * Toggles visibility of the (frontal) transparent shim agent for the frame.  Used primarily for masking the frame during drag operations.
              * @param {Boolean} show Optional True to activate the shim, false to hide the shim agent.
              */
             toggleShim : function(show){
                var shim = this.frameShim || this.createFrameShim();
                var cls = this.shimCls + '-on';
                !show && shim.removeClass(cls);
                show && !shim.hasClass(cls) && shim.addClass(cls);
             },

            /**
             * Loads this panel's iframe immediately with content returned from an XHR call.
             * @param {Object/String/Function} config A config object containing any of the following options:
             * <pre><code>
             *      frame.load({
             *         url: &quot;your-url.php&quot;,
             *         params: {param1: &quot;foo&quot;, param2: &quot;bar&quot;}, // or encoded string
             *         callback: yourFunction,
             *         scope: yourObject, // optional scope for the callback
             *         discardUrl: false,
             *         nocache: false,
             *         text: &quot;Loading...&quot;,
             *         timeout: 30,
             *         scripts: false,
             *         //optional custom renderer
             *         renderer:{render:function(el, response, updater, callback){....}}  
             *      });
             * </code></pre>
             * The only required property is url. The optional properties
             *            nocache, text and scripts are shorthand for
             *            disableCaching, indicatorText and loadScripts and are used
             *            to set their associated property on this panel Updater
             *            instance.
             * @return {Ext.ManagedIFrame.Element} this
             */
            load : function(loadCfg) {
                var um;
                if (um = this.getUpdater()) {
                    if (loadCfg && loadCfg.renderer) {
                        um.setRenderer(loadCfg.renderer);
                        delete loadCfg.renderer;
                    }
                    um.update.apply(um, arguments);
                }
                return this;
            },

             /** @private
              * Frame document event proxy
              */
             _eventProxy : function(e) {
                 if (!e) return;
                 e = Ext.EventObject.setEvent(e);
                 var be = e.browserEvent || e, er, args = [e.type, this];
                 
                 if (!be['eventPhase']
                         || (be['eventPhase'] == (be['AT_TARGET'] || 2))) {
                            
                     if(e.type == 'resize'){
	                    var doc = this.getFrameDocument();
	                    doc && (args.push(
	                        { height: ELD.getDocumentHeight(doc), width : ELD.getDocumentWidth(doc) },
	                        { height: ELD.getViewportHeight(doc), width : ELD.getViewportWidth(doc) },
	                        { height: ELD.getViewHeight(false, doc), width : ELD.getViewWidth(false, doc) }
	                      ));  
	                 }
                     
                     er =  this._observable ? 
                           this._observable.fireEvent.apply(this._observable, args.concat(
                              Array.prototype.slice.call(arguments,0))) 
                           : null;
                 
	                 // same-domain unloads should clear ElCache for use with the
	                 // next document rendering
	                 (e.type == 'unload') && this._unHook();
                     
                 }
                 return er;
            },
            
            /**
	         * dispatch a message to the embedded frame-window context (same-origin frames only)
	         * @name sendMessage
	         * @param {Mixed} message The message payload.  The payload can be any supported JS type. 
	         * @param {String} tag Optional reference tag 
	         * @param {String} origin Optional domain designation of the sender (defaults
	         * to document.domain).
	         */
	        sendMessage : function(message, tag, origin) {
	          //(implemented by mifmsg.js )
	        },
            
            /**
	         * Dispatch a cross-document message (per HTML5 specification) if the browser supports it natively.
	         * @name postMessage
	         * @param {String} message Required message payload (String only)
	         * @param {Array} ports Optional array of ports/channels. 
	         * @param {String} origin Optional domain designation of the sender (defaults
	         * to document.domain). 
	         * <p>Notes:  on IE8, this action is synchronous.
	         */
	        postMessage : function(message ,ports ,origin ){
	            //(implemented by mifmsg.js )
	        }

    });
   
    ElFrame = Ext.Element.IFRAME = Ext.Element.FRAME = Ext.ux.ManagedIFrame.Element;
    
      
    var fp = ElFrame.prototype;
    /**
     * @ignore
     */
    Ext.override ( ElFrame , {
          
    /**
     * Appends an event handler (shorthand for {@link #addListener}).
     * @param {String} eventName The type of event to handle
     * @param {Function} fn The handler function the event invokes
     * @param {Object} scope (optional) The scope (this element) of the handler function
     * @param {Object} options (optional) An object containing standard {@link #addListener} options
     * @member Ext.Element
     * @method on
     */
        on :  fp.addListener,
        
    /**
     * Removes an event handler from this element (shorthand for {@link #removeListener}).
     * @param {String} eventName the type of event to remove
     * @param {Function} fn the method the event invokes
     * @return {MIF.Element} this
     * @member Ext.Element
     * @method un
     */
        un : fp.removeListener,
        
        getUpdateManager : fp.getUpdater
    });

  /**
   * @class Ext.ux.ManagedIFrame.ComponentAdapter
   * @version 2.1 
   * @author Doug Hendricks. doug[always-At]theactivegroup.com
   * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
   * @copyright 2007-2009, Active Group, Inc.  All rights reserved.
   * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a>
   * @constructor
   * @desc
   * Abstract class.  This class should not be instantiated.
   */
  
   Ext.ux.ManagedIFrame.ComponentAdapter = function(){}; 
   Ext.ux.ManagedIFrame.ComponentAdapter.prototype = {
       
        /** @property */
        version : 2.01,
        
        /**
         * @cfg {String} defaultSrc the default src property assigned to the Managed Frame when the component is rendered.
         * @default null
         */
        defaultSrc : null,
        
        title      : '&#160;',
        
        /**
         * @cfg {String} unsupportedText Text to display when the IFRAMES/FRAMESETS are disabled by the browser.
         *
         */
        unsupportedText : 'Inline frames are NOT enabled\/supported by your browser.',
        
        hideMode   : !Ext.isIE && !!Ext.ux.plugin.VisibilityMode ? 'nosize' : 'display',
        
        animCollapse  : Ext.isIE ,

        animFloat  : Ext.isIE ,
        
        /**
         * @cfg {object} frameConfig Frames DOM configuration options
         * This optional configuration permits override of the IFRAME's DOM attributes
         * @example
          frameConfig : {
              name : 'framePreview',
              frameborder : 1,
              allowtransparency : true
             }
         */
        frameConfig  : null,
        
        /**
         * @cfg focusOnLoad True to set focus on the frame Window as soon as its document
         * reports loaded.  (Many external sites use IE's document.createRange to create 
         * DOM elements, but to be successfull IE requires that the FRAME have focus before
         * the method is called)
         * @default false
         */
        focusOnLoad   : false,
        
        /**
         * @property {Object} frameEl An {@link #Ext.ux.ManagedIFrame.Element} reference to rendered frame Element.
         */
        frameEl : null, 
  
        /**
         * @cfg {Boolean} useShim
         * True to use to create a transparent shimming agent for use in masking the frame during
         * drag operations.
         * @default false
         */
        useShim   : false,

        /**
         * @cfg {Boolean} autoScroll
         * True to use overflow:'auto' on the frame element and show scroll bars automatically when necessary,
         * false to clip any overflowing content (defaults to true).
         * @default true
         */
        autoScroll: true,
        
         /**
         * @cfg {String/Object} autoLoad
         * Loads this Components frame after the Component is rendered with content returned from an
         * XHR call or optionally from a form submission.  See {@link #Ext.ux.ManagedIFrame.ComponentAdapter-load} and {@link #Ext.ux.ManagedIFrame.ComponentAdapter-submitAsTarget} methods for
         * available configuration options.
         * @default null
         */
        autoLoad: null,
        
        /** @private */
        getId : function(){
             return this.id   || (this.id = "mif-comp-" + (++Ext.Component.AUTO_ID));
        },
        
        stateEvents : ['documentloaded'],
        
        stateful    : false,
        
        /**
         * Sets the autoScroll state for the frame.
         * @param {Boolean} auto True to set overflow:auto on the frame, false for overflow:hidden
         * @return {Ext.ux.ManagedIFrame.Component} this
         */
        setAutoScroll : function(auto){
            var scroll = Ext.value(auto, this.autoScroll === true);
            this.rendered && this.getFrame() &&  
                this.frameEl.setOverflow( (this.autoScroll = scroll) ? 'auto':'hidden');
            return this;
        },
        
        getContentTarget : function(){
            return this.getFrame();
        },
        
        /**
         * Returns the Ext.ux.ManagedIFrame.Element of the frame.
         * @return {Ext.ux.ManagedIFrame.Element} this.frameEl 
         */
        getFrame : function(){
             if(this.rendered){
                if(this.frameEl){ return this.frameEl;}
                var f = this.items && this.items.first ? this.items.first() : null;
                f && (this.frameEl = f.frameEl);
                return this.frameEl;
             }
             return null;
            },
        
        /**
         * Returns the frame's current window object.
         *
         * @return {Window} The frame Window object.
         */
        getFrameWindow : function() {
            return this.getFrame() ? this.frameEl.getWindow() : null;
        },

        /**
         * If sufficient privilege exists, returns the frame's current document
         * as an HTMLElement.
         *
         * @return {HTMLElement} The frame document or false if access to
         *         document object was denied.
         */
        getFrameDocument : function() {
            return this.getFrame() ? this.frameEl.getFrameDocument() : null;
        },

        /**
         * Get the embedded iframe's document as an Ext.Element.
         *
         * @return {Ext.Element object} or null if unavailable
         */
        getFrameDoc : function() {
            return this.getFrame() ? this.frameEl.getDoc() : null;
        },

        /**
         * If sufficient privilege exists, returns the frame's current document
         * body as an HTMLElement.
         *
         * @return {Ext.Element} The frame document body or Null if access to
         *         document object was denied.
         */
        getFrameBody : function() {
            return this.getFrame() ? this.frameEl.getBody() : null;
        },
        
        /**
         * Reset the embedded frame to a neutral domain state and clear its contents
          * @param {String}src (Optional) A specific reset string (eg. 'about:blank')
         *            to use for resetting the frame.
         * @param {Function} callback (Optional) A callback function invoked when the
         *            frame reset is complete.
         * @param {Object} scope (Optional) scope by which the callback function is
         *            invoked.
         * @return {Ext.ux.ManagedIFrame.Component} this
         */
        resetFrame : function() {
            this.getFrame() && this.frameEl.reset.apply(this.frameEl, arguments);
            return this;
        },
        
        /**
         * Loads the Components frame with the response from a form submit to the 
         * specified URL with the ManagedIframe.Element as it's submit target.
         * @param {Object} submitCfg A config object containing any of the following options:
         * <pre><code>
         *      mifPanel.submitAsTarget({
         *         form : formPanel.form,  //optional Ext.FormPanel, Ext form element, or HTMLFormElement
         *         url: &quot;your-url.php&quot;,
         *         params: {param1: &quot;foo&quot;, param2: &quot;bar&quot;}, // or a URL encoded string
         *         callback: yourFunction,  //optional
         *         scope: yourObject, // optional scope for the callback
         *         method: 'POST', //optional form.action (default:'POST')
         *         encoding : "multipart/form-data" //optional, default HTMLForm default
         *      });
         *
         * </code></pre>
         *
         * @return {Ext.ux.ManagedIFrame.Component} this
         */
        submitAsTarget  : function(submitCfg){
            this.getFrame() && this.frameEl.submitAsTarget.apply(this.frameEl, arguments);
            return this;
        },
        
        /**
         * Loads this Components's frame immediately with content returned from an
         * XHR call.
         *
         * @param {Object/String/Function} loadCfg A config object containing any of the following
         *            options:
         *
         * <pre><code>
         *      mifPanel.load({
         *         url: &quot;your-url.php&quot;,
         *         params: {param1: &quot;foo&quot;, param2: &quot;bar&quot;}, // or a URL encoded string
         *         callback: yourFunction,
         *         scope: yourObject, // optional scope for the callback
         *         discardUrl: false,
         *         nocache: false,
         *         text: &quot;Loading...&quot;,
         *         timeout: 30,
         *         scripts: false,
         *         submitAsTarget : false,  //optional true, to use Form submit to load the frame (see submitAsTarget method)
         *         renderer:{render:function(el, response, updater, callback){....}}  //optional custom renderer
         *      });
         *
         * </code></pre>
         *
         * The only required property is url. The optional properties
         *            nocache, text and scripts are shorthand for
         *            disableCaching, indicatorText and loadScripts and are used
         *            to set their associated property on this panel Updater
         *            instance.
         * @return {Ext.ux.ManagedIFrame.Component} this
         */
        load : function(loadCfg) {
            if(loadCfg && this.getFrame()){
                var args = arguments;
                this.resetFrame(null, function(){ 
                    loadCfg.submitAsTarget ?
                    this.submitAsTarget.apply(this,args):
                    this.frameEl.load.apply(this.frameEl,args);
                },this);
            }
            this.autoLoad = loadCfg;
            return this;
        },

        /** @private */
        doAutoLoad : function() {
            this.autoLoad && this.load(typeof this.autoLoad == 'object' ? 
                this.autoLoad : { url : this.autoLoad });
        },

        /**
         * Get the {@link #Ext.ux.ManagedIFrame.Updater} for this panel's iframe. Enables
         * Ajax-based document replacement of this panel's iframe document.
         *
         * @return {Ext.ux.ManagedIFrame.Updater} The Updater
         */
        getUpdater : function() {
            return this.getFrame() ? this.frameEl.getUpdater() : null;
        },
        
        /**
         * Sets the embedded Iframe src property. Note: invoke the function with
         * no arguments to refresh the iframe based on the current src value.
         *
         * @param {String/Function} url (Optional) A string or reference to a Function that
         *            returns a URI string when called
         * @param {Boolean} discardUrl (Optional) If not passed as <tt>false</tt>
         *            the URL of this action becomes the default SRC attribute
         *            for this iframe, and will be subsequently used in future
         *            setSrc calls (emulates autoRefresh by calling setSrc
         *            without params).
         * @param {Function} callback (Optional) A callback function invoked when the
         *            frame document has been fully loaded.
         * @param {Object} scope (Optional) scope by which the callback function is
         *            invoked.
         * @return {Ext.ux.ManagedIFrame.Component} this
         */
        setSrc : function(url, discardUrl, callback, scope) {
            this.getFrame() && this.frameEl.setSrc.apply(this.frameEl, arguments);
            return this;
        },

        /**
         * Sets the embedded Iframe location using its replace method. Note: invoke the function with
         * no arguments to refresh the iframe based on the current src value.
         *
         * @param {String/Function} url (Optional) A string or reference to a Function that
         *            returns a URI string when called
         * @param {Boolean} discardUrl (Optional) If not passed as <tt>false</tt>
         *            the URL of this action becomes the default SRC attribute
         *            for this iframe, and will be subsequently used in future
         *            setSrc calls (emulates autoRefresh by calling setSrc
         *            without params).
         * @param {Function} callback (Optional) A callback function invoked when the
         *            frame document has been fully loaded.
         * @param {Object} scope (Optional) scope by which the callback function is
         *            invoked.
         * @return {Ext.ux.ManagedIFrame.Component} this
         */
        setLocation : function(url, discardUrl, callback, scope) {
           this.getFrame() && this.frameEl.setLocation.apply(this.frameEl, arguments);
           return this;
        },

        /**
         * @private //Make it state-aware
         */
        getState : function() {
            var URI = this.getFrame() ? this.frameEl.getDocumentURI() || null : null;
            var state = this.supr().getState.call(this);
            state = Ext.apply(state || {}, 
                {defaultSrc : Ext.isFunction(URI) ? URI() : URI,
                 autoLoad   : this.autoLoad
                });
            return state;
        },
        
        /**
         * @private
         */
        setMIFEvents : function(){
            
            this.addEvents(

                    /**
                     * Fires when the iFrame has reached a loaded/complete state.
                     * @event documentloaded
                     * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                     * @param {Ext.ux.ManagedIFrame.Element} frameEl
                     */
                    'documentloaded',  
                      
                    /**
                     * Fires ONLY when an iFrame's Document(DOM) has reach a
                     * state where the DOM may be manipulated (ie same domain policy)
                     * Note: This event is only available when overwriting the iframe
                     * document using the update method and to pages retrieved from a "same
                     * domain". Returning false from the eventHandler stops further event
                     * (documentloaded) processing.
                     * @event domready 
                     * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                     * @param {Ext.ux.ManagedIFrame.Element} this.frameEl
                     */
                    'domready',
                    /**
                     * Fires when the frame actions raise an error
                     * @event exception
                     * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                     * @param {Ext.ux.MIF.Element} frameEl
                     * @param {Error/string} exception
                     */
                    'exception',

                    /**
                     * Fires upon receipt of a message generated by window.sendMessage
                     * method of the embedded Iframe.window object
                     * @event message
                     * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                     * @param {Ext.ux.ManagedIFrame.Element} this.frameEl
                     * @param {object}
                     *            message (members: type: {string} literal "message", data
                     *            {Mixed} [the message payload], domain [the document domain
                     *            from which the message originated ], uri {string} the
                     *            document URI of the message sender source (Object) the
                     *            window context of the message sender tag {string} optional
                     *            reference tag sent by the message sender
                     * <p>Alternate event handler syntax for message:tag filtering Fires upon
                     * receipt of a message generated by window.sendMessage method which
                     * includes a specific tag value of the embedded Iframe.window object
                     *
                     */
                    'message',

                    /**
                     * Fires when the frame is blurred (loses focus).
                     * @event blur
                     * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                     * @param {Ext.ux.ManagedIFrame.Element} frameEl
                     * @param {Ext.Event} e Note: This event is only available when overwriting the
                     *            iframe document using the update method and to pages
                     *            retrieved from a "same domain". Returning false from the
                     *            eventHandler [MAY] NOT cancel the event, as this event is
                     *            NOT ALWAYS cancellable in all browsers.
                     */
                    'blur',

                    /**
                     * Fires when the frame gets focus. Note: This event is only available
                     * when overwriting the iframe document using the update method and to
                     * pages retrieved from a "same domain". Returning false from the
                     * eventHandler [MAY] NOT cancel the event, as this event is NOT ALWAYS
                     * cancellable in all browsers.
                     * @event focus
                     * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                     * @param {Ext.ux.ManagedIFrame.Element} frameEl
                     * @param {Ext.Event} e
                     *
                    */
                    'focus',
                    
                     /**
                     * Note: This event is only available when overwriting the iframe
                     * document using the update method and to pages retrieved from a "same-origin"
                     * domain.  To prevent numerous scroll events from being raised use the <i>buffer</i> listener 
                     * option to limit the number of times the event is raised.
                     * @event scroll 
                     * @param {Ext.ux.MIF.Element} this.
                     * @param {Ext.Event}
                     */
                    'scroll',
                    
                    /**
                     * Fires when the frames window is resized. Note: This event is only available
                     * when overwriting the iframe document using the update method and to
                     * pages retrieved from a "same domain". 
                     * @event resize
                     * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                     * @param {Ext.ux.ManagedIFrame.Element} frameEl
                     * @param {Ext.Event} e
                     * @param {Object} documentSize A height/width object signifying the new document size
                     * @param {Object} viewPortSize A height/width object signifying the size of the frame's viewport
                     * @param {Object} viewSize A height/width object signifying the size of the frame's view
                     *
                    */
                    'resize',
                    
                    /**
                     * Fires when(if) the frames window object raises the unload event
                     * Note: This event is only available when overwriting the iframe
                     * document using the update method and to pages retrieved from a "same-origin"
                     * domain. Note: Opera does not raise this event.
                     * @event unload 
                     * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                     * @param {Ext.ux.ManagedIFrame.Element} frameEl
                     * @param {Ext.Event}
                     */
                    'unload',
                    
                    /**
                     * Fires when the iFrame has been reset to a neutral domain state (blank document).
                     * @event reset
                     * @param {Ext.ux.ManagedIFrame.Element} frameEl
                     */
                    'reset'
                );
        },
        
        /**
         * dispatch a message to the embedded frame-window context (same-origin frames only)
         * @name sendMessage
         * @memberOf Ext.ux.ManagedIFrame.Element
         * @param {Mixed} message The message payload.  The payload can be any supported JS type. 
         * @param {String} tag Optional reference tag 
         * @param {String} origin Optional domain designation of the sender (defaults
         * to document.domain).
         */
        sendMessage : function(message, tag, origin) {
       
          //(implemented by mifmsg.js )
        },
        //Suspend (and queue) host container events until the child MIF.Component is rendered.
        onAdd : function(C){
             C.relayTarget && this.suspendEvents(true); 
        },
        
        initRef: function() {
      
	        if(this.ref){
	            var t = this,
	                levels = this.ref.split('/'),
	                l = levels.length,
	                i;
	            for (i = 0; i < l; i++) {
	                if(t.ownerCt){
	                    t = t.ownerCt;
	                }
	            }
	            this.refName = levels[--i];
	            t[this.refName] || (t[this.refName] = this);
	            
	            this.refOwner = t;
	        }
	    }
      
   };
   
   /*
    * end Adapter
    */
   
  /**
   * @class Ext.ux.ManagedIFrame.Component
   * @extends Ext.BoxComponent
   * @version 2.1 
   * @author Doug Hendricks. doug[always-At]theactivegroup.com
   * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
   * @copyright 2007-2009, Active Group, Inc.  All rights reserved.
   * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a>
   * @constructor
   * @base Ext.ux.ManagedIFrame.ComponentAdapter
   * @param {Object} config The config object
   */
  Ext.ux.ManagedIFrame.Component = Ext.extend(Ext.BoxComponent , { 
            
            ctype     : "Ext.ux.ManagedIFrame.Component",
            
            /** @private */
            initComponent : function() {
               
                var C = {
	                monitorResize : this.monitorResize || (this.monitorResize = !!this.fitToParent),
	                plugins : (this.plugins ||[]).concat(
	                    this.hideMode === 'nosize' && Ext.ux.plugin.VisibilityMode ? 
		                    [new Ext.ux.plugin.VisibilityMode(
		                        {hideMode :'nosize',
		                         elements : ['bwrap']
		                        })] : [] )
                  };
                  
                MIF.Component.superclass.initComponent.call(
                  Ext.apply(this,
                    Ext.apply(this.initialConfig, C)
                    ));
                    
                this.setMIFEvents();
            },   

            /** @private */
            onRender : function(ct, position){
                
                //default child frame's name to that of MIF-parent id (if not specified on frameCfg).
                var frCfg = this.frameCfg || this.frameConfig || (this.relayTarget ? {name : this.relayTarget.id}: {}) || {};
                
                //backward compatability with MIF 1.x
                var frDOM = frCfg.autoCreate || frCfg;
                frDOM = Ext.apply({tag  : 'iframe', id: Ext.id()}, frDOM);
                
                var el = Ext.getDom(this.el);

                (el && el.tagName == 'iframe') || 
                  (this.autoEl = Ext.apply({
                                    name : frDOM.id,
                                    frameborder : 0
                                   }, frDOM ));
                 
                MIF.Component.superclass.onRender.apply(this, arguments);
               
                if(this.unsupportedText){
                    ct.child('noframes') || ct.createChild({tag: 'noframes', html : this.unsupportedText || null});  
                }   
                var frame = this.el ;
                
                var F;
                if( F = this.frameEl = (this.el ? new MIF.Element(this.el.dom, true): null)){
                    (F.ownerCt = (this.relayTarget || this)).frameEl = F;
                    F.addClass('ux-mif'); 
                    if (this.loadMask) {
                        //resolve possible maskEl by Element name eg. 'body', 'bwrap', 'actionEl'
                        var mEl = this.loadMask.maskEl;
                        F.loadMask = Ext.apply({
                                    disabled    : false,
                                    hideOnReady : false,
                                    msgCls      : 'ext-el-mask-msg x-mask-loading',  
                                    maskCls     : 'ext-el-mask'
                                },
                                {
                                  maskEl : F.ownerCt[String(mEl)] || F.parent('.' + String(mEl)) || F.parent('.ux-mif-mask-target') || mEl 
                                },
                                Ext.isString(this.loadMask) ? {msg:this.loadMask} : this.loadMask
                              );
                        Ext.get(F.loadMask.maskEl) && Ext.get(F.loadMask.maskEl).addClass('ux-mif-mask-target');
                    }
                    F.disableMessaging = Ext.value(frCfg.disableMessaging, true);
                    F._observable && 
                        (this.relayTarget || this).relayEvents(F._observable, frameEvents.concat(this._msgTagHandlers || []));
                    delete this.contentEl;
                 }
                 
            },
            
            /** @private */
            afterRender  : function(container) {
                MIF.Component.superclass.afterRender.apply(this,arguments);
                
                // only resize (to Parent) if the panel is NOT in a layout.
                // parentNode should have {style:overflow:hidden;} applied.
                if (this.fitToParent && !this.ownerCt) {
                    var pos = this.getPosition(), size = (Ext.get(this.fitToParent)
                            || this.getEl().parent()).getViewSize();
                    this.setSize(size.width - pos[0], size.height - pos[1]);
                }

                this.getEl().setOverflow('hidden'); //disable competing scrollers
                this.setAutoScroll();
                var F;
               /* Enable auto-Shims if the Component participates in (nested?)
                * border layout.
                * Setup event handlers on the SplitBars and region panels to enable the frame
                * shims when needed
                */
                if(F = this.frameEl){
                    var ownerCt = this.ownerCt;
                    while (ownerCt) {
                        ownerCt.on('afterlayout', function(container, layout) {
                            Ext.each(['north', 'south', 'east', 'west'],
                                    function(region) {
                                        var reg;
                                        if ((reg = layout[region]) && 
                                             reg.split && reg.split.dd &&
                                             !reg._splitTrapped) {
                                               reg.split.dd.endDrag = reg.split.dd.endDrag.createSequence(MIM.hideShims, MIM );
                                               reg.split.on('beforeresize',MIM.showShims,MIM);
                                               reg._splitTrapped = MIM._splitTrapped = true;
                                        }
                            }, this);
                        }, this, { single : true}); // and discard
                        ownerCt = ownerCt.ownerCt; // nested layouts?
                    }
                    /*
                     * Create an img shim if the component participates in a layout or forced
                     */
                    if(!!this.ownerCt || this.useShim ){ this.frameShim = F.createFrameShim(); }
                    this.getUpdater().showLoadIndicator = this.showLoadIndicator || false;
                    
                    //Resume Parent containers' events 
                    var resumeEvents = this.relayTarget && this.ownerCt ?                         
                       this.ownerCt.resumeEvents.createDelegate(this.ownerCt) : null;
                       
                    if(this.autoLoad){
                       this.doAutoLoad();
                    } else if(this.frameMarkup || this.html) {
                       F.update(this.frameMarkup || this.html, true, resumeEvents);
                       delete this.html;
                       delete this.frameMarkup;
                       return;
                    }else{
                       
                        if(this.defaultSrc){
                            F.setSrc(this.defaultSrc, false);
                        }else{
                            /* If this is a no-action frame, reset it first, then resume parent events
                             * allowing access to a fully reset frame by upstream afterrender/layout events
                             */ 
                            F.reset(null, resumeEvents);
                            return;
                        }
                    }
                    resumeEvents && resumeEvents();
                }
            },
            
            /** @private */
            beforeDestroy : function() {
                this.setVisible(false);
                var F;
                if(F = this.getFrame()){
                    F.hide();
                    F.remove();
                    this.frameEl = this.frameShim = null;
                }
                this.relayTarget && (this.relayTarget.frameEl = null);
                MIF.Component.superclass.beforeDestroy.call(this);
            }
    });

    Ext.override(MIF.Component, MIF.ComponentAdapter.prototype);
    Ext.reg('mif', MIF.Component);
   
    /*
    * end Component
    */
    
  /**
   * @private
   * this function renders a child MIF.Component to MIF.Panel and MIF.Window
   * designed to be called by the constructor of higher-level MIF.Components only.
   */
  function embed_MIF(config){
    
    config || (config={});
    config.layout = 'fit';
    config.items = {
             xtype    : 'mif',
               ref    : 'mifChild',
            useShim   : true,
           autoScroll : Ext.value(config.autoScroll , this.autoScroll),
          defaultSrc  : Ext.value(config.defaultSrc , this.defaultSrc),
         frameMarkup  : Ext.value(config.html , this.html),
            loadMask  : Ext.value(config.loadMask , this.loadMask),
         focusOnLoad  : Ext.value(config.focusOnLoad, this.focusOnLoad),
          frameConfig : Ext.value(config.frameConfig || config.frameCfg , this.frameConfig),
          relayTarget : this  //direct relay of events to the parent component
        };
    delete config.html; 
    this.setMIFEvents();
    return config; 
    
  };
    
  /**
   * @class Ext.ux.ManagedIFrame.Panel
   * @extends Ext.Panel
   * @version 2.1 
   * @author Doug Hendricks. doug[always-At]theactivegroup.com
   * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
   * @copyright 2007-2009, Active Group, Inc.  All rights reserved.
   * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a>
   * @constructor
   * @base Ext.ux.ManagedIFrame.ComponentAdapter
   * @param {Object} config The config object
   */

  Ext.ux.ManagedIFrame.Panel = Ext.extend( Ext.Panel , {
        ctype       : 'Ext.ux.ManagedIFrame.Panel',
        bodyCssClass: 'ux-mif-mask-target',
        constructor : function(config){
            MIF.Panel.superclass.constructor.call(this, embed_MIF.call(this, config));
         }
  });
  
  Ext.override(MIF.Panel, MIF.ComponentAdapter.prototype);
  Ext.reg('iframepanel', MIF.Panel);
    /*
    * end Panel
    */

    /**
     * @class Ext.ux.ManagedIFrame.Portlet
     * @extends Ext.ux.ManagedIFrame.Panel
     * @version 2.1 
     * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
     * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a> 
     * @author Doug Hendricks. Forum ID: <a href="http://extjs.com/forum/member.php?u=8730">hendricd</a> 
     * @copyright 2007-2009, Active Group, Inc. All rights reserved.
     * @constructor Create a new Ext.ux.ManagedIFramePortlet 
     * @param {Object} config The config object
     */

    Ext.ux.ManagedIFrame.Portlet = Ext.extend(Ext.ux.ManagedIFrame.Panel, {
                ctype      : "Ext.ux.ManagedIFrame.Portlet",
                anchor     : '100%',
                frame      : true,
                collapseEl : 'bwrap',
                collapsible: true,
                draggable  : true,
                cls        : 'x-portlet'
                
            });
            
    Ext.reg('iframeportlet', MIF.Portlet);
   /*
    * end Portlet
    */
    
  /**
   * @class Ext.ux.ManagedIFrame.Window
   * @extends Ext.Window
   * @version 2.1 
   * @author Doug Hendricks. 
   * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
   * @copyright 2007-2009, Active Group, Inc.  All rights reserved.
   * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a>
   * @constructor
   * @base Ext.ux.ManagedIFrame.ComponentAdapter
   * @param {Object} config The config object
   */
    
  Ext.ux.ManagedIFrame.Window = Ext.extend( Ext.Window , 
       {
            ctype       : "Ext.ux.ManagedIFrame.Window",
            bodyCssClass: 'ux-mif-mask-target',
            constructor : function(config){
			    MIF.Window.superclass.constructor.call(this, embed_MIF.call(this, config));
            }
    });
    Ext.override(MIF.Window, MIF.ComponentAdapter.prototype);
    Ext.reg('iframewindow', MIF.Window);
    
    /*
    * end Window
    */
    
    /**
     * @class Ext.ux.ManagedIFrame.Updater
     * @extends Ext.Updater
     * @version 2.1 
     * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
     * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a> 
     * @author Doug Hendricks. Forum ID: <a href="http://extjs.com/forum/member.php?u=8730">hendricd</a> 
     * @copyright 2007-2009, Active Group, Inc. All rights reserved.
     * @constructor Creates a new Ext.ux.ManagedIFrame.Updater instance.
     * @param {String/Object} el The element to bind the Updater instance to.
     */
    Ext.ux.ManagedIFrame.Updater = Ext.extend(Ext.Updater, {
    
       /**
         * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
         * method may be overridden to perform a custom action while this Updater is actively updating its contents.
         */
        showLoading : function(){
            this.showLoadIndicator && this.el && this.el.mask(this.indicatorText);
            
        },
        
        /**
         * Hide the Frames masking agent.
         */
        hideLoading : function(){
            this.showLoadIndicator && this.el && this.el.unmask();
        },
        
        // private
        updateComplete : function(response){
            MIF.Updater.superclass.updateComplete.apply(this,arguments);
            this.hideLoading();
        },
    
        // private
        processFailure : function(response){
            MIF.Updater.superclass.processFailure.apply(this,arguments);
            this.hideLoading();
        }
        
    }); 
    
    
    var styleCamelRe = /(-[a-z])/gi;
    var styleCamelFn = function(m, a) {
        return a.charAt(1).toUpperCase();
    };
    
    /**
     * @class Ext.ux.ManagedIFrame.CSS
     * Stylesheet interface object
     * @version 2.1 
     * @author Doug Hendricks. doug[always-At]theactivegroup.com
     * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
     * @copyright 2007-2009, Active Group, Inc.  All rights reserved.
     * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a>
     */
    Ext.ux.ManagedIFrame.CSS = function(hostDocument) {
        var doc;
        if (hostDocument) {
            doc = hostDocument;
            return {
                rules : null,
                /** @private */
                destroy  :  function(){  return doc = null; },

                /**
                 * Creates a stylesheet from a text blob of rules. These rules
                 * will be wrapped in a STYLE tag and appended to the HEAD of
                 * the document.
                 *
                 * @param {String}
                 *            cssText The text containing the css rules
                 * @param {String} id An (optional) id to add to the stylesheet for later removal
                 * @return {StyleSheet}
                 */
                createStyleSheet : function(cssText, id) {
                    var ss;
                    if (!doc)return;
                    var head = doc.getElementsByTagName("head")[0];
                    var rules = doc.createElement("style");
                    rules.setAttribute("type", "text/css");
                    Ext.isString(id) && rules.setAttribute("id", id);

                    if (Ext.isIE) {
                        head.appendChild(rules);
                        ss = rules.styleSheet;
                        ss.cssText = cssText;
                    } else {
                        try {
                            rules.appendChild(doc.createTextNode(cssText));
                        } catch (e) {
                            rules.cssText = cssText;
                        }
                        head.appendChild(rules);
                        ss = rules.styleSheet
                                ? rules.styleSheet
                                : (rules.sheet || doc.styleSheets[doc.styleSheets.length - 1]);
                    }
                    this.cacheStyleSheet(ss);
                    return ss;
                },

                /**
                 * Removes a style or link tag by id
                 *
                 * @param {String}
                 *            id The id of the tag
                 */
                removeStyleSheet : function(id) {

                    if (!doc || !id)return;
                    var existing = doc.getElementById(id);
                    if (existing) {
                        existing.parentNode.removeChild(existing);
                    }
                },

                /**
                 * Dynamically swaps an existing stylesheet reference for a new
                 * one
                 *
                 * @param {String}
                 *            id The id of an existing link tag to remove
                 * @param {String}
                 *            url The href of the new stylesheet to include
                 */
                swapStyleSheet : function(id, url) {
                    if (!doc)return;
                    this.removeStyleSheet(id);
                    var ss = doc.createElement("link");
                    ss.setAttribute("rel", "stylesheet");
                    ss.setAttribute("type", "text/css");
                    Ext.isString(id) && ss.setAttribute("id", id);
                    ss.setAttribute("href", url);
                    doc.getElementsByTagName("head")[0].appendChild(ss);
                },

                /**
                 * Refresh the rule cache if you have dynamically added stylesheets
                 * @return {Object} An object (hash) of rules indexed by selector
                 */
                refreshCache : function() {
                    return this.getRules(true);
                },

                // private
                cacheStyleSheet : function(ss, media) {
                    this.rules || (this.rules = {});
                    
                     try{// try catch for cross domain access issue
			          
				          Ext.each(ss.cssRules || ss.rules || [], 
				            function(rule){ 
				              this.hashRule(rule, ss, media);
				          }, this);  
				          
				          //IE @imports
				          Ext.each(ss.imports || [], 
				           function(sheet){
				              sheet && this.cacheStyleSheet(sheet,this.resolveMedia([sheet, sheet.parentStyleSheet]));
				           }
				          ,this);
			          
			        }catch(e){}
                },
                 // @private
			   hashRule  :  function(rule, sheet, mediaOverride){
			      
			      var mediaSelector = mediaOverride || this.resolveMedia(rule);
			      
			      //W3C @media
			      if( rule.cssRules || rule.rules){
			          this.cacheStyleSheet(rule, this.resolveMedia([rule, rule.parentRule ]));
			      } 
			      
			       //W3C @imports
			      if(rule.styleSheet){ 
			         this.cacheStyleSheet(rule.styleSheet, this.resolveMedia([rule, rule.ownerRule, rule.parentStyleSheet]));
			      }
			      
			      rule.selectorText && 
			        Ext.each((mediaSelector || '').split(','), 
			           function(media){
			            this.rules[((media ? media.trim() + ':' : '') + rule.selectorText).toLowerCase()] = rule;
			        }, this);
			      
			   },
			
			   /**
			    * @private
			    * @param {Object/Array} rule CSS Rule (or array of Rules/sheets) to evaluate media types.
			    * @return a comma-delimited string of media types. 
			    */
			   resolveMedia  : function(rule){
			        var media;
			        Ext.each([].concat(rule),function(r){
			            if(r && r.media && r.media.length){
			                media = r.media;
			                return false;
			            }
			        });
			        return media ? (Ext.isIE ? String(media) : media.mediaText ) : '';
			     },

                /**
                 * Gets all css rules for the document
                 *
                 * @param {Boolean}
                 *            refreshCache true to refresh the internal cache
                 * @return {Object} An object (hash) of rules indexed by
                 *         selector
                 */
                getRules : function(refreshCache) {
                    if (!this.rules || refreshCache) {
                        this.rules = {};
                        if (doc) {
                            var ds = doc.styleSheets;
                            for (var i = 0, len = ds.length; i < len; i++) {
                                try {
                                    this.cacheStyleSheet(ds[i]);
                                } catch (e) {}
                            }
                        }
                    }
                    return this.rules;
                },

               /**
			    * Gets an an individual CSS rule by selector(s)
			    * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
			    * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
			    * @param {String} mediaSelector Name of optional CSS media context (eg. print, screen)
			    * @return {CSSRule} The CSS rule or null if one is not found
			    */
                getRule : function(selector, refreshCache, mediaSelector) {
                    var rs = this.getRules(refreshCache);

			        if(Ext.type(mediaSelector) == 'string'){
			            mediaSelector = mediaSelector.trim() + ':';
			        }else{
			            mediaSelector = '';
			        }
			
			        if(!Ext.isArray(selector)){
			            return rs[(mediaSelector + selector).toLowerCase()];
			        }
			        var select;
			        for(var i = 0; i < selector.length; i++){
			            select = (mediaSelector + selector[i]).toLowerCase();
			            if(rs[select]){
			                return rs[select];
			            }
			        }
			        return null;
                },

               /**
			    * Updates a rule property
			    * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
			    * @param {String} property The css property
			    * @param {String} value The new value for the property
			    * @param {String} mediaSelector Name(s) of optional media contexts. Multiple may be specified, delimited by commas (eg. print,screen)
			    * @return {Boolean} true If a rule was found and updated
			    */
                updateRule : function(selector, property, value, mediaSelector){
    
			         Ext.each((mediaSelector || '').split(','), function(mediaSelect){    
			            if(!Ext.isArray(selector)){
			                var rule = this.getRule(selector, false, mediaSelect);
			                if(rule){
			                    rule.style[property.replace(camelRe, camelFn)] = value;
			                    return true;
			                }
			            }else{
			                for(var i = 0; i < selector.length; i++){
			                    if(this.updateRule(selector[i], property, value, mediaSelect)){
			                        return true;
			                    }
			                }
			            }
			            return false;
			         }, this);
                }
            };
        }
    };

    /**
     * @class Ext.ux.ManagedIFrame.Manager
     * @version 2.1 
	 * @author Doug Hendricks. doug[always-At]theactivegroup.com
	 * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
	 * @copyright 2007-2009, Active Group, Inc.  All rights reserved.
	 * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a>
	 * @singleton
     */
    Ext.ux.ManagedIFrame.Manager = function() {
        var frames = {};
        var implementation = {
            // private DOMFrameContentLoaded handler for browsers (Gecko, Webkit, Opera) that support it.
            _DOMFrameReadyHandler : function(e) {
                try {
                    var $frame ;
                    if ($frame = e.target.ownerCt){
                        $frame.loadHandler.call($frame,e);
                    }
                } catch (rhEx) {} //nested iframes will throw when accessing target.id
            },
            /**
             * @cfg {String} shimCls
             * @default "ux-mif-shim"
             * The default CSS rule applied to MIF image shims to toggle their visibility.
             */
            shimCls : 'ux-mif-shim',

            /** @private */
            register : function(frame) {
                frame.manager = this;
                frames[frame.id] = frames[frame.name] = {ref : frame };
                return frame;
            },
            /** @private */
            deRegister : function(frame) {
                delete frames[frame.id];
                delete frames[frame.name];
                
            },
            /**
             * Toggles the built-in MIF shim off on all visible MIFs
             * @methodOf Ext.ux.MIF.Manager
             *
             */
            hideShims : function() {
                var mm = MIF.Manager;
                mm.shimsApplied && Ext.select('.' + mm.shimCls, true).removeClass(mm.shimCls+ '-on');
                mm.shimsApplied = false;
            },

            /**
             * Shim ALL MIFs (eg. when a region-layout.splitter is on the move or before start of a drag operation)
             * @methodOf Ext.ux.MIF.Manager
             */
            showShims : function() {
                var mm = MIF.Manager;
                !mm.shimsApplied && Ext.select('.' + mm.shimCls, true).addClass(mm.shimCls+ '-on');
                mm.shimsApplied = true;
            },

            /**
             * Retrieve a MIF instance by its DOM ID
             * @methodOf Ext.ux.MIF.Manager
             * @param {Ext.ux.MIF/string} id
             */
            getFrameById : function(id) {
                return typeof id == 'string' ? (frames[id] ? frames[id].ref
                        || null : null) : null;
            },

            /**
             * Retrieve a MIF instance by its DOM name
             * @methodOf Ext.ux.MIF.Manager
             * @param {Ext.ux.MIF/string} name
             */
            getFrameByName : function(name) {
                return this.getFrameById(name);
            },

            /** @private */
            // retrieve the internal frameCache object
            getFrameHash : function(frame) {
                return frames[frame.id] || frames[frame.id] || null;
            },

            /** @private */
            _flyweights : {},

            /** @private */
            destroy : function() {
                if (document.addEventListener && !Ext.isOpera) {
                      window.removeEventListener("DOMFrameContentLoaded", this._DOMFrameReadyHandler , false);
                }
                delete this._flyweights;
            }
        };
        // for Gecko and any who might support it later 
        document.addEventListener && !Ext.isOpera &&
            window.addEventListener("DOMFrameContentLoaded", implementation._DOMFrameReadyHandler , false);

        Ext.EventManager.on(window, 'beforeunload', implementation.destroy, implementation);
        return implementation;
    }();
    
    MIM = MIF.Manager;
    MIM.showDragMask = MIM.showShims;
    MIM.hideDragMask = MIM.hideShims;
    
    /**
     * Shim all MIF's during a Window drag operation.
     */
    var winDD = Ext.Window.DD;
    Ext.override(winDD, {
       startDrag : winDD.prototype.startDrag.createInterceptor(MIM.showShims),
       endDrag   : winDD.prototype.endDrag.createInterceptor(MIM.hideShims)
    });

    //Previous release compatibility
    Ext.ux.ManagedIFramePanel = MIF.Panel;
    Ext.ux.ManagedIFramePortlet = MIF.Portlet;
    Ext.ux.ManagedIframe = function(el,opt){
        
        var args = Array.prototype.slice.call(arguments, 0),
            el = Ext.get(args[0]),
            config = args[0];

        if (el && el.dom && el.dom.tagName == 'IFRAME') {
            config = args[1] || {};
        } else {
            config = args[0] || args[1] || {};

            el = config.autoCreate ? Ext.get(Ext.DomHelper.append(
                    config.autoCreate.parent || Ext.getBody(), Ext.apply({
                        tag : 'iframe',
                        frameborder : 0,
                        cls : 'x-mif',
                        src : (Ext.isIE && Ext.isSecure)? Ext.SSL_SECURE_URL: 'about:blank'
                    }, config.autoCreate)))
                    : null;

            if(el && config.unsupportedText){
                Ext.DomHelper.append(el.dom.parentNode, {tag:'noframes',html: config.unsupportedText } );
            }
        }
        
        var mif = new MIF.Element(el,true);
        if(mif){
            Ext.apply(mif, {
                disableMessaging : Ext.value(config.disableMessaging , true),
                loadMask : !!config.loadMask ? Ext.apply({
                            msg : 'Loading..',
                            msgCls : 'x-mask-loading',
                            maskEl : null,
                            hideOnReady : false,
                            disabled : false
                        }, config.loadMask) : false,
                _windowContext : null,
                eventsFollowFrameLinks : Ext.value(config.eventsFollowFrameLinks ,true)
            });
            
            config.listeners && mif.on(config.listeners);
            
            if(!!config.html){
                mif.update(config.html);
            } else {
                !!config.src && mif.setSrc(config.src);
            }
        }
        
        return mif;   
    };

    /**
     * Internal Error class for ManagedIFrame Components
	 * @class Ext.ux.ManagedIFrame.Error
     * @extends Ext.Error
     * @version 2.1 
     * @donate <a target="tag_donate" href="http://donate.theactivegroup.com"><img border="0" src="http://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif" border="0" alt="Make a donation to support ongoing development"></a>
     * @license <a href="http://www.gnu.org/licenses/gpl.html">GPL 3.0</a> 
     * @author Doug Hendricks. Forum ID: <a href="http://extjs.com/forum/member.php?u=8730">hendricd</a> 
     * @copyright 2007-2009, Active Group, Inc. All rights reserved.
	 * @constructor 
     * @param {String} message
     * @param {Mixed} arg optional argument to include in Error object.
	 */
	Ext.ux.ManagedIFrame.Error = Ext.extend(Ext.Error, {
	    constructor : function(message, arg) {
	        this.arg = arg;
	        Ext.Error.call(this, message);
	    },
	    name : 'Ext.ux.ManagedIFrame'
	});
    
	Ext.apply(Ext.ux.ManagedIFrame.Error.prototype, {
	    lang: {
	        'documentcontext-remove': 'An attempt was made to remove an Element from the wrong document context.',
	        'execscript-secure-context': 'An attempt was made at script execution within a document context with limited access permissions.',
	        'printexception': 'An Error was encountered attempting the print the frame contents (document access is likely restricted).'
	    }
	});
    
    /** @private */
    Ext.onReady(function() {
            // Generate CSS Rules but allow for overrides.
            var CSS = new Ext.ux.ManagedIFrame.CSS(document), rules = [];

            CSS.getRule('.ux-mif-fill')|| (rules.push('.ux-mif-fill{height:100%;width:100%;}'));
            CSS.getRule('.ux-mif-mask-target')|| (rules.push('.ux-mif-mask-target{position:relative;zoom:1;}'));
            CSS.getRule('.ux-mif-el-mask')|| (rules.push(
              '.ux-mif-el-mask {z-index: 100;position: absolute;top:0;left:0;-moz-opacity: 0.5;opacity: .50;*filter: alpha(opacity=50);width: 100%;height: 100%;zoom: 1;} ',
              '.ux-mif-el-mask-msg {z-index: 1;position: absolute;top: 0;left: 0;border:1px solid;background:repeat-x 0 -16px;padding:2px;} ',
              '.ux-mif-el-mask-msg div {padding:5px 10px 5px 10px;border:1px solid;cursor:wait;} '
              ));


            if (!CSS.getRule('.ux-mif-shim')) {
                rules.push('.ux-mif-shim {z-index:8500;position:absolute;top:0px;left:0px;background:transparent!important;overflow:hidden;display:none;}');
                rules.push('.ux-mif-shim-on{width:100%;height:100%;display:block;zoom:1;}');
                rules.push('.ext-ie6 .ux-mif-shim{margin-left:5px;margin-top:3px;}');
            }

            !!rules.length && CSS.createStyleSheet(rules.join(' '), 'mifCSS');
            
        });

    /** @sourceURL=<mif.js> */
    Ext.provide && Ext.provide('mif');
})();/* global Ext */
/* Messaging Driver for ux.ManagedIFrame 
 *******************************************************************************
 * This file is distributed on an AS IS BASIS WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * ***********************************************************************************
 * @version 2.0
 *
 * License: ux.ManagedIFrame, ux.ManagedIFramePanel, ux.ManagedIFrameWindow  
 * are licensed under the terms of the Open Source GPL 3.0 license:
 * http://www.gnu.org/licenses/gpl.html
 *
 * Commercial use is prohibited without a Commercial Developement License. See
 * http://licensing.theactivegroup.com.
 *
 * Donations are welcomed: http://donate.theactivegroup.com
 *
 */ 
 
(function(){
      
     /**
     * @private, frame messaging interface (for same-domain-policy frames
     *           only)
     */
     var _XFrameMessaging = function() {
        // each tag gets a hash queue ($ = no tag ).
        var tagStack = { '$' : [] };
        var isEmpty = function(v, allowBlank) {
            return v === null || v === undefined
                    || (!allowBlank ? v === '' : false);
        };
        var apply = function(o, c, defaults) {
	        if (defaults) { apply(o, defaults);}
	        if (o && c && typeof c == 'object') {
	            for (var p in c) {
	                o[p] = c[p];
	            }
	        }
	        return o;
	    };

        window.sendMessage = function(message, tag, origin) {
            var MIF;
            if (MIF = arguments.callee.manager) {
                if (message._fromHost) {
                    var fn, result;
                    // only raise matching-tag handlers
                    var compTag = message.tag || tag || null;
                    var mstack = !isEmpty(compTag) ? tagStack[String(compTag).toLowerCase()] || [] : tagStack["$"];

                    for (var i = 0, l = mstack.length; i < l; i++) {
                        if (fn = mstack[i]) {
                            result = fn.apply(fn.__scope, arguments) === false? false: result;
                            if (fn.__single) {
                                mstack[i] = null;
                            }
                            if (result === false) {
                                break;
                            }
                        }
                    }

                    return result;
                } else {

                    message = {
                        type : 'message',
                        data : message,
                        domain : origin || document.domain,
                        uri : document.documentURI,
                        source : window,
                        tag : tag ? String(tag).toLowerCase() : null
                    };

                    try {
                        return MIF.disableMessaging !== true
                                ? MIF._observable ? MIF._observable.fireEvent.call(MIF._observable, message.type,MIF, message) : null
                                : null;
                    } catch (ex) {
                    } // trap for message:tag handlers not yet defined

                    return null;
                }

            }
        };
        window.onhostmessage = function(fn, scope, single, tag) {

            if (typeof fn == 'function') {
                if (!isEmpty(fn.__index)) {
                    throw "onhostmessage: duplicate handler definition"
                            + (tag ? " for tag:" + tag : '');
                }

                var k = isEmpty(tag) ? "$" : tag.toLowerCase();
                tagStack[k] || (tagStack[k] = []);
                apply(fn, {
                            __tag : k,
                            __single : single || false,
                            __scope : scope || window,
                            __index : tagStack[k].length
                        });
                tagStack[k].push(fn);

            } else {
                throw "onhostmessage: function required";
            }

        };
        window.unhostmessage = function(fn) {
            if (typeof fn == 'function' && typeof fn.__index != 'undefined') {
                var k = fn.__tag || "$";
                tagStack[k][fn.__index] = null;
            }
        };

    }; 
   
    var MIFEl = Ext.ux.ManagedIFrame.Element;
    
    Ext.override( MIFEl ,{
        
        disableMessaging :  true,
        
        _renderHook : MIFEl.prototype._renderHook.createSequence(function(){
            
            if(this.disableMessaging){return;}
            
            //assert a default 'message' event
            var O=this._observable;
            O && (O.events.message || (O.addEvents('message')));
            
            if (this.domWritable()) {
                this.loadFunction({
                            name : 'XMessage',
                            fn : _XFrameMessaging
                        }, false, true);
                        
                var sm, w = this.getWindow();
                w && (sm = w.sendMessage) && (sm.manager = this);
                
            }
            
        }),
        
        /**
         * dispatch a message to the embedded frame-window context (same-origin frames only)
         * @name sendMessage
         * @memberOf Ext.ux.ManagedIFrame.Element
         * @param {Mixed} message The message payload.  The payload can be any supported JS type. 
         * @param {String} tag Optional reference tag 
         * @param {String} origin Optional domain designation of the sender (defaults
         * to document.domain).
         */
        sendMessage : function(message, tag, origin) {
            var win;
            if (this.domWritable() && (win = this.getWindow())) {
                // support MIF frame-to-frame messaging relay
                tag || (tag = message.tag || '');
                tag = tag.toLowerCase();
                message = Ext.applyIf(message.data ? message : {
                            data : message
                        }, {
                            type : 'message',
                            domain : origin || document.domain,
                            origin : origin || document.domain,
                            uri : document.documentURI,
                            source : window,
                            tag : tag || null,
                            _fromHost : this
                        });
                return win.sendMessage ? win.sendMessage.call(null, message, tag, origin) : undefined;
            }
            return;
        },
        
        /**
         * Dispatch a cross-document message (per HTML5 specification) if the browser supports it natively.
         * @name postMessage
         * @memberOf Ext.ux.ManagedIFrame.Element
         * @param {String} message Required message payload (String only)
         * @param {Array} ports Optional array of ports/channels. 
         * @param {String} origin Optional domain designation of the sender (defaults
         * to document.domain). 
         * <p>Notes:  on IE8, this action is synchronous.
         */
        postMessage : function(message /* , [ports] [,origin] */){
            var d, w=this.getWindow();
            if(w && !this.disableMessaging){
	            var args =[String(message || '')].concat(
	                Array.prototype.slice.call(arguments,1)
	            );
                //assert an origin (last argument)
                var L = args.length;
                var origin = L > 1 ? args[L-1] : undefined;
                typeof origin === 'string' || args.push(document.domain);
	            //Opera may implement postMessage on the document   
	            var post = w.postMessage || (d = this.getDocument()? d.postMessage : null);
	            post && post.apply(w, args);
            }
        }
        
    });
    var MIF = Ext.ux.ManagedIFrame;
    Ext.each(['Component', 'Panel', 'Window'], 
	      function(K){
           MIF[K] && Ext.override(MIF[K] ,{
                /**
                 * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                 * @param {Mixed} message The message payload.  The payload can be any supported JS type. 
                 * @param {String} tag Optional reference tag 
                 * @param {String} origin Optional domain designation of the sender (defaults
                 * to document.domain).
                 */
	           sendMessage : function(){
                  this.getFrame() && this.frameEl.sendMessage.apply(this.frameEl, arguments);
               }
	       });
    });
    
    var MM = MIF.Manager;
    MM && Ext.apply(MM,{
    
        /**
         * @private
         * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
         */
        onMessage : function(e){
            
            var be = e.browserEvent;
            //Copy the relevant message members to the Ext.event
            be && Ext.apply(e,{
               origin      : be.origin,
               data        : be.data,
               lastEventId : be.lastEventId,
               source      : be.source
            });
            try {
                var $frame ;
                if ($frame = be && be.source ? be.source.frameElement.id : null){
                    $frame = this.getFrameById($frame);
                    $frame && $frame._observable.fireEvent('message', $frame, e);
                }
            } catch (rhEx) {} 
        },
        /**
         * @private
         */
        destroy : MM.destroy.createSequence(function(){
             (window.postMessage || document.postMessage) &&
                Ext.EventManager.un(window, 'message', this.onMessage, this);  
        })
        
    });
    
     //Add support for postMessage message events if the browser supports it
    MM && (window.postMessage || document.postMessage) &&
        Ext.EventManager.on(window, 'message', MM.onMessage, MM);       
      
})();   

  /** @sourceURL=<mifmsg.js> */
Ext.provide && Ext.provide('mifmsg');
  
/* global Ext */
/* Messaging Driver for ux.ManagedIFrame 
 *******************************************************************************
 * This file is distributed on an AS IS BASIS WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * ***********************************************************************************
 * @version 2.0
 *
 * License: ux.ManagedIFrame, ux.ManagedIFramePanel, ux.ManagedIFrameWindow  
 * are licensed under the terms of the Open Source GPL 3.0 license:
 * http://www.gnu.org/licenses/gpl.html
 *
 * Commercial use is prohibited without a Commercial Developement License. See
 * http://licensing.theactivegroup.com.
 *
 * Donations are welcomed: http://donate.theactivegroup.com
 *
 */ 
 
(function(){
      
     /**
     * @private, frame messaging interface (for same-domain-policy frames
     *           only)
     */
     var _XFrameMessaging = function() {
        // each tag gets a hash queue ($ = no tag ).
        var tagStack = { '$' : [] };
        var isEmpty = function(v, allowBlank) {
            return v === null || v === undefined
                    || (!allowBlank ? v === '' : false);
        };
        var apply = function(o, c, defaults) {
	        if (defaults) { apply(o, defaults);}
	        if (o && c && typeof c == 'object') {
	            for (var p in c) {
	                o[p] = c[p];
	            }
	        }
	        return o;
	    };

        window.sendMessage = function(message, tag, origin) {
            var MIF;
            if (MIF = arguments.callee.manager) {
                if (message._fromHost) {
                    var fn, result;
                    // only raise matching-tag handlers
                    var compTag = message.tag || tag || null;
                    var mstack = !isEmpty(compTag) ? tagStack[String(compTag).toLowerCase()] || [] : tagStack["$"];

                    for (var i = 0, l = mstack.length; i < l; i++) {
                        if (fn = mstack[i]) {
                            result = fn.apply(fn.__scope, arguments) === false? false: result;
                            if (fn.__single) {
                                mstack[i] = null;
                            }
                            if (result === false) {
                                break;
                            }
                        }
                    }

                    return result;
                } else {

                    message = {
                        type : 'message',
                        data : message,
                        domain : origin || document.domain,
                        uri : document.documentURI,
                        source : window,
                        tag : tag ? String(tag).toLowerCase() : null
                    };

                    try {
                        return MIF.disableMessaging !== true
                                ? MIF._observable ? MIF._observable.fireEvent.call(MIF._observable, message.type,MIF, message) : null
                                : null;
                    } catch (ex) {
                    } // trap for message:tag handlers not yet defined

                    return null;
                }

            }
        };
        window.onhostmessage = function(fn, scope, single, tag) {

            if (typeof fn == 'function') {
                if (!isEmpty(fn.__index)) {
                    throw "onhostmessage: duplicate handler definition"
                            + (tag ? " for tag:" + tag : '');
                }

                var k = isEmpty(tag) ? "$" : tag.toLowerCase();
                tagStack[k] || (tagStack[k] = []);
                apply(fn, {
                            __tag : k,
                            __single : single || false,
                            __scope : scope || window,
                            __index : tagStack[k].length
                        });
                tagStack[k].push(fn);

            } else {
                throw "onhostmessage: function required";
            }

        };
        window.unhostmessage = function(fn) {
            if (typeof fn == 'function' && typeof fn.__index != 'undefined') {
                var k = fn.__tag || "$";
                tagStack[k][fn.__index] = null;
            }
        };

    }; 
   
    var MIFEl = Ext.ux.ManagedIFrame.Element;
    
    Ext.override( MIFEl ,{
        
        //disableMessaging :  true,
        
        _renderHook : MIFEl.prototype._renderHook.createSequence(function(){
            
            if(this.disableMessaging){return;}
            
            //assert a default 'message' event
            var O=this._observable;
            O && (O.events.message || (O.addEvents('message')));
            
            if (this.domWritable()) {
                this.loadFunction({
                            name : 'XMessage',
                            fn : _XFrameMessaging
                        }, false, true);
                        
                var sm, w = this.getWindow();
                w && (sm = w.sendMessage) && (sm.manager = this);
                
            }
            
        }),
        
        /**
         * dispatch a message to the embedded frame-window context (same-origin frames only)
         * @name sendMessage
         * @memberOf Ext.ux.ManagedIFrame.Element
         * @param {Mixed} message The message payload.  The payload can be any supported JS type. 
         * @param {String} tag Optional reference tag 
         * @param {String} origin Optional domain designation of the sender (defaults
         * to document.domain).
         */
        sendMessage : function(message, tag, origin) {
            var win;
            if (this.domWritable() && (win = this.getWindow())) {
                // support MIF frame-to-frame messaging relay
                tag || (tag = message.tag || '');
                tag = tag.toLowerCase();
                message = Ext.applyIf(message.data ? message : {
                            data : message
                        }, {
                            type : 'message',
                            domain : origin || document.domain,
                            origin : origin || document.domain,
                            uri : document.documentURI,
                            source : window,
                            tag : tag || null,
                            _fromHost : this
                        });
                return win.sendMessage ? win.sendMessage.call(null, message, tag, origin) : undefined;
            }
            return;
        },
        
        /**
         * Dispatch a cross-document message (per HTML5 specification) if the browser supports it natively.
         * @name postMessage
         * @memberOf Ext.ux.ManagedIFrame.Element
         * @param {String} message Required message payload (String only)
         * @param {Array} ports Optional array of ports/channels. 
         * @param {String} origin Optional domain designation of the sender (defaults
         * to document.domain). 
         * <p>Notes:  on IE8, this action is synchronous.
         */
        postMessage : function(message /* , [ports] [,origin] */){
            var d, w=this.getWindow();
            if(w && !this.disableMessaging){
	            var args =[String(message || '')].concat(
	                Array.prototype.slice.call(arguments,1)
	            );
                //assert an origin (last argument)
                var L = args.length;
                var origin = L > 1 ? args[L-1] : undefined;
                typeof origin === 'string' || args.push(document.domain);
	            //Opera may implement postMessage on the document   
	            var post = w.postMessage || (d = this.getDocument()? d.postMessage : null);
	            post && post.apply(w, args);
            }
        }
        
    });
    var MIF = Ext.ux.ManagedIFrame;
    Ext.each(['Component', 'Panel', 'Window'], 
	      function(K){
           MIF[K] && Ext.override(MIF[K] ,{
                /**
                 * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
                 * @param {Mixed} message The message payload.  The payload can be any supported JS type. 
                 * @param {String} tag Optional reference tag 
                 * @param {String} origin Optional domain designation of the sender (defaults
                 * to document.domain).
                 */
	           sendMessage : function(){
                  this.getFrame() && this.frameEl.sendMessage.apply(this.frameEl, arguments);
               }
	       });
    });
    
    var MM = MIF.Manager;
    MM && Ext.apply(MM,{
    
        /**
         * @private
         * @memberOf Ext.ux.ManagedIFrame.ComponentAdapter
         */
        onMessage : function(e){
            
            var be = e.browserEvent;
            //Copy the relevant message members to the Ext.event
            be && Ext.apply(e,{
               origin      : be.origin,
               data        : be.data,
               lastEventId : be.lastEventId,
               source      : be.source
            });
            try {
                var $frame ;
                if ($frame = be && be.source ? be.source.frameElement.id : null){
                    $frame = this.getFrameById($frame);
                    $frame && $frame._observable.fireEvent('message', $frame, e);
                }
            } catch (rhEx) {} 
        },
        /**
         * @private
         */
        destroy : MM.destroy.createSequence(function(){
             (window.postMessage || document.postMessage) &&
                Ext.EventManager.un(window, 'message', this.onMessage, this);  
        })
        
    });
    
     //Add support for postMessage message events if the browser supports it
    MM && (window.postMessage || document.postMessage) &&
        Ext.EventManager.on(window, 'message', MM.onMessage, MM);       
  
  /** @sourceURL=<mifmsg.js> */    
	Ext.provide && Ext.provide('mifmsg');  
})();   

  

  
// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * Ext.ux.IconMenu Plugin 
 *
 * Adds user defined menu to Window/Panel icon and adds dbl click close action
 *
 * @author    Ing. Jozef Sakáloš
 * @date      19. March 2008
 * @version   $Id: Ext.ux.IconMenu.js 114 2008-03-29 00:45:38Z jozo $
 *
 * @license Ext.ux.IconMenu is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/*global Ext */

// still only in svn
if(!Ext.isArray) {
	Ext.isArray = function(v){
		return v && typeof v.pop == 'function';
	}
}

/**
 * @class Ext.ux.IconMenu
 * @extends Ext.util.Observable
 *
 * @constructor
 * Creates new IconMenu
 * @param {Object} config The configuration object
 */
Ext.ux.IconMenu = function(config) {
	// apply config
	Ext.apply(this, config);

	// call parent
	Ext.ux.IconMenu.superclass.constructor.apply(this, arguments);

}; // eo constructor

Ext.extend(Ext.ux.IconMenu, Ext.util.Observable, {

	// configuration options
	// {{{
	/**
	 * @cfg {String} closeText Text for Close item
	 */
	 closeText:'Close'

	/**
	 * @cfg {Boolean} createDefault false to not create default menu
	 */
	,createDefault:true

	/**
	 * @cfg {Boolean} dblClickClose false to disable dblclick close action (defaults to true)
	 */
	,dblClickClose:true

	/**
	 * @cfg {Array} customItems Array of menu items configs.
	 * For example:
	 * new Ext.ux.IconMenu:({
	 *  customItems:['separator'
	 *	,{
	 *		 text:'This is <b>zoom</b> item'
	 *		,iconCls:'icon-zoom'
	 *		,handler:function() {console.info(arguments)}
	 *	},{
	 *		 text:'This is <b>print</b> item'
	 *		,iconCls:'icon-print'
	 *		,handler:function() {console.info(arguments)}
	 *	}]
	 */

	/**
	 * @cfg {Array} defaultItems Default items to create. If you add items you need to add texts and handlers too.
	 */
	,defaultItems:['restore', 'minimize', 'maximize', 'separator', 'close']

	/**
	 * @cfg {String} iconCls Optional but recommended. Icon Class to use. If not set iconCls of Panel/Window is used
	 */

	/**
	 * @cfg {String} maximizeText Text for Maximize item
	 */
	,maximizeText:'Maximize'
	
	/**
	 * @cfg {Ext.menu.Menu} menu Optional. Menu to show on click.
	 */

	/**
	 * @cfg {String} minimizeText Text for Minimize item
	 */
	,minimizeText:'Minimize'

	/**
	 * @cfg {String} qtip Optional. Tooltip to display when mouse cursor hovers over icon
	 */

	/**
	 * @cfg {String} restoreText Text for Restor item
	 */
	,restoreText:'Restore'

	/**
	 * @cfg {String} style Style to use for icon div
	 */
	,style:'width:16px;height:16px;left:0;top:4px;position:absolute;cursor:pointer'

	/**
	 * @cfg {String} tooltip Optional. Synonym for qtip
	 */

	// }}}

	// methods
	// {{{
	/** 
	 * Closes Panel/Window
	 * @private
	 */
	,closeHandler:function() {
		this.hideMenu();
		if(this.panel.closable) {
			this.panel[this.panel.closeAction]();
		}
	} // eo function closeHandler
	// }}}
	// {{{
	/**
	 * Returns menu item identified by cmd
	 * @param {String} cmd 
	 * @return {Ext.menu.Item}
	 */
	,getItemByCmd:function(cmd) {
		if(!this.menu || !cmd) {
			return null;
		}
		var item = this.menu.items.find(function(i) {
			return cmd === i.cmd;
		});
		return item;

	} // eo function getItemByCmd
	// }}}
	// {{{
	/**
	 * Hides menu
	 */
	,hideMenu:function() {
		if(this.menu) {
			this.menu.hide();
		}
	} // eo function hideMenu
	// }}}
	// {{{
	/**
	 * Main init function
	 * @private
	 * @param {Ext.Panel/Ext.Window} panel Panel/Window this plugin is in
	 */
	,init:function(panel) {
		this.panel = panel;
		this.iconCls = this.iconCls || panel.iconCls;
		panel.on({scope:this
			,render:this.onRender
			,hide:this.hideMenu
			,destroy:this.onDestroy
		});
		// create default menu
		var i, menuId = 'im-' + panel.id;

		// create default menu
		if(this.createDefault && Ext.isArray(this.defaultItems) && this.defaultItems.length) {
			if(this.menu) {
				this.menu.add('-');
			}
			else {
				this.menu = new Ext.menu.Menu({id:menuId});
			}
			for(i = 0; i < this.defaultItems.length; i++) {
				this.addItem(this.defaultItems[i]);
			}
		}

		// create custom menu
		if(Ext.isArray(this.customItems)) {
			if(!this.menu) {
				this.menu = new Ext.menu.Menu({id:menuId});
			}
			for(i = 0; i < this.customItems.length; i++) {
				this.addItem(this.customItems[i]);
			}
		}

		// install handlers
		if(this.menu) {
			this.menu.id = this.menu.id || menuId;
			this.menu = Ext.menu.MenuMgr.get(this.menu);
			this.menu.on("show", this.onMenuShow, this);
			this.menu.on("hide", this.onMenuHide, this);
			this.menu.parentPanel = this.panel;
		}
	} // eo function init
	// }}}
	// {{{
	/**
	 * Adds item to menu
	 * @private
	 * @param {String/Object} item String for default items config object for custom items
	 */
	,addItem:function(item) {
		var cfg;
		if('separator' === item || '-' === item) {
			this.menu.add('-');
			return;
		}
		if('string' === typeof item) {
			cfg = {
				 text:this[item + 'Text']
				,cmd:item
				,iconCls:'x-im-icon x-tool x-tool-' + item
				,scope:this
				,handler:this[item + 'Handler']
			};
		}
		else {
			cfg = item;
		}
		this.menu.add(cfg);
	} // eo function addItem
	// }}}
	// {{{
	/**
	 * Maximizes Panel/Window
	 * @private
	 */
	,maximizeHandler:function() {
		this.hideMenu();
		if(this.panel.maximizable) {
			this.panel.maximize();
		}
	} // eo function maximizePanel
	// }}}
	// {{{
	/**
	 * Minimizes Panel/Window
	 * @private
	 */
	,minimizeHandler:function() {
		this.hideMenu();
		if(this.panel.minimizable) {
			this.panel.minimize();
		}
	} // eo function minimizePanel
	// }}}
	// {{{
	/**
	 * Destroys underlying menu
	 * @private
	 */
	,onDestroy:function() {
		if(this.menu) {
			this.menu.hide();
			this.menu.destroy();
		}
	} // eo function onDestroy
	// }}}
	// {{{
	/**
	 * hide menu event handler
	 * @private
	 */
    ,onMenuHide:function(e){
        this.ignoreNextClick = this.restoreClick.defer(350, this);
    } // eo function onMenuHide
	// }}}
	// {{{
	/**
	 * show menu event handler
	 * @private
	 */
    ,onMenuShow:function(e){
        this.ignoreNextClick = 0;
    } // eo function onMenuShow
	// }}}
	// {{{
	/**
	 * Creates new div for icon and installs event handlers
	 * @private
	 */
	,onRender:function() {

		var hd = this.panel.header,
			img;
		if(!hd) {
			return;
		}

		Ext.util.CSS.createStyleSheet('.x-im-icon{float:none!important;margin-left:0!important}');

		// adjust panel header
		hd.addClass('x-panel-icon');
		hd.applyStyles({position:'relative'});

		// create icon div
		this.icon = hd.insertFirst({
			 tag:'div'
			,id:Ext.id()
			,style:this.style
			,cls:this.iconCls
			,qtip:this.qtip || this.tooltip || ''
		}, 'first');

		// panels in layouts create img for icon so align ourselves to it
		img = hd.down('img');
		if(img) {
			this.icon.alignTo(img, 'tl-tl');
			img.removeClass(this.panel.iconCls || this.iconCls);
			img.set({src:Ext.BLANK_IMAGE_URL});
		}

		// install icon event handlers
		this.icon.on({
			 scope:this
			,dblclick:function() {
				if(this.dblClickClose) {
					this.closeHandler();
				}
			}
			,click:{scope:this, delay:200, fn:function(e, t) {
				if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick) {
					this.showMenu();
				}
			}}
		});

		// override panel's setIconClass method
		this.panel.setIconClass = this.setIconClass.createDelegate(this);

	} // eo function onRender
	// }}}
	// {{{
	/**
	 * @private
	 */
    ,restoreClick:function(){
        this.ignoreNextClick = 0;
    } // eo function restoreClick
	// }}}
	// {{{
	/**
	 * Restores Panel/Window
	 * @private
	 */
	,restoreHandler:function() {
		this.hideMenu();
		this.panel.restore();
	} // eo function restorePanel
	// }}}
	// {{{
	/**
	 * Replaces Panel/Window setIconClass method
	 * @private
	 */
	,setIconClass:function(iconCls) {
		this.icon.replaceClass(this.iconCls, iconCls);
	} // eo function setIconClass
	// }}}
	// {{{
	/**
	 * Shows menu
	 */
	,showMenu:function() {
		var item;
		if(this.menu) {
			try {
				// enable/disable close
				item = this.getItemByCmd('close');
				if(item) {
					item.setDisabled(!this.panel.closable);
				}

				// enable/disable maximize
				item = this.getItemByCmd('maximize');
				if(item) {
					item.setDisabled(!this.panel.maximizable || this.panel.maximized);
				}

				// enable/disable minimize
				item = this.getItemByCmd('minimize');
				if(item) {
					item.setDisabled(!this.panel.minimizable || this.panel.minimized);
				}

				// enable/disable restore
				item = this.getItemByCmd('restore');
				if(item) {
					item.setDisabled(!(this.panel.minimized || this.panel.maximized));
				}

				// show menu
				this.menu.show(this.icon, 'tl-bl?');
			} catch(e){}
		}
	}
	// }}}

}); // eo extend

Ext.reg('iconmenu', Ext.ux.IconMenu);

// eof

/*
  * Ext.ux.DatePickerPlus  Addon
  * Ext.ux.form.DateFieldPlus  Addon  
  *
  * @author    Marco Wienkoop (wm003/lubber)
  * @copyright (c) 2008, Marco Wienkoop (marco.wienkoop@lubber.de) http://www.lubber.de
  *
  * @class Ext.ux.DatePickerPlus
  * @extends Ext.DatePicker
  *
  * @class Ext.ux.form.DateFieldPlus
  * @extends Ext.form.DateField
  *
  
  You need at least ExtJS 2.0.2 or higher 
  
Also adds Ext.util.EasterDate
	Calculates the Date-Object of easter-sunday of a given year

-----------------------------------------------------------------------------------------------------
-- DatePickerPlus Extension based on 4 contributed extensions from the ext-forum
-- and of course the original Datepicker from the awesome ExtJS Javascript Library
-----------------------------------------------------------------------------------------------------
-- (1) Multimonth calendar extension (enhanced integration) 
-- (2) Datepicker extension for multiple day/week/month selection (basic idea adopted)
-- (3) Weeknumber display (enhanced integration)
-- (4) XDateField with configurable submitFormat (full integration)
-- using my own getFirstDateOfWeek routine as it is more flexible for choosing which day is the first day of a week (in some countries its sunday, not monday!)
-----------------------------------------------------------------------------------------------------
-- (2) (multimonth calendar)
-- Author: aungii
-- Source: http://extjs.com/forum/showthread.php?t=20597
--
-- (2)  (multiple day/week/month selection)
-- Author: cocorossello / stevenvegt
-- Source: http://extjs.com/forum/showthread.php?t=22473
--
-- (3) (weeknumber display)
-- Author: eni.kao
-- Source: http://extjs.com/forum/showthread.php?t=15635
--
-- (4) (XDateField with configurable submitFormat)
-- Author: jsakalos
-- Source: http://extjs.com/forum/showthread.php?t=25900



 * @license  licensing of Ext.ux.DatePickerPlus and Ext.ux.form.DateFieldPlus depends of the underlying ExtJS Framework Version
 *
 * If you use ExtJS <= 2.0.2 Ext.ux.DatePickerPlus and Ext.ux.form.DateFieldPlus are licensed under the terms of the
 * LGPL v3
 * License details: http://www.gnu.org/licenses/lgpl.html
 * 
 * If you use ExtJS >= 2.1 Ext.ux.DatePickerPlus and Ext.ux.form.DateFieldPlus are licensed under the terms of the
 * GPL v3
 * License details: http://www.gnu.org/licenses/gpl.html

* Donations are always welcome :)
* Any amount is greatly appreciated and will help to continue on developing ExtJS Widgets
*
* You can donate via PayPal to donate@lubber.de
*
	This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
	
 * This Addon requires the ExtJS Library, which is distributed under the terms of the GPL v3 (from V2.1)
 * See http://extjs.com/license for more info
 



  
Revision History:
v.1.3 [2008/08/05]
- Support of ExtJS 2.2
- Adopted new config items from 1.2 to DateFieldPlus also

v.1.2 [2008/08/04]
- support "allowOtherMenus" Config for DateFieldPlus
- datefieldplus can be hidden by clicking the triggerbutton again in cases hiding by clicking outside isn't possible
- added config "styleDisabledDates" to be able to set custom style dates (eventdates/weekends..) on disabled dates also (suggested by descheret)
- added config "eventDatesSelectable" to disble event-dates selection if desired (even if the dates are not disabled at all) (thanks to descheret)
- added config "disableSingleDateSelection" to force user to use week- or monthselection only (suggested by chemist458)
- added config "stayInAllowedRange" when setting minDate/maxDate, this will prevent to change months outside the allowed daterange  (suggested by descheret)
- added config "summarizeHeader" to add an optional global header when using multimonth display containing the month range (e.g. january 2008-october 2008)
- added italian locale (thanks to andreabat)
- BUGFIX: setMinDate/MaxDate/DateLimits did not update the viewport properly

V1.1 Final [2008/06/12]
- added config "allowMouseWheel" to generally turn on/off Mousewheelsupport (suggested by boraldo)
- added event "beforemousewheel" to be able to temporary disable the mousewheel if desired
- added event "beforemaxdays" to be able to cancel the default MessageBox but do something on your own instead
- Implemented fix for xdatefield code to support applyTo Config (thanks to mystix)
- updated russian locale (thanks to WhiteRussian)
- BUGFIX: updating eventclasses (and others) could result in wrong class-definition per cell (reported by aacraig)


V1.1 RC4 [2008/05/20]
- DateFieldPlus now also supports multiselection (thanks to Nohcs777)
- extended xdatefield to support multiselection
- "value" config for datefieldplus now also supports arrays in multiselection mode instead of just one date
- range selection is now also possible for a wider period than only the visible months (suggested by jo2008)
- updated xdatefield code integration to disable/enable the hidden submitfield from datefieldplus also, if the mainformfield gets disabled/enabled
- improved xdatefield code to fill the hiddenField with a given value at config time
- Improved some code-sections (mainly for respecting summertime changings when handling with Date.gettime())
- Corrected eventhandling on Datemenu and DateFieldPlus
- support for minDate and maxDate for Datefieldplus (as an alias for datepickers minValue and maxValue) to be more compatible to usual datepicker/datemenu config options
- added "multiSelectionDelimiter" config (datefieldplus and multiselection only)
- added "renderPrevNextButtons" config (if you want the user not to be able to change the month or force him to use the monthpicker)
- added "renderPrevNextYearButtons" config to display 2 small double-arrow buttons for changing next/previous year 
- added "nextYearText" config which will be displayed as tooltip on NextYear Button (updated locale!)
- added "prevYearText" config which will be displayed as tooltip on PrevYear Button (updated locale!)
- added "showActiveDate" will display the active Date to use keynavigation
- added "shiftSpaceSelect" if set to true (default) and showactivedate is set to true you can select dates on keynavigation by using shift+Space (because the space-key alone will select the today-date)
	if this is set to false , this behaviour reverses (shift+space selects today, space alone select the date under the shown activedate from keynavigation)
- added "disableMonthPicker" config
- added "disabledLetter" config to display e.g. a "X" instead of the daynumber if a date is disabled. (default false)
- added event "beforeyearchange"
- added event "afteryearchange"
- added russian locale (thanks to WhiteRussian)
- UP/DOWN/LEFT/RIGHT Keynavigation is now only available if showActiveDate is set to true and works much faster
- CTRL+UP/DOWN for year changing is now only available if either disableMonthPicker is false or renderPrevNextYearButtons is true
- CTRL+LEFT/RIGHT for month changing is now only available if either disableMonthPicker is false or renderPrevNextButtons is true
- BUGFIX: setEventDates did not update the viewport (reported by aacraig)
- BUGFIX: Array-Cloning was done in a wrong way (reported by lpfLoveExt)
- BUGFIX: weekselection was wrong when a different startDay was given (reported by WhiteRussian)
- Minor Upgrade Version because of much added features instead of a bugfix-only release


V1.0 RC3 [2008/04/21]
- checked to work with ExtJS 2.1
- added config strictRangeSelect (suggested by sigaref)
- added config displayMask and displayMaskText to support update masking 
- added config defaultEventDatesText and defaultEventDatesCls. used if no text/cls-object is given in eventdates
- added Events "aftermonthchange" and "beforemonthchange" (fires everytime the first month changes (by monthpicker or prev/next-month buttons)
- added method setEventDates, to automatically transform given arrays/or objects to working functions, if not already specified
- BUGFIX: range selection over specific months was incorrect

V1.0 RC2 [2008/04/10]
- BUGFIX: typo in DateFieldPlus corrected (reported by sigaref)

V1.0 RC1 [2008/04/10]
- BUGFIX: Undo-Function works again
- BUGFIX: Config items allowedDates and allowedDatesText had no effect on DateFieldPlus

V0.9 Beta 9 [2008/04/09]
- Added config items allowedDates and allowedDatesText
- Added method setAllowedDates()

V0.9 Beta 8 [2008/04/09]
- BUGFIX: setSelectedDates had another BUG...(thanks to wehtam for reporting!)

V0.9 Beta 7 [2008/04/08]
- added the state of the afterdateclick to examine, if the date was selected or unselected, same with week/month
- added option to cleanSelectedDates to not update the picker (e.g. to immediatly add dates manually by setSelectedDates(that would call update twice)
- added option to setSelectedDates to clean the selectedDates before setting the new once and to not update the picker
- BUGFIX: setSelectedDates did not work properly

V0.9 Beta 6 [2008/04/08]
- Added method clearSelectedDates() (suggested by wehtam)
- Added method setSelectedDates() (suggested by wehtam)
- Changes eventtriggering for afterdateclick. It now will always be fired when a date is clicked . Regardless, whether multiSelection is enabled or not.
- BUGFIX: Given listeners to DateFieldPlus have been ignored (reported by Richie1985)

V0.9 Beta 5 [2008/04/07]
- Added method setDateLimits() to change minDate and maxDate at once at runtime
- BUGFIX: Range selection by using the SHIFT-Key for a range more than one month, did not select some remaining days at the end of the range (reported by Spirit)

V0.9 Beta 4 [2008/04/06]
- Added method setMinDate() to change the minDate at runtime and immediatly update the datepicker
- Added method setMaxDate() to change the maxDate at runtime and immediatly update the datepicker
- BUGFIX: hidden submitformat Field had same name as original field, thus confuses IE with duplicate id/name. if name has not been specified in the config or is same as id datefieldplus will add a suffix to the hiddenfield (default "-format"). this field holds the custom submitFormat value

V0.9 Beta 3 [2008/04/06]
- Added xtype "datefieldplus"
- BUGFIX: DateFieldPlus accidently had renderTodayButton set to false by default...

V0.9 Beta 2 [2008/04/06]
- BUGFIX: Width on DateMenu and DateFieldPlus was broken in Firefox 3 (tested on latest Beta 5) (reported by ludoo)
- BUGFIX: Some old testpath in generating transparent GIF images was left in the code and has been deleted now (reported by sanjshah)
- Added new config options
"disablePartialUnselect" : Boolean/String (default true) (suggested by DVSDevise)
When multiselecting whole months or weeks, already selected days within this week/month will _not_ get unselected anymore. Set this to false, if you want them to get unselected.
Note: When the _whole set_ of the month/week are already selected, they get _all_ unselected anyway.

"renderOkUndoButtons" : Boolean (default true) (suggested by jsakalos)
If set to false, the OK- and Undo-Buttons will not be rendered on Multiselection Calendars
This way any selected Date will be immediatly available in the "selectedDates" Array. If used together with DateMenu or DateFieldPlus you need to click outside the Calendar to make it disappear or press Return (if calendar got focus...)
Works only if multiSelection is set to true

"renderTodayButton" : Boolean (default true) (suggested by jsakalos)
Whether the Today-Button should be rendered


V0.9 Beta [2008/04/05]
Initial Release:
Joined the extensions together nicely and added even more features:
- fixed some bugs/improved the original extensions a bit
- works on Original DateMenu and DateField (Ext.ux.form.DateFieldPlus) also
- Definable Multimonths (rows,amount,fill..)
- Custom CSS for definable days
- Weeknumber Display
- Weekend CSS Styling
- National Holidays CSS Styling
- Quicktip support
- Function based custom displayed days
- Multiselection support by CTRL-Key to add single days (when clicked on a date)
- Multiselection support by CTRL-Key to add single weeks (when clicked on a weeknumber)
- Multiselection support by CTRL-Key to add single months (when clicked on the weeknumber header)
- Multiselection support by SHIFT-Key to add a range of days depending on the lastclicked day  (when clicked on a single date)
- returned the prev/next monthbuttons to the monthlabelrow
- implemented mousewheel-event again for comfortable increasing/decreasing months
- implemented monthpicker again to comfortably select the starting month. the monthpicker is rendered on the very first monthname so with only 1 month given, it acts just like the original datepicker
- added quick dayselection routine without calling update() every time. MUCH faster selection, especially when using huge multimonth calendars!
- added "OK"- and "Undo"-Buttons when multiSelection is set to true
- unneccessary renderupdate trigger eliminated (performance-leak on huge multimonthcalendars!) (setvalue-function changed, much more faster now)
- prevented opening a new browsertab in IE7/IE8 when CTRL multiselecting (occured in original multimonth calendar extension and datepicker also if clicked on an empty area within the cell) (default behaviour for a-tags, prevented this by CSS)
- extend keynavigation (RETURN=ok-Button, CTRL as usual)
- added Tooltip functionality to DateFieldPlus just like Buttons (tooltip show on triggerbutton only, this way invalidtext tooltips stay intact)


- Tested in FF2,FF3,IE6,IE7,IE8b,Op9.x,Saf3.1.x(Win)

- Default Language is (of course) english (including US Holidays!)
- Current available localization files (including Holidays):
	german
	russian
	english (for your own translations)

Create a copy of ext.ux.datepickerplus-lang-en.js and change it to your language settings to get this widget easily translated
Be sure to include it AFTER the datepickerwidget!

--- See Release-Notes for Full API Documentation --- 

TODO:
* COMING UP *
- added "resizable" to support resizing of datepicker when displayed as datemenu or datefield and automatically create more/less months and adjust noOfMonth, noOfMonthPerRow

* FUTURE *
- implement time selection also like http://extjs.com/forum/showthread.php?p=170472#post170472
- use the spinner plugin for above time selection if available (or integrate it) or combobox instead (?)
- optional combobox as an alternative to the monthpicker with a given range of previous/next months to select from
- context menu to select predefined dates (12 months ago, next 3 thursdays, etc...thinking of integrating datejs for this ?)
- usage of window.createPopup for IE only to render more quickly (? based on http://extjs.com/forum/showthread.php?t=33331)
- create a new form.datelist item (select-box with multiselect and no dropdown) component to be able to display multiselected dates like datefield after selection
- add config to define the sorting of prevnext(year) buttons (currently the prevnextyear buttons are rendered inside as the usual prevnextmonth buttons are outside anytime)
- support drag selection of days/weeks/months (like in dataview example)
- extend property grid/create plugin to use datepickerplus aswell for date-fields in there
- show monthpicker only (requested in http://extjs.com/forum/showthread.php?t=13911)
- try to speed up rendering-performance, when clicking on next/previous month (update()) and on startup (onRender()) (IE and FF are much slower than Opera(which is equal slow, but renders immediatly every part of the calendar while IE/FF are rendering the complete calender at the end). Safari3.1(Win) renders very fast by now!

* ? BROWSER BUGS ? *
- FF2: CTRL-multiselect clicking leaves an odd blue frame on the cell when clicking in empty areas of the cell (the CSS-Trick for preventing new TABs in IE does not work here...yet :)


*/



Date.prototype.getFirstDateOfWeek = function(startDay) {
//set startDay to Sunday by default
	if (typeof startDay === "undefined") {
		startDay=(Ext.DatePicker?Ext.DatePicker.prototype.startDay:0);
	}
	var dayDiff = this.getDay()-startDay;
	if (dayDiff<0) {
		dayDiff+=7;
	}
	return this.add(Date.DAY,-dayDiff);
};

Array.prototype.sortDates = function() {
	return this.sort(function(a,b){
		return a.getTime() - b.getTime();		
	});
};


if (!Ext.util.EasterDate) {
	Ext.util.EasterDate = function(year, plusDays) {
		if (typeof year === "undefined") {
			year = new Date().getFullYear();
		}
		year = parseInt(year,10);
	
		if (typeof plusDays === "undefined") {
			plusDays = 0;
		}
		plusDays = parseInt(plusDays,10);
		
	//difference to first sunday after first fullmoon after beginning of spring
		var a = year % 19;
		var d = (19 * a + 24) % 30;
		var diffDay = d + (2 * (year % 4) + 4 * (year % 7) + 6 * d + 5) % 7;
		if ((diffDay == 35) || ((diffDay == 34) && (d == 28) && (a > 10))) {
			diffDay -= 7;
		}
	
		var EasterDate = new Date(year, 2, 22);	//beginning of spring
		EasterDate.setTime(EasterDate.getTime() + 86400000 * diffDay + 86400000 * plusDays);
		return EasterDate;
	};
}


Ext.namespace('Ext.ux','Ext.ux.form');

/**
 * @class Ext.ux.DatePickerPlus
 * @extends Ext.DatePicker
 * @constructor
  * @param {Object} config The config object
 */
Ext.ux.DatePickerPlus = Ext.extend(Ext.DatePicker, {
    /**
    * @cfg {Number} noOfMonth
    * No of Month to be displayed
	* Default to 1 so it will displayed as original Datepicker 
    */
    noOfMonth : 1,
	/**
    * @cfg {Array} noOfMonthPerRow
    * No. Of Month to be displayed in a row
    */    
    noOfMonthPerRow : 3,
    /**
    * @cfg {Array} fillupRows
    * eventually extends the number of months to view to fit the given row/column matrix and avoid odd white gaps (especially when using as datemenu fill will lookup ugly when set to false
    */    
	fillupRows : true,
    /**
    * @cfg {Function returns Array} eventDates
    * a Function which returns an Object List of Dates which have an event (show in separate given css-class)
	* This function is called everytime a year has changed when rendering the calendar
	* attributes are date, text(optional) and cls(optional)
	* Its implemented as a function to be able to create cycling days for year
	* example
	* eventDates: function(year) {
		var myDates = 
		[{
			date: new Date(2008,0,1), //fixed date marked only on 2008/01/01
			text: "New Year 2008",
			cls: "x-datepickerplus-eventdates"			
		},
		{
			date: new Date(year,4,11), //will be marked every year on 05/11
			text: "May 11th, Authors Birthday (Age:"+(year-1973)+")",
			cls: "x-datepickerplus-eventdates"
		}];
		return myDates;
	*
	*
    */    
    eventDates : function(year) {
		return [];
	},
	
	styleDisabledDates: false,
	eventDatesSelectable : true,

	defaultEventDatesText : '',
	defaultEventDatesCls : 'x-datepickerplus-eventdates',
	
	setEventDates : function(edArray,update) {
		if (typeof update === "undefined") {
			update=true;
		}
		this.edArray = [];
		for (var i=0,il=edArray.length;i<il;i++) {
			if (Ext.isDate(edArray[i])) {
				this.edArray.push({
					date:edArray[i],
					text:this.defaultEventDatesText,
					cls:this.defaultEventDatesCls
				});
			}
			else if (edArray[i].date) {
				this.edArray.push(edArray[i]);				
			}
		}
		this.eventDates = function(year) {
			return this.edArray;
		};
		if (this.rendered && update) {
			this.eventDatesNumbered = this.convertCSSDatesToNumbers(this.eventDates(this.activeDate.getFullYear()));
			this.update(this.activeDate);
		}
	},
	/**
	 * @cfg {Boolean} eventDatesRE
	 * To selected specific Days over a regular expression
	 */
	eventDatesRE : false,
	
	/**
	 * @cfg {String} eventDatesRECls
	 * Specifies what CSS Class will be applied to the days found by "eventDatesRE"
	 */
	eventDatesRECls : '',
	
	/**
	 * @cfg {String} eventDatesRECls
	 * Specifies what Quicktip will be displayed to the days found by "eventDatesRE"
	 */
	eventDatesREText : '',	
	
	/**
	 * @cfg {Boolean} showWeekNumber
	 * Whether the week number should be shown
	 */
	showWeekNumber : true,
	/**
	 * @cfg {String} weekName
	 * The short name of the week number column
	 */
	weekName : "Wk.",
	/**
	 * @cfg {String} selectWeekText
	 * Text to display when hovering over the weekNumber and multiSelection is enabled
	 */
	selectWeekText : "Click to select all days of this week",
	/**
	 * @cfg {String} selectMonthText
	 * Text to display when hovering over the MonthNumber and multiSelection is enabled
	 * Whole Month selection is disabled when displaying only 1 Month (think twice..)	 
	 */
	selectMonthText : "Click to select all weeks of this month",

	/**
	 * @cfg {String} multiSelection
	 * whether multiselection of dates is allowed. selection of weeks depends on displaying of weeknumbers
	 */
	multiSelection : false,
	/**
	 * @cfg {String} multiSelectByCTRL
	 * whether multiselection is made by pressing CTRL (default behaviour, a single click without CTRL will set the selection list to the last selected day/week) or without (ever click a day is added/removed)
	 */
	
	multiSelectByCTRL : true,

/**
    * @cfg {Array of Dateobjects} selectedDates
    * List of Dates which have been selected when multiselection is set to true (this.value only sets the startmonth then)
    */    
    selectedDates : [],

	/**
    * @cfg {Array of Dateobjects} preSelectedDates
    * contains the same at selection runtime (until "OK" is pressed)
	*/
	preSelectedDates : [], 

	/**
    * @cfg {Object} lastSelectedDate
    * contains the last selected Date or false right after initializing the object..
    */    
	lastSelectedDate : false, 

	/**
	 * @cfg {Array} markNationalHolidays
	 * trigger to add existing nationalHolidays to the eventdates list (nationalholidays can be changed in locale files, so these are independant from custom event Dates
	 */
	markNationalHolidays :true,

	/**
	 * @cfg {String} nationalHolidaysCls
	 * CSS Class displayed to national Holidays if markNationalHolidays is set to true
	 */
	nationalHolidaysCls : 'x-datepickerplus-nationalholidays',
	
	/**
    * @cfg {Function} nationalHolidays
    * returns an Array-List of national Holiday Dates which could by marked with separate given CSS. Will be shown if markNationalHolidays is set to true
	* Change this in your local file to override it with you country's own national Holiday Dates
	*
	* if markNationalHolidays is set to true, a new instance of this array (and thus recalculation of holidays) will be generated at month update, if year has been changed from last drawn month.
	*
    */  

	nationalHolidays : function(year) {
		year = (typeof year === "undefined" ? (this.lastRenderedYear ? this.lastRenderedYear : new Date().getFullYear()) : parseInt(year,10));
//per default the US national holidays are calculated (according to http://en.wikipedia.org/wiki/Public_holidays_of_the_United_States) 
//override this function in your local file to calculate holidays for your own country
//but remember to include the locale file _AFTER_ datepickerplus !
		var dayOfJan01 = new Date(year,0,1).getDay();
		var dayOfFeb01 = new Date(year,1,1).getDay();
		var dayOfMay01 = new Date(year,4,1).getDay();
		var dayOfSep01 = new Date(year,8,1).getDay();
		var dayOfOct01 = new Date(year,9,1).getDay();
		var dayOfNov01 = new Date(year,10,1).getDay();		

		var holidays = 
		[{
			text: "New Year's Day",
			date: new Date(year,0,1)
		},
		{
			text: "Martin Luther King Day", //(every third monday in january)
			date: new Date(year,0,(dayOfJan01>1?16+7-dayOfJan01:16-dayOfJan01))
		},
		{
			text: "Washington's Birthday", //(every third monday in february)
			date: new Date(year,1,(dayOfFeb01>1?16+7-dayOfFeb01:16-dayOfFeb01))
		},
		{
			text: "Memorial Day",//(last Monday in May)
			date: new Date(year,4,(dayOfMay01==6?31:30-dayOfMay01))
		},
		{
			text: "Independence Day",
			date: new Date(year,6,4)
		},
		{
			text: "Labor Day",//(every first monday in September)
			date: new Date(year,8,(dayOfSep01>1?2+7-dayOfSep01:2-dayOfSep01))
		},
		{
			text: "Columbus Day",//(every second monday in october)
			date: new Date(year,9,(dayOfOct01>1?9+7-dayOfOct01:9-dayOfOct01))
		},
		{
			text: "Veterans Day",
			date: new Date(year,10,11)
		},
		{
			text: "Thanksgiving Day",//(Fourth Thursday in November)
			date: new Date(year,10,(dayOfNov01>4?26+7-dayOfNov01:26-dayOfNov01))
		},
		{
			text: "Christmas Day",
			date: new Date(year,11,25)
		}];
		
		return holidays;
	},
	
	/**
	 * @cfg {Boolean} markWeekends
	 * whether weekends should be displayed differently
	 */
	markWeekends :true,
	/**
	 * @cfg {String} weekendCls
	 * CSS class to use for styling Weekends
	 */
	weekendCls : 'x-datepickerplus-weekends',
	/**
	 * @cfg {String} weekendText
	 * Quicktip for Weekends
	 */
	weekendText :'',
	/**
	 * @cfg {Array} weekendDays
	 * Array of Days (according to Days from dateobject thus Sunday=0,Monday=1,...Saturday=6)
	 * Additionally to weekends, you could use this to display e.g. every Tuesday and Thursday with a separate CSS class
	 */
	weekendDays: [6,0],
	
	/**
	 * @cfg {Boolean} useQuickTips
	 * Wheter TIps should be displayed as Ext.quicktips or browsercontrolled title-attributes
	 */
	useQuickTips : true,
	
	/**
	 * @cfg {Number} pageKeyWarp
	 * Amount of Months the picker will move forward/backward when pressing the pageUp/pageDown Keys
	 */
	pageKeyWarp : 1,

	/**
	 * @cfg {Number} maxSelectionDays
	 * Amount of Days that are selectable, set to false for unlimited selection
	 */
	maxSelectionDays : false,
	
	maxSelectionDaysTitle : 'Datepicker',
	maxSelectionDaysText : 'You can only select a maximum amount of %0 days',
	/**
	 * @cfg {Number} minSelectionDays
	 * Amount of Days that are selectable, set to false for unlimited selection
	 */
	minSelectionDays : false,
	
	minSelectionDaysTitle : 'Datepicker',
	minSelectionDaysText : 'You can only select a minimum amount of %0 days',
	
	undoText : "Undo",
	
	
	/**
	 * @cfg {Boolean} stayInAllowedRange
	 * used then mindate/maxdate is set to prevent changing to a month that does not contain allowed dates
	 */
	stayInAllowedRange: true,

	/**
	 * @cfg {Boolean} summarizeHeader
	 * displays the from/to daterange on top of the datepicker
	 */
	summarizeHeader:false,
	
	/**
	 * @cfg {Boolean} resizable
	 * Whether the calendar can be extended with more/less months by simply resizing it like window
	 */
	resizable: false,
	
	/**
	 * @cfg {Boolean} renderOkUndoButtons
	 * If set to true, the OK- and Undo-Buttons will not be rendered on Multiselection Calendars
	 */
	renderOkUndoButtons : true,

	/**
	 * @cfg {Boolean} renderTodayButton
	 * Whether the Today Button should be rendered
	 */
	renderTodayButton : true,
	/**
	 * @cfg {Boolean} disablePartialUnselect
	 * When multiselecting whole months or weeks, already selected days within this week/month will _not_ get unselected anymore. Set this to false, if you want them to get unselected.
	 * Note: When the _whole set_ of the month/week are already selected, they get _all_ unselected anyway.
	 */
	disablePartialUnselect: true,
	
	allowedDates : false,
	allowedDatesText : '',

	strictRangeSelect : false,

	/**
	 * @cfg {Boolean/Number} displayMask
	 * As huge multimonth calendars can take some updating time this will display a mask when the noOfMonth property is higher than the given value in displayMask.
	 * Set to false to never display the mask
	 * default is 3
	 */
	displayMask:3,
	
	displayMaskText: 'Please wait...',
	
	renderPrevNextButtons: true,
	renderPrevNextYearButtons: false,
	disableMonthPicker:false,
	
	nextYearText: "Next Year (Control+Up)",
	prevYearText: "Previous Year (Control+Down)",
	
	showActiveDate: false,
	shiftSpaceSelect: true,
	disabledLetter: false,
	
	allowMouseWheel: true,
	
//this is accidently called too often in the original (when hovering over monthlabel or bottombar..there is no need to update the cells again and just leaks performance)
	focus: Ext.emptyFn,
	
	initComponent : function(){
		Ext.ux.DatePickerPlus.superclass.initComponent.call(this);
		this.noOfMonthPerRow = this.noOfMonthPerRow > this.noOfMonth ?this.noOfMonth : this.noOfMonthPerRow;
        this.addEvents(
            /**
             * @event beforeyearchange
             * Fires before a new year is selected (or prevYear/nextYear buttons)
             * @param {DatePicker} this
             * @param {oldyearnumber} dates The previous selected year
             * @param {newyearnumber} dates The new selected year
             */
            'beforeyearchange',
            /**
             * @event afteryearchange
             * Fires before a new year is selected (by prevYear/nextYear buttons)
             * @param {DatePicker} this
             * @param {oldyearnumber} dates The previous selected year		 
             * @param {newyearnumber} dates The new selected year
             */
            'afteryearchange',
            /**
             * @event beforemonthchange
             * Fires before a new startmonth is selected (by monthpicker or prev/next buttons)
             * @param {DatePicker} this
             * @param {oldmonthnumber} dates The previous selected month	 
             * @param {newmonthnumber} dates The new selected month
             */
            'beforemonthchange',
            /**
             * @event aftermonthchange
             * Fires before a new startmonth is selected (by monthpicker or prev/next buttons)
             * @param {DatePicker} this
             * @param {oldmonthnumber} dates The previous selected month			 
             * @param {newmonthnumber} dates The new selected month
             */
            'aftermonthchange',
            /**
             * @event aftermonthclick
             * Fires after a full month is (un)selected
             * @param {DatePicker} this
             * @param {monthnumber} dates The selected month
             */
            'aftermonthclick',
            /**
             * @event afterweekclick
             * Fires after a week is (un)selected
             * @param {DatePicker} this
             * @param {dateobject} dates The first date of selected week
             */
            'afterweekclick',
            /**
             * @event afterweekclick
             * Fires after a single day is (un)selected
             * @param {DatePicker} this
             * @param {dateobject} dates The selected date
             */
            'afterdateclick',
            /**
             * @event undo
             * Fires when Undo Button is clicked on multiselection right before deleting the preselected dates
             * @param {DatePicker} this
             * @param {Array} dates The preselected Dates
             */
            'undo',
            /**
             * @event beforemousewheel
             * Fires before a mousewheel event should be triggered return false in your function to disable the month change
             * @param {DatePicker} this
             * @param {object} event object
             */
			'beforemousewheel',
            /**
             * @event beforemousewheel
             * Fires before the default message box appears when max days have been reached
			 * return false to cancel the messagebox (to do something on your own)
             * @param {DatePicker} this
             * @param {object} event object
             */
			'beforemaxdays');
	},  
	
	activeDateKeyNav: function(direction) {
		if (this.showActiveDate) {
			this.activeDate = this.activeDate.add("d", direction);
			var adCell = this.activeDateCell.split("#");
			var tmpMonthCell = parseInt(adCell[0],10);
			var tmpDayCell = parseInt(adCell[1],10);
			var currentGetCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
//cursor gets out of visible range?
			if (	(tmpDayCell+direction>41 && tmpMonthCell+1>=this.cellsArray.length)	||
					(tmpDayCell+direction<0 && tmpMonthCell-1<0)	){
				this.update(this.activeDate);
			}
			else {
				currentGetCell.removeClass("x-datepickerplus-activedate");
				tmpDayCell+=direction;
				if (tmpDayCell>41) {
					tmpDayCell-=42;
					tmpMonthCell++;
				}
				else if (tmpDayCell<0) {
					tmpDayCell+=42;
					tmpMonthCell--;
				}
				currentGetCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
				currentGetCell.addClass("x-datepickerplus-activedate");
				this.activeDateCell = tmpMonthCell+"#"+tmpDayCell;
			}
		}
	},

    handleMouseWheel : function(e){
        if(this.fireEvent("beforemousewheel", this,e) !== false){
			var oldStartMonth = (this.activeDate ? this.activeDate.getMonth() : 99);
			var oldStartYear = (this.activeDate ? this.activeDate.getFullYear() : 0);			
			Ext.ux.DatePickerPlus.superclass.handleMouseWheel.call(this,e);
			var newStartMonth = (this.activeDate ? this.activeDate.getMonth() : 999);
			var newStartYear = (this.activeDate ? this.activeDate.getFullYear() : 9999);
			if (oldStartMonth!=newStartMonth) {
				this.fireEvent("aftermonthchange", this, oldStartMonth, newStartMonth);
			}
			if (oldStartYear!=newStartYear) {
				this.fireEvent("afteryearchange", this, oldStartYear, newStartYear);
			}
		}
	},
	
    // private
	onRender : function(container, position){
		if (this.noOfMonthPerRow===0) {
			this.noOfMonthPerRow = 1;
		}
		if (this.fillupRows && this.noOfMonthPerRow > 1 && this.noOfMonth % this.noOfMonthPerRow!==0) {
			this.noOfMonth+= (this.noOfMonthPerRow - (this.noOfMonth % this.noOfMonthPerRow));
		}
		var addIEClass = (Ext.isIE?' x-datepickerplus-ie':'');
		var m = ['<table cellspacing="0"',(this.multiSelection?' class="x-date-multiselect'+addIEClass+'" ':(addIEClass!==''?'class="'+addIEClass+'" ':'')),'>'];

		m.push("<tr>");

		var widfaker = (Ext.isIE?'<img src="'+Ext.BLANK_IMAGE_URL+'" />':'');
		var weekNumberQuickTip = (this.multiSelection ? (this.useQuickTips? ' ext:qtip="'+this.selectWeekText+'" ' :' title="'+this.selectWeekText+'" ') : '');
//as weekends (or defined weekly cycles) are displayed on every month at the same place, we can render the quicktips here to save time in update process
		var weekEndQuickTip = (this.markWeekends && this.weekendText!==''? (this.useQuickTips? ' ext:qtip="'+this.weekendText+'" ' :' title="'+this.weekendText+'" '):'');


//calculate the HTML of one month at first to gain some speed when rendering many calendars
		var mpre = ['<thead><tr>'];
		if (this.showWeekNumber) {
			mpre.push('<th class="x-date-weeknumber-header"><a href="#" hidefocus="on" class="x-date-weeknumber" tabIndex="1"><em><span ',(this.multiSelection ? (this.useQuickTips? ' ext:qtip="'+this.selectMonthText+'" ' :' title="'+this.selectMonthText+'" ') : ''),'>' + this.weekName + '</span></em></a></th>');
		}
		
		var dn = this.dayNames;
		for(var i = 0; i < 7; i++){
		   var d = this.startDay+i;
		   if(d > 6){
			   d = d-7;
		   }
			mpre.push('<th><span>', dn[d].substr(0,1), '</span></th>');
		}
		mpre.push('</tr></thead><tbody><tr>');

		if (this.showWeekNumber) {
			mpre.push('<td class="x-date-weeknumber-cell"><a href="#" hidefocus="on" class="x-date-weeknumber" tabIndex="1"><em><span ',weekNumberQuickTip,'></span></em></a></td>');
		}
		
		for(var k = 0; k < 42; k++) {
			if(k % 7 === 0 && k > 0){
				if (this.showWeekNumber) {
					mpre.push('</tr><tr><td class="x-date-weeknumber-cell"><a href="#" hidefocus="on" class="x-date-weeknumber" tabIndex="1"><em><span ',weekNumberQuickTip,'></span></em></a></td>');
				} else {
					mpre.push('</tr><tr>');
				}
			}
			mpre.push('<td class="x-date-date-cell"><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span ',(this.weekendDays.indexOf((k+this.startDay)%7)!=-1?weekEndQuickTip:''),'></span></em></a></td>');
		}
		mpre.push('</tr></tbody></table></td></tr></table></td>');
		var prerenderedMonth = mpre.join("");

		if (this.summarizeHeader && this.noOfMonth > 1) {
			m.push('<td align="center" id="',this.id,'-summarize" colspan="',this.noOfMonthPerRow,'" class="x-date-middle x-date-pickerplus-middle"></td></tr>');
			m.push("<tr>");
		}

		for(var x=0; x<this.noOfMonth; x++) {            
            m.push('<td><table class="x-date-pickerplus',(x%this.noOfMonthPerRow===0?'':' x-date-monthtable'),'" cellspacing="0"><tr>');
			if (x===0) {
				m.push('<td class="x-date-left">');
				if (this.renderPrevNextButtons) {
					m.push('<a class="npm" href="#" ',(this.useQuickTips? ' ext:qtip="'+this.prevText+'" ' :' title="'+this.prevText+'" '),'></a>');
				}
				if (this.renderPrevNextYearButtons) {
					m.push('<a class="npy" href="#" ',(this.useQuickTips? ' ext:qtip="'+this.prevYearText+'" ' :' title="'+this.prevYearText+'" '),'></a>');
				}
				m.push('</td>');
			}			
			else if (x==this.noOfMonthPerRow-1) {
				if (this.renderPrevNextButtons) {				
					m.push('<td class="x-date-dummy x-date-middle">',widfaker,'</td>');
				}
			}			
            m.push("<td class='x-date-middle x-date-pickerplus-middle",(x===0 && !this.disableMonthPicker ?" x-date-firstMonth":""),"' align='center'>");
			if (x>0 || this.disableMonthPicker) {
				m.push('<span id="',this.id,'-monthLabel', x , '"></span>');
			}
			m.push('</td>');
			if (x==this.noOfMonthPerRow-1)	{
				m.push('<td class="x-date-right">');
				if (this.renderPrevNextButtons) {				
					m.push('<a class="npm" href="#" ', (this.useQuickTips? ' ext:qtip="'+this.nextText+'" ' :' title="'+this.nextText+'" ') ,'></a>');
				}
				if (this.renderPrevNextYearButtons) {
					m.push('<a class="npy" href="#" ',(this.useQuickTips? ' ext:qtip="'+this.nextYearText+'" ' :' title="'+this.nextYearText+'" '),'></a>');
				}
				m.push('</td>');				
			}
			else if (x===0) {
				if (this.renderPrevNextButtons) {				
					m.push('<td class="x-date-dummy x-date-middle">',widfaker,'</td>');
				}
			}			
			
            m.push('</tr><tr><td',(x===0 || x==this.noOfMonthPerRow-1?' colspan="3" ':''),'><table class="x-date-inner" id="',this.id,'-inner-date', x ,'" cellspacing="0">');

			m.push(prerenderedMonth);
	
            if( (x+1) % this.noOfMonthPerRow === 0) {
                m.push("</tr><tr>");
            }            
        }
        m.push('</tr>');
		
		m.push('<tr><td',(this.noOfMonthPerRow>1?' colspan="'+this.noOfMonthPerRow+'"':''),' class="x-date-bottom" align="center"><div><table width="100%" cellpadding="0" cellspacing="0"><tr><td align="right" class="x-date-multiokbtn">',widfaker,'</td><td align="center" class="x-date-todaybtn">',widfaker,'</td><td align="left" class="x-date-multiundobtn">',widfaker,'</td></tr></table></div></td></tr>');
		
		m.push('</table><div class="x-date-mp"></div>');
        var el = document.createElement("div");
        el.className = "x-date-picker";
        el.innerHTML = m.join("");  

        container.dom.insertBefore(el, position);

        this.el = Ext.get(el);        
        this.eventEl = Ext.get(el.firstChild);

		if (this.renderPrevNextButtons) {
			var crl = new Ext.util.ClickRepeater(this.el.child("td.x-date-left a.npm"), {
				handler: this.showPrevMonth,
				scope: this,
				preventDefault:true,
				stopDefault:true
			});
	
			var crr = new Ext.util.ClickRepeater(this.el.child("td.x-date-right a.npm"), {
				handler: this.showNextMonth,
				scope: this,
				preventDefault:true,
				stopDefault:true
			});
		}
		
		if (this.renderPrevNextYearButtons) {
			var cryl = new Ext.util.ClickRepeater(this.el.child("td.x-date-left a.npy"), {
				handler: this.showPrevYear,
				scope: this,
				preventDefault:true,
				stopDefault:true
			});
	
			var cryr = new Ext.util.ClickRepeater(this.el.child("td.x-date-right a.npy"), {
				handler: this.showNextYear,
				scope: this,
				preventDefault:true,
				stopDefault:true
			});
		}
		if (this.allowMouseWheel) {
			this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
		}

		if (!this.disableMonthPicker) {
	        this.monthPicker = this.el.down('div.x-date-mp');
	        this.monthPicker.enableDisplayMode('block');
		}


        var kn = new Ext.KeyNav(this.eventEl, {
            "left" : function(e){
                (e.ctrlKey && (!this.disableMonthPicker || this.renderPrevNextButtons) ?
                    this.showPrevMonth() :
					this.activeDateKeyNav(-1));
            },

            "right" : function(e){
                (e.ctrlKey && (!this.disableMonthPicker || this.renderPrevNextButtons) ?
                    this.showNextMonth() :
					this.activeDateKeyNav(1));
            },

            "up" : function(e){
                (e.ctrlKey && (!this.disableMonthPicker || this.renderPrevNextYearButtons) ?
                    this.showNextYear() :
					this.activeDateKeyNav(-7));
            },

            "down" : function(e){
                (e.ctrlKey && (!this.disableMonthPicker || this.renderPrevNextYearButtons) ?
                    this.showPrevYear() :
					this.activeDateKeyNav(7));
            },

            "pageUp" : function(e){
		        this.update(this.activeDate.add("mo", this.pageKeyWarp*(-1)));
            },

            "pageDown" : function(e){
		        this.update(this.activeDate.add("mo", this.pageKeyWarp));
            },

            "enter" : function(e){
                e.stopPropagation();
				if (this.multiSelection) {
					this.okClicked();
				}
				else {
					this.finishDateSelection(this.activeDate);
				}
                return true;
            }, 
            scope : this 
        });

		if (!this.disableSingleDateSelection) {
			this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
		}
		if (this.multiSelection && this.showWeekNumber) {
			this.eventEl.on("click", this.handleWeekClick,  this, {delegate: "a.x-date-weeknumber"});
		}
        this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.spaceKeyPressed,  this);		
        
        this.cellsArray = [];
        this.textNodesArray = [];
        this.weekNumberCellsArray = [];
        this.weekNumberTextElsArray = [];		
		this.weekNumberHeaderCellsArray = [];
	
		var cells,textNodes,weekNumberCells,weekNumberTextEls,weekNumberHeaderCells;
        for(var xx=0; xx< this.noOfMonth; xx++) {
            cells = Ext.get(this.id+'-inner-date'+xx).select("tbody td.x-date-date-cell");
            textNodes = Ext.get(this.id+'-inner-date'+xx).query("tbody td.x-date-date-cell span");
            this.cellsArray[xx] = cells;
            this.textNodesArray[xx] = textNodes;
			if (this.showWeekNumber) {
				weekNumberCells = Ext.get(this.id+'-inner-date'+xx).select("tbody td.x-date-weeknumber-cell");
				weekNumberTextEls = Ext.get(this.id+'-inner-date'+xx).select("tbody td.x-date-weeknumber-cell span");				
				this.weekNumberCellsArray[xx] = weekNumberCells;
				this.weekNumberTextElsArray[xx] = weekNumberTextEls;				
				weekNumberHeaderCells = Ext.get(this.id+'-inner-date'+xx).select("th.x-date-weeknumber-header");
				this.weekNumberHeaderCellsArray[xx] = weekNumberHeaderCells;
			}
        }

//set the original monthpicker again to the first month only to be able to quickly change the startmonth		
		if (!this.disableMonthPicker) {
			this.mbtn = new Ext.Button({
				text: " ",
				tooltip: this.monthYearText,
				renderTo: this.el.child("td.x-date-firstMonth", true)			
			});
	
			this.mbtn.on('click', this.showMonthPicker, this);
			this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
		}

//showtoday from Ext 2.2
		if (this.renderTodayButton || this.showToday) {
	        var today = new Date().dateFormat(this.format);			
			this.todayBtn = new Ext.Button({
				renderTo: this.el.child("td.x-date-bottom .x-date-todaybtn", true),
                text: String.format(this.todayText, today),
				tooltip: String.format(this.todayTip, today),
				handler: this.selectToday,
				scope: this
			});
		}
		
		if (this.multiSelection && this.renderOkUndoButtons) {
			this.OKBtn = new Ext.Button({
	            renderTo: this.el.child("td.x-date-bottom .x-date-multiokbtn", true),
				text: this.okText,
				handler: this.okClicked,
				scope: this
			});

			this.undoBtn = new Ext.Button({
	            renderTo: this.el.child("td.x-date-bottom .x-date-multiundobtn", true),
				text: this.undoText,
				handler: function() {
		            this.fireEvent("undo", this, this.preSelectedDates);
					this.preSelectedDates = [];
					for (var i=0,il=this.selectedDates.length;i<il;i++) {
						this.preSelectedDates.push(this.selectedDates[i].clearTime().getTime());
					}
					this.update(this.activeDate);
				},
				scope: this
			});
		}
		
//In development...
/*
		if (this.resizable) {		
			var resizer = new Ext.Resizable(this.el, {
				handles: 'all',
// at least one month should be displayed				
				minWidth:200,
				minHeight:300,
				maxWidth: 1000,
				maxHeight: 800,
				heightIncrement: 250,
				widthIncrement: 200,
				adjustments: 'auto',	
				transparent:true
			});
			resizer.on("resize", function(){
	//			alert("you resized the calendar,ouch!");
			},this);
		}
*/

		if(Ext.isIE){
            this.el.repaint();
        }
//preselect dates if given
		this.preSelectedDates = [];
		for(var sdc=0, sdcl=this.selectedDates.length; sdc < sdcl; sdc++) {
		   this.preSelectedDates.push(this.selectedDates[sdc].clearTime().getTime());
		}

        this.update(this.value);
    },

//converts all custom dates to timestamps numbers for faster calculations and splits their attributes into separate arrays
	convertCSSDatesToNumbers : function(objarr) {
//date,text,class		
		var converted =  [[],[],[]];
		for (var i=0,il=objarr.length;i<il;i++) {
			converted[0][i] = objarr[i].date.clearTime().getTime();
			converted[1][i] = (objarr[i].text ? objarr[i].text : this.defaultEventDatesText);
			converted[2][i] = (objarr[i].cls ? objarr[i].cls : this.defaultEventDatesCls);
		}
		return converted;
	},

	clearSelectedDates : function(update) {
		if (typeof update === "undefined") {
			update=true;
		}
		this.selectedDates = [];
		this.preSelectedDates = [];
		if (this.rendered && update) {
			this.update(this.activeDate);
		}
	},
	
	setSelectedDates : function(dates,update) {
		if (typeof update === "undefined") {
			update=true;
		}
		if (!Ext.isArray(dates)) {
			dates = [dates];
		}
		var d, dt;
		for (var i=0,il=dates.length;i<il;i++) {
			d = dates[i];
			dt = d.clearTime().getTime();
			if (this.preSelectedDates.indexOf(dt)==-1) {
				this.preSelectedDates.push(dt);
				this.selectedDates.push(d);				
			}
		}
		if (this.rendered && update) {
			this.update(this.activeDate);
		}
	},

	setAllowedDates : function(dates,update) {
		if (typeof update === "undefined") {
			update=true;
		}
		this.allowedDates = dates;
		if (this.rendered && update) {
			this.update(this.activeDate);
		}
	},

	setMinDate: function(minDate) {
		this.minDate = minDate;
        this.update(this.value, true);
	},

	setMaxDate: function(maxDate) {
		this.maxDate = maxDate;
        this.update(this.value, true);
	},

	setDateLimits: function(minDate,maxDate) {
		this.minDate = minDate;
		this.maxDate = maxDate;
        this.update(this.value, true);
	},

	
	// private
//forcerefresh option from ext 2.2 just included to be compatible	
    update : function(date, forceRefresh ,masked){
		if (typeof masked==="undefined")  {
			masked = false;
		}
		if (typeof forceRefresh==="undefined")  {
			forceRefresh = false;
		}
		
		if (forceRefresh) {
			var ad = this.activeDate;
			this.activeDate = null;
			date = ad;			
		}				
		
		var dMask = (this.displayMask && (isNaN(this.displayMask) || this.noOfMonth > this.displayMask)? true: false);
		
		if (!masked && dMask) {
			this.el.mask(this.displayMaskText);
//set forcerefresh to false because new date (from old activedate) is already calculated
			this.update.defer(10, this, [date,false,true]);
			return false;
		}
		
		if (this.stayInAllowedRange && (this.minDate||this.maxDate)) {
			if (this.minDate && (this.minDate.getFullYear() > date.getFullYear() || (this.minDate.getMonth() > date.getMonth() && this.minDate.getFullYear() == date.getFullYear()))) {
				date = new Date(this.minDate.getTime());
			}
			else if (this.maxDate && (this.maxDate.getFullYear() < date.getFullYear() || (this.maxDate.getMonth() < date.getMonth() && this.maxDate.getFullYear() == date.getFullYear()))) {
				date = new Date(this.maxDate.getTime());
			}
		}
		
		var newStartMonth = date.getMonth();
		var oldStartMonth = (this.activeDate ? this.activeDate.getMonth() : newStartMonth);
		var newStartYear = date.getFullYear();
		var oldStartYear = (this.activeDate ? this.activeDate.getFullYear() : newStartYear);
		
		if (oldStartMonth!=newStartMonth) {
            this.fireEvent("beforemonthchange", this, oldStartMonth, newStartMonth);			
		}
		if (oldStartYear!=newStartYear) {
            this.fireEvent("beforeyearchange", this, oldStartYear, newStartYear);
		}
		
        this.activeDate = date.clearTime();
		this.preSelectedCells = [];
		this.lastSelectedDateCell = '';
		this.activeDateCell = '';
		var lsd = (this.lastSelectedDate?this.lastSelectedDate:0);
		var today = new Date().clearTime().getTime();
		var min = this.minDate ? this.minDate.clearTime().getTime() : Number.NEGATIVE_INFINITY;
		var max = this.maxDate ? this.maxDate.clearTime().getTime() : Number.POSITIVE_INFINITY;
		var ddMatch = this.disabledDatesRE;
		var ddText = this.disabledDatesText;
		var ddays = this.disabledDays ? this.disabledDays.join("") : false;
		var ddaysText = this.disabledDaysText;
		
		var edMatch = this.eventDatesRE;
		var edCls = this.eventDatesRECls;
		var edText = this.eventDatesREText;		

		var adText = this.allowedDatesText;
		
		var format = this.format;
		var adt = this.activeDate.getTime();
		
		this.todayMonthCell	= false;
		this.todayDayCell = false;
		if (this.allowedDates) {
			this.allowedDatesT = [];
			for (var k=0, kl=this.allowedDates.length;k<kl;k++) {
				this.allowedDatesT.push(this.allowedDates[k].clearTime().getTime());
			}
		}
		var setCellClass = function(cal, cell,textnode,d){
	
			var foundday, eCell = Ext.get(cell), eTextNode = Ext.get(textnode), t = d.getTime(), tiptext=false, fvalue;
			cell.title = "";
			cell.firstChild.dateValue = t;

//check this per day, so holidays between years in the same week will be recognized (newyear in most cases),
//yearly eventdates are also possible then
			var dfY = d.getFullYear();
			if (cal.lastRenderedYear!==dfY) {
				cal.lastRenderedYear=dfY;
				if(cal.markNationalHolidays) {
//neue berechnete holiday-liste erzeugen
					cal.nationalHolidaysNumbered = cal.convertCSSDatesToNumbers(cal.nationalHolidays(dfY));
				}
				cal.eventDatesNumbered = cal.convertCSSDatesToNumbers(cal.eventDates(dfY));
			}
			
			// disabling
			if(t < min) {
				cell.className = " x-date-disabled";
				tiptext = cal.minText;				
			}
			if(t > max) {
				cell.className = " x-date-disabled";
				tiptext = cal.maxText;
			}
			if(ddays){
				if(ddays.indexOf(d.getDay()) != -1){
					tiptext = ddaysText;
					cell.className = " x-date-disabled";
				}
			}
			if(ddMatch && format){
				fvalue = d.dateFormat(format);
				if(ddMatch.test(fvalue)){
					tiptext = ddText.replace("%0", fvalue);					
					cell.className = " x-date-disabled";
				}
			}

			if (cal.allowedDates && cal.allowedDatesT.indexOf(t)==-1){
				cell.className = " x-date-disabled";
				tiptext = adText;
			}

			//mark weekends
			if(cal.markWeekends && cal.weekendDays.indexOf(d.getDay()) != -1 && !eCell.hasClass('x-date-disabled')) {
				eCell.addClass(cal.weekendCls);
			}
			

			if(!eCell.hasClass('x-date-disabled') || cal.styleDisabledDates) {
//mark dates with specific css (still selectable) (higher priority than weekends)
				if (cal.eventDatesNumbered[0].length>0) {
					foundday = cal.eventDatesNumbered[0].indexOf(t);
					if (foundday!=-1) {
						if(cal.eventDatesNumbered[2][foundday]!==""){						
							eCell.addClass(cal.eventDatesNumbered[2][foundday]+(cal.eventDatesSelectable?"":"-disabled"));
							tiptext = (cal.eventDatesNumbered[1][foundday]!=="" ? cal.eventDatesNumbered[1][foundday] : false);
						}
					}
				}

//regular Expression custom CSS Dates
				if(edMatch && format){
					fvalue = d.dateFormat(format);
					if(edMatch.test(fvalue)){
						tiptext = edText.replace("%0", fvalue);					
						cell.className = edCls;
					}
				}
			}
			
			
			if(!eCell.hasClass('x-date-disabled')) {
//mark Holidays				
				if(cal.markNationalHolidays && cal.nationalHolidaysNumbered[0].length>0) {
					foundday = cal.nationalHolidaysNumbered[0].indexOf(t);
					if (foundday!=-1) {
						eCell.addClass(cal.nationalHolidaysCls);
						tiptext = (cal.nationalHolidaysNumbered[1][foundday]!=="" ? cal.nationalHolidaysNumbered[1][foundday] : false);
					}
				}
				
				
//finally mark already selected items as selected
				if (cal.preSelectedDates.indexOf(t)!=-1) {
					eCell.addClass("x-date-selected");
					cal.preSelectedCells.push(cell.firstChild.monthCell+"#"+cell.firstChild.dayCell);
				}
				
				if (t == lsd) {
					cal.lastSelectedDateCell = cell.firstChild.monthCell+"#"+cell.firstChild.dayCell;
				}
				
			}
			else if (cal.disabledLetter){
				textnode.innerHTML = cal.disabledLetter;
			}

//mark today afterwards to ensure today CSS has higher priority
			if(t == today){
				eCell.addClass("x-date-today");
				tiptext = cal.todayText;
			}

//keynavigation?
			if(cal.showActiveDate && t == adt && cal.activeDateCell === ''){
				eCell.addClass("x-datepickerplus-activedate");
				cal.activeDateCell = cell.firstChild.monthCell+"#"+cell.firstChild.dayCell;
			}

//any quicktips necessary?
			if (tiptext) {
				if (cal.useQuickTips) {
					Ext.QuickTips.register({
						target: eTextNode,
						text: tiptext
					});
				}
				else {
					cell.title = tiptext;
				}
			}
			
			
		};

		var cells,textEls,days,firstOfMonth,startingPos,pm,prevStart,d,sel,i,intDay,weekNumbers,weekNumbersTextEls,curWeekStart,weekNumbersHeader,monthLabel,main,w;
		var qturfn = function(c){
			Ext.QuickTips.unregister(c);
		};
		var summarizeHTML = [];
		for(var x=0;x<this.noOfMonth;x++) {
			if (this.summarizeHeader && this.noOfMonth > 1 && (x==0||x==this.noOfMonth-1)) {
				summarizeHTML.push(this.monthNames[date.getMonth()]," ",date.getFullYear());
				if (x==0) {
					summarizeHTML.push(" - ");
				}
			}
			cells = this.cellsArray[x].elements;
			textEls = this.textNodesArray[x];

			if ((this.markNationalHolidays || this.eventDates.length>0) && this.useQuickTips) {
				Ext.each(textEls,qturfn,this);
			}
			
			days = date.getDaysInMonth();
			firstOfMonth = date.getFirstDateOfMonth();
			startingPos = firstOfMonth.getDay()-this.startDay;
	
			if(startingPos <= this.startDay){
				startingPos += 7;
			}
	
			pm = date.add("mo", -1);
			prevStart = pm.getDaysInMonth()-startingPos;
	
			days += startingPos;
	
			d = new Date(pm.getFullYear(), pm.getMonth(), prevStart).clearTime();
	
			i = 0;
			if (this.showWeekNumber) {
				weekNumbers = this.weekNumberCellsArray[x].elements;
				weekNumbersTextEls = this.weekNumberTextElsArray[x].elements;				
				curWeekStart = new Date(d);
				curWeekStart.setDate(curWeekStart.getDate() + 7);
				
				weekNumbersHeader = this.weekNumberHeaderCellsArray[x].elements;
				weekNumbersHeader[0].firstChild.monthValue = date.getMonth();
				weekNumbersHeader[0].firstChild.dateValue = curWeekStart.getTime();				
				weekNumbersHeader[0].firstChild.monthCell = x;
				weekNumbersHeader[0].firstChild.dayCell = 0;
				
				while(i < weekNumbers.length) {
					weekNumbersTextEls[i].innerHTML = curWeekStart.getWeekOfYear();
					weekNumbers[i].firstChild.dateValue = curWeekStart.getTime();
					weekNumbers[i].firstChild.monthCell = x;
					weekNumbers[i].firstChild.dayCell = (i*7);
					curWeekStart.setDate(curWeekStart.getDate() + 7);
					i++;
				}
				i = 0;
			}

			for(; i < startingPos; i++) {
				textEls[i].innerHTML = (++prevStart);
				cells[i].firstChild.monthCell = x;
				cells[i].firstChild.dayCell = i;
				
				d.setDate(d.getDate()+1);
				cells[i].className = "x-date-prevday";
				setCellClass(this, cells[i],textEls[i],d);
			}
			
			for(; i < days; i++){
				intDay = i - startingPos + 1;
				textEls[i].innerHTML = (intDay);
				cells[i].firstChild.monthCell = x;
				cells[i].firstChild.dayCell = i;
				d.setDate(d.getDate()+1);
				cells[i].className = "x-date-active";
				setCellClass(this, cells[i],textEls[i],d);
				if(d.getTime() == today){
					this.todayMonthCell	= x;
					this.todayDayCell = i;
				}
			}
		
			var extraDays = 0;
			for(; i < 42; i++) {
				textEls[i].innerHTML = (++extraDays);
				cells[i].firstChild.monthCell = x;
				cells[i].firstChild.dayCell = i;
				d.setDate(d.getDate()+1);
				cells[i].className = "x-date-nextday";
				setCellClass(this, cells[i],textEls[i],d);
			}

			if (x===0 && !this.disableMonthPicker) {
				this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
			}
			else {
				monthLabel = Ext.get(this.id+'-monthLabel' + x);                    
				monthLabel.update(this.monthNames[date.getMonth()] + " " + date.getFullYear());
			}
			date = date.add('mo',1);

			
			if(!this.internalRender){
				main = this.el.dom.firstChild;
				w = main.offsetWidth;
				this.el.setWidth(w + this.el.getBorderWidth("lr"));
				Ext.fly(main).setWidth(w);
				this.internalRender = true;
				// opera does not respect the auto grow header center column
				// then, after it gets a width opera refuses to recalculate
				// without a second pass
//Not needed anymore (tested with opera 9)
/*
				if(Ext.isOpera && !this.secondPass){
					main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
					this.secondPass = true;
					this.update.defer(10, this, [date]);
				}
*/							
			}
		}
		if (this.summarizeHeader && this.noOfMonth > 1) {
			var topHeader = Ext.get(this.id+'-summarize');
			topHeader.update(summarizeHTML.join(""));
		}
		this.el.unmask();
		if (oldStartMonth!=newStartMonth) {
            this.fireEvent("aftermonthchange", this, oldStartMonth, newStartMonth);
		}
		if (oldStartYear!=newStartYear) {
            this.fireEvent("afteryearchange", this, oldStartYear, newStartYear);
		}
	
    },

	beforeDestroy : function() {
		if(this.rendered) {		
			if (this.mbtn) {		
				this.mbtn.destroy();
			}
			if (this.todayBtn) {
				this.todayBtn.destroy();
			}
			if (this.OKBtn){
				this.OKBtn.destroy();
			}
			if (this.undoBtn){
				this.undoBtn.destroy();			
			}
		}
	},


    handleWeekClick : function(e, t){

		e.stopEvent();
		var startweekdate = new Date(t.dateValue).getFirstDateOfWeek(this.startDay), amount=0, startmonth, curmonth,enableUnselect;
		var monthcell = t.monthCell;
		var daycell = t.dayCell;		
		switch(t.parentNode.tagName.toUpperCase()) {
		case "TH":
			amount=42;
			startmonth = t.monthValue;
			break;
		case "TD":
			amount=7;
			break;
		}
		if (!Ext.EventObject.ctrlKey && this.multiSelectByCTRL) {
			this.removeAllPreselectedClasses();
		}
		
		enableUnselect=true;	
		if (this.disablePartialUnselect) {
			var teststartweekdate = startweekdate;
			for (var k=0;k<amount;k++) {
//check, if the whole set is still selected, then make unselection possible again
				curmonth = teststartweekdate.getMonth();		
				if ((amount == 7 || curmonth === startmonth) && this.preSelectedDates.indexOf(teststartweekdate.clearTime().getTime())==-1) {
					enableUnselect=false;
					break;
				}
				teststartweekdate = teststartweekdate.add(Date.DAY,1);
			}
		}

		var reverseAdd =  false;
		var dateAdder = 1;
		if (this.strictRangeSelect &&	(
											(this.preSelectedDates.indexOf(startweekdate.add(Date.DAY,-1).clearTime().getTime())==-1 && !enableUnselect) ||
											(this.preSelectedDates.indexOf(startweekdate.add(Date.DAY,-1).clearTime().getTime())!=-1 && enableUnselect)
										)
			) {
			reverseAdd = true;
			startweekdate = startweekdate.add(Date.DAY,amount-1);
			dateAdder = -1;
		}
		
		this.maxNotified = false;
		for (var i=0,ni;i<amount;i++) {
			curmonth = startweekdate.getMonth();
			ni = (reverseAdd ? amount-1-i : i);
			if (amount == 7 || curmonth === startmonth) {
				this.markDateAsSelected(startweekdate.clearTime().getTime(),true,monthcell,daycell+ni,enableUnselect);
			}
			startweekdate = startweekdate.add(Date.DAY,dateAdder);
		}
		if (amount==42) {
			this.fireEvent("aftermonthclick", this, startmonth,this.lastStateWasSelected);
		}
		else {
			this.fireEvent("afterweekclick", this, new Date(t.dateValue).getFirstDateOfWeek(this.startDay),this.lastStateWasSelected);
		}
	},

	markDateAsSelected : function(t,fakeCTRL,monthcell,daycell,enableUnselect) {
		var currentGetCell = Ext.get(this.cellsArray[monthcell].elements[daycell]);
		if (this.multiSelection && (Ext.EventObject.ctrlKey || fakeCTRL)) {
			var beforeDate = new Date(t).add(Date.DAY,-1).clearTime().getTime();
			var afterDate = new Date(t).add(Date.DAY,1).clearTime().getTime();				
			
			if (this.preSelectedDates.indexOf(t)==-1) {
				if (this.maxSelectionDays === this.preSelectedDates.length) {
					if (!this.maxNotified)  {
				        if(this.fireEvent("beforemaxdays", this) !== false){
							Ext.Msg.alert(this.maxSelectionDaysTitle,this.maxSelectionDaysText.replace(/%0/,this.maxSelectionDays));
						}
						this.maxNotified = true;
					}
					return false;
				}
				if (currentGetCell.hasClass("x-date-disabled")) {
					return false;
				}
				
				if (this.strictRangeSelect && this.preSelectedDates.indexOf(afterDate)==-1 && this.preSelectedDates.indexOf(beforeDate)==-1 && this.preSelectedDates.length > 0) {
					return false;
				}
				
				this.preSelectedDates.push(t);
				this.markSingleDays(monthcell,daycell,false);
				this.markGhostDatesAlso(monthcell,daycell,false);
				this.lastStateWasSelected = true;
			}
			else {
				if (enableUnselect &&	(!this.strictRangeSelect ||
											(this.strictRangeSelect && 
											 	(
													(this.preSelectedDates.indexOf(afterDate)==-1 && this.preSelectedDates.indexOf(beforeDate)!=-1 ) ||
													(this.preSelectedDates.indexOf(afterDate)!=-1 && this.preSelectedDates.indexOf(beforeDate)==-1 )
												)
											)
										)
					){
					this.preSelectedDates.remove(t);
					this.markSingleDays(monthcell,daycell,true);
					this.markGhostDatesAlso(monthcell,daycell,true);
					this.lastStateWasSelected = false;
				}
			}
		}
		else {
//calling update in any case would get too slow on huge multiselect calendars, so set the class for the selected cells manually	 (MUCH faster if not calling update() every time!)
			this.removeAllPreselectedClasses();
			this.preSelectedDates = [t];			
			this.preSelectedCells = [];
			this.markSingleDays(monthcell,daycell,false);
			this.markGhostDatesAlso(monthcell,daycell,false);
			this.lastStateWasSelected = true;
		}
		this.lastSelectedDate = t;
		this.lastSelectedDateCell = monthcell+"#"+daycell;
		if (this.multiSelection && !this.renderOkUndoButtons) {
			this.copyPreToSelectedDays();
		}
		return true;
	},

	markSingleDays : function(monthcell,daycell,remove) {
		if(!remove) {
			Ext.get(this.cellsArray[monthcell].elements[daycell]).addClass("x-date-selected");
			this.preSelectedCells.push((monthcell)+"#"+(daycell));
		}
		else {
			Ext.get(this.cellsArray[monthcell].elements[daycell]).removeClass("x-date-selected");
			this.preSelectedCells.remove((monthcell)+"#"+(daycell));
		}
	},

	markGhostDatesAlso : function(monthcell,daycell,remove) {
		var currentGetCell = Ext.get(this.cellsArray[monthcell].elements[daycell]), dayCellDiff;
		if(currentGetCell.hasClass("x-date-prevday") && monthcell>0) {
			dayCellDiff = (5-Math.floor(daycell/7))*7;
			if(Ext.get(this.cellsArray[monthcell-1].elements[daycell+dayCellDiff]).hasClass("x-date-nextday")) {
				dayCellDiff-=7;
			}
			this.markSingleDays(monthcell-1,daycell+dayCellDiff,remove);
		}
		else if(currentGetCell.hasClass("x-date-nextday") && monthcell<this.cellsArray.length-1) {
			dayCellDiff = 28;
			if(this.cellsArray[monthcell].elements[daycell].firstChild.firstChild.firstChild.innerHTML != this.cellsArray[monthcell+1].elements[daycell-dayCellDiff].firstChild.firstChild.firstChild.innerHTML) {
				dayCellDiff=35;
			}
			this.markSingleDays(monthcell+1,daycell-dayCellDiff,remove);
		}
		else if(currentGetCell.hasClass("x-date-active") && ((daycell < 14 && monthcell>0) || (daycell > 27 && monthcell<this.cellsArray.length-1))){
			if (daycell<14) {
				dayCellDiff = 28;
				if(!Ext.get(this.cellsArray[monthcell-1].elements[daycell+dayCellDiff]).hasClass("x-date-nextday")) {
					dayCellDiff=35;
				}
				if(daycell+dayCellDiff < 42 && this.cellsArray[monthcell].elements[daycell].firstChild.firstChild.firstChild.innerHTML == this.cellsArray[monthcell-1].elements[daycell+dayCellDiff].firstChild.firstChild.firstChild.innerHTML) {
					this.markSingleDays(monthcell-1,daycell+dayCellDiff,remove);
				}
			}
			else {
				dayCellDiff = 28;
				if(!Ext.get(this.cellsArray[monthcell+1].elements[daycell-dayCellDiff]).hasClass("x-date-prevday")) {
					dayCellDiff=35;
				}
				if(daycell-dayCellDiff >= 0 && this.cellsArray[monthcell].elements[daycell].firstChild.firstChild.firstChild.innerHTML == this.cellsArray[monthcell+1].elements[daycell-dayCellDiff].firstChild.firstChild.firstChild.innerHTML) {
					this.markSingleDays(monthcell+1,daycell-dayCellDiff,remove);
				}
			}
		}
	},
	
	
	removeAllPreselectedClasses : function() {
		Ext.each(this.preSelectedCells,function(c){
			var position = c.split("#");
			Ext.get(this.cellsArray[position[0]].elements[position[1]]).removeClass("x-date-selected");

		},this);
		this.preSelectedDates = [];
		this.preSelectedCells = [];
	},

    handleDateClick : function(e, t){
		
		e.stopEvent();
		var tp = Ext.fly(t.parentNode);

        if(t.dateValue && !tp.hasClass("x-date-disabled") && !tp.hasClass("x-datepickerplus-eventdates-disabled")){
		
			var eO = Ext.EventObject;
			if ((!eO.ctrlKey && this.multiSelectByCTRL) || eO.shiftKey || !this.multiSelection) {
				this.removeAllPreselectedClasses();
			}
			var ctrlfaker = (((!eO.ctrlKey && !this.multiSelectByCTRL) || eO.shiftKey) && this.multiSelection ? true:false);


			if (eO.shiftKey && this.multiSelection && this.lastSelectedDate) {
				var startdate = this.lastSelectedDate;
				var targetdate = t.dateValue;
				var dayDiff = (startdate<targetdate? 1:-1);
				var lsdCell = this.lastSelectedDateCell.split("#");
				var tmpMonthCell = parseInt(lsdCell[0],10);
				var tmpDayCell = parseInt(lsdCell[1],10);
				var testCell,ghostCounter=0,ghostplus=0;

				this.maxNotified = false;



//startdate lies in nonvisible month ?
				var firstVisibleDate = this.activeDate.getFirstDateOfMonth().clearTime().getTime();
				var lastVisibleDate = this.activeDate.add(Date.MONTH,this.noOfMonth-1).getLastDateOfMonth().clearTime().getTime();

				if (startdate<firstVisibleDate ||
					startdate>lastVisibleDate) {
			
//prepare for disabledCheck
					var min = this.minDate ? this.minDate.clearTime().getTime() : Number.NEGATIVE_INFINITY;
					var max = this.maxDate ? this.maxDate.clearTime().getTime() : Number.POSITIVE_INFINITY;
					var ddays = this.disabledDays ? this.disabledDays.join("") : "";
					var ddMatch = this.disabledDatesRE;
					var format = this.format;
					var allowedDatesT =  this.allowedDates ? this.allowedDatesT : false;
					var d,ddMatchResult,fvalue;
//check, if the days would be disabled
					while(startdate<firstVisibleDate || startdate>lastVisibleDate) {
						d=new Date(startdate);

						ddMatchResult = false;
						if(ddMatch){
							fvalue = d.dateFormat(format);
							ddMatchResult = ddMatch.test(fvalue);
						}
//don't use >= and <= here for datecomparison, because the dates can differ in timezone
						if(	!(startdate < min) &&
							!(startdate > max) &&
							ddays.indexOf(d.getDay()) == -1 &&
							!ddMatchResult &&
							( !allowedDatesT || allowedDatesT.indexOf(startdate)!=-1 )
						   ) {
//is not disabled and can be processed

							if (this.maxSelectionDays === this.preSelectedDates.length) {
						        if(this.fireEvent("beforemaxdays", this) !== false){								
									Ext.Msg.alert(this.maxSelectionDaysTitle,this.maxSelectionDaysText.replace(/%0/,this.maxSelectionDays));
								}
								break;
							}
							this.preSelectedDates.push(startdate);

						}
						startdate = new Date(startdate).add(Date.DAY,dayDiff).clearTime().getTime();
					}
				
					tmpMonthCell = (dayDiff>0 ? 0 : this.cellsArray.length-1);
					tmpDayCell = (dayDiff>0 ? 0 : 41);

//mark left ghostdates aswell
					testCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
					while (testCell.hasClass("x-date-prevday") || testCell.hasClass("x-date-nextday")) {
						testCell.addClass("x-date-selected");
						this.preSelectedCells.push((tmpMonthCell)+"#"+(tmpDayCell));
						tmpDayCell+=dayDiff;
						testCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
					}
				}
				
//mark range of visible dates
				while ((targetdate-startdate)*dayDiff >0 && tmpMonthCell>=0 && tmpMonthCell<this.cellsArray.length) {									
					this.markDateAsSelected(startdate,ctrlfaker,tmpMonthCell,tmpDayCell,true);

//take care of summertime changing (would return different milliseconds)
					startdate = new Date(startdate).add(Date.DAY,dayDiff).clearTime().getTime();
									
					testCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);

					if (testCell.hasClass("x-date-active")) {
						ghostCounter=0;						
					}
					else {
						ghostCounter++;
					}
					tmpDayCell+=dayDiff;
					if (tmpDayCell==42) {
						tmpMonthCell++;
						tmpDayCell=(ghostCounter>=7?14:7);
					}
					else if (tmpDayCell<0) {
						tmpMonthCell--;
						tmpDayCell=34;
						
						testCell = Ext.get(this.cellsArray[tmpMonthCell].elements[tmpDayCell]);
						if (testCell.hasClass("x-date-nextday") || ghostCounter==7) {
							tmpDayCell=27;
						}
					}
				}

			}
			

			this.markDateAsSelected(t.dateValue,ctrlfaker,t.monthCell,t.dayCell,true);
				
			this.finishDateSelection(new Date(t.dateValue));
        }
    },
	
	copyPreToSelectedDays : function() {
		this.selectedDates = [];
		for (var i=0,il=this.preSelectedDates.length;i<il;i++) {
			this.selectedDates.push(new Date(this.preSelectedDates[i]));
		}
	},
	okClicked : function() {
		this.copyPreToSelectedDays();
		if (this.selectedDates.length < this.minSelectionDays){
			if (!this.minNotified)  {
		        if(this.fireEvent("beforemindays", this) !== false){
					Ext.Msg.alert(this.minSelectionDaysTitle,this.minSelectionDaysText.replace(/%0/,this.minSelectionDays));
				}
				this.minNotified = true;
				return;
			}
		}
		this.selectedDates = this.selectedDates.sortDates();
		this.fireEvent("select", this, this.selectedDates);
	},

	spaceKeyPressed: function(e) {
		var ctrlfaker = (((!Ext.EventObject.ctrlKey && !this.multiSelectByCTRL) || Ext.EventObject.shiftKey) && this.multiSelection ? true:false);
		if (this.shiftSpaceSelect == Ext.EventObject.shiftKey && this.showActiveDate) {
			var adCell = this.activeDateCell.split("#");
			var tmpMonthCell = parseInt(adCell[0],10);
			var tmpDayCell = parseInt(adCell[1],10);
			this.markDateAsSelected(this.activeDate.getTime(),ctrlfaker,tmpMonthCell,tmpDayCell,true);
			this.finishDateSelection(this.activeDate);
		}
		else {
			this.selectToday();
		}
	},

	finishDateSelection: function(date) {
        this.setValue(date);		
		if (this.multiSelection) {
			this.fireEvent("afterdateclick", this, date,this.lastStateWasSelected);
		}
		else {
			this.fireEvent("afterdateclick", this, date,this.lastStateWasSelected);				
	        this.fireEvent("select", this, this.value);
		}
	},

    selectToday : function(){
        if(this.todayBtn && !this.todayBtn.disabled){
			var today = new Date().clearTime();
			var todayT = today.getTime();
		//today already visible?
			if (typeof this.todayMonthCell === "number") {
				this.markDateAsSelected(todayT,false,this.todayMonthCell,this.todayDayCell,true);
			}
			else if (this.multiSelection){
				this.update(today);
			}
			this.finishDateSelection(today);
        }		
    },
	
    setValue : function(value){
		if (Ext.isArray(value)) {
			this.selectedDates = [];
			this.preSelectedDates = [];			
			this.setSelectedDates(value,true);
			value = value[0];
		}
        this.value = value.clearTime(true);

        if(this.el && !this.multiSelection && this.noOfMonth==1){
            this.update(this.value);
        }
		
    },
	
/* this is needed to get it displayed in a panel correctly, it is called several times...*/	
	setSize: Ext.emptyFn
	
});
Ext.reg('datepickerplus', Ext.ux.DatePickerPlus);  


/*
To use DatepickerPlus in menus and datefields, DateItem and datefield needs to be rewritten. This way Ext.DateMenu stays original and by supplying new config item usePickerPlus:true will use the datepickerplus insted of the original picker. 
*/
	Ext.menu.DateItem = Ext.ux.DatePickerPlus;
	Ext.override(Ext.menu.DateMenu,{
		initComponent: function(){
			this.on('beforeshow', this.onBeforeShow, this);
			if(this.strict = (Ext.isIE7 && Ext.isStrict)){
				this.on('show', this.onShow, this, {single: true, delay: 20});
			}
			var PickerWidget = (this.initialConfig.usePickerPlus ? Ext.ux.DatePickerPlus : Ext.DatePicker);
			Ext.apply(this, {
				plain: true,
				showSeparator: false,
				items: this.picker = new PickerWidget(Ext.apply({
					internalRender: this.strict || !Ext.isIE,
					ctCls: 'x-menu-date-item'
				}, this.initialConfig))
			});
			Ext.menu.DateMenu.superclass.initComponent.call(this);
			this.relayEvents(this.picker, ["select"]);
			this.on('select', this.menuHide, this);
			if(this.handler){
				this.on('select', this.handler, this.scope || this);
			}
		}
	});

	if (Ext.form && Ext.form.DateField) {
		Ext.ux.form.DateFieldPlus = Ext.extend(Ext.form.DateField, {
			usePickerPlus: true,
			showWeekNumber: true,
			eventDates: [],
			noOfMonth : 1,
			noOfMonthPerRow : 3,
			nationalHolidaysCls: 'x-datepickerplus-nationalholidays',
			markNationalHolidays:true,
			eventDates: function(year) {
				return [];
			},
			eventDatesRE : false,
			eventDatesRECls : '',
			eventDatesREText : '',
			multiSelection: false,
			multiSelectionDelimiter: ',',			
			multiSelectByCTRL: true,	
			fillupRows: true,
			markWeekends:true,
			weekendText:'',
			weekendCls: 'x-datepickerplus-weekends',
			weekendDays: [6,0],
			useQuickTips: true,
			pageKeyWarp: 1,
			maxSelectionDays: false,
			minSelectionDays: false,
			resizable: false,
			renderTodayButton: true,
			renderOkUndoButtons: true,
			tooltipType: 'qtip',
			allowedDates : false,
			allowedDatesText : '',
			renderPrevNextButtons: true,
			renderPrevNextYearButtons: false,
			disableMonthPicker:false,
			showActiveDate: false,
			shiftSpaceSelect: true,
			disabledLetter: false,
			allowMouseWheel:  true,
			summarizeHeader: false,
			stayInAllowedRange: true,
			disableSingleDateSelection: false,
			eventDatesSelectable: false,
			styleDisabledDates: false,

			allowOtherMenus: false,

			onBeforeYearChange : function(picker, oldStartYear, newStartYear){
				this.fireEvent("beforeyearchange", this, oldStartYear, newStartYear, picker);
			},
			
			onAfterYearChange : function(picker, oldStartYear, newStartYear){
				this.fireEvent("afteryearchange", this, oldStartYear, newStartYear, picker);
			},
			
			onBeforeMonthChange : function(picker, oldStartMonth, newStartMonth){
				this.fireEvent("beforemonthchange", this, oldStartMonth, newStartMonth, picker);
			},
			
			onAfterMonthChange : function(picker, oldStartMonth, newStartMonth){
				this.fireEvent("aftermonthchange", this, oldStartMonth, newStartMonth, picker);
			},
			
			onAfterMonthClick : function(picker, month, wasSelected){
				this.fireEvent("aftermonthclick", this, month, wasSelected, picker);
			},
			
			onAfterWeekClick : function(picker, startOfWeek, wasSelected){
				this.fireEvent("afterweekclick", this, startOfWeek, wasSelected, picker);
			},

			onAfterDateClick : function(picker, date, wasSelected){
				this.fireEvent("afterdateclick", this, date, wasSelected, picker);
			},
			
			onBeforeMouseWheel : function(picker, event){
				this.fireEvent("beforemousewheel", this, event, picker);
			},
			
			onBeforeMaxDays : function(picker){
				this.fireEvent("beforemaxdays", this, picker);
			},
			
			onUndo : function(picker, preSelectedDates){
				this.fireEvent("undo", this, preSelectedDates, picker);
			},
				
			onTriggerClick : function(){
				if(this.disabled){
					return;
				}
				if(!this.menu){
					this.menu = new Ext.menu.DateMenu({
						allowOtherMenus: this.allowOtherMenus,
//is needed at initialisation		
						usePickerPlus:this.usePickerPlus,
						noOfMonth:this.noOfMonth,
						noOfMonthPerRow:this.noOfMonthPerRow,

						listeners: {
							'beforeyearchange': {fn:this.onBeforeYearChange,scope:this},
							'afteryearchange': {fn:this.onAfterYearChange,scope:this},
							'beforemonthchange': {fn:this.onBeforeMonthChange,scope:this},
							'aftermonthchange': {fn:this.onAfterMonthChange,scope:this},
							'afterdateclick': {fn:this.onAfterDateClick,scope:this},
							'aftermonthclick': {fn:this.onAfterMonthClick,scope:this},
							'afterweekclick': {fn:this.onAfterWeekClick,scope:this},
							'beforemousewheel': {fn:this.onBeforeMouseWheel,scope:this},
							'beforemaxdays': {fn:this.onBeforeMaxDays,scope:this},							
							'undo': {fn:this.onUndo,scope:this}
						}
					});
					this.relayEvents(this.menu, ["select"]);
				}

				if (this.menu.isVisible()) {
					this.menu.hide();
					return;
				}
				if (this.disabledDatesRE) {
					this.ddMatch = this.disabledDatesRE;
				}
				if(typeof this.minDate == "string"){
					this.minDate = this.parseDate(this.minDate);
				}
				if(typeof this.maxDate == "string"){
					this.maxDate = this.parseDate(this.maxDate);
				}
				
				Ext.apply(this.menu.picker,  {
					minDate : this.minValue || this.minDate,
					maxDate : this.maxValue || this.maxDate,
					disabledDatesRE : this.ddMatch,
					disabledDatesText : this.disabledDatesText,
					disabledDays : this.disabledDays,
					disabledDaysText : this.disabledDaysText,
		            showToday : this.showToday,	//from Ext 2.2
					format : this.format,
					minText : String.format(this.minText, this.formatDate(this.minValue || this.minDate)),
					maxText : String.format(this.maxText, this.formatDate(this.maxValue || this.maxDate)),
					showWeekNumber: this.showWeekNumber,
					nationalHolidaysCls: this.nationalHolidaysCls,
					markNationalHolidays:this.markNationalHolidays,
					multiSelectByCTRL: this.multiSelectByCTRL,	
					fillupRows: this.fillupRows,
					multiSelection: this.multiSelection,
					markWeekends:this.markWeekends,
					weekendText:this.weekendText,
					weekendCls: this.weekendCls,
					weekendDays: this.weekendDays,
					useQuickTips: this.useQuickTips,
					eventDates: this.eventDates,
					eventDatesRE: this.eventDatesRE,
					eventDatesRECls: this.eventDatesRECls,
					eventDatesREText: this.eventDatesREText,
					pageKeyWarp: this.pageKeyWarp,
					maxSelectionDays: this.maxSelectionDays,
					minSelectionDays: this.minSelectionDays,
					resizable: this.resizable,
					renderTodayButton: this.renderTodayButton,
					renderOkUndoButtons: this.renderOkUndoButtons,
					allowedDates : this.allowedDates,
					allowedDatesText : this.allowedDatesText,
					renderPrevNextButtons: this.renderPrevNextButtons,
					renderPrevNextYearButtons: this.renderPrevNextYearButtons,
					disableMonthPicker:this.disableMonthPicker,
					showActiveDate: this.showActiveDate,
					shiftSpaceSelect: this.shiftSpaceSelect,
					disabledLetter: this.disabledLetter,
					allowMouseWheel: this.allowMouseWheel,
					summarizeHeader: this.summarizeHeader,
					stayInAllowedRange: this.stayInAllowedRange,
					disableSingleDateSelection: this.disableSingleDateSelection,
					eventDatesSelectable: this.eventDatesSelectable,
					styleDisabledDates: this.styleDisabledDates
				});
				this.menu.on(Ext.apply({}, this.menuListeners, {
					scope:this
				}));
				//this.relayEvents(this.menu, ["select"]);
				this.menu.picker.setValue(this.getValue() || new Date());
				this.menu.show(this.el, "tl-bl?");
				this.menu.focus();
			},
			
			setValue : function(date){
				var field = this;     
				if (Ext.isArray(date)) {
					var formatted = [];
					
					Ext.each(date, function(item, index, array) {
						if (index == 0 || index == array.length - 1){ 
							formatted.push(field.formatDate(item));
						}
					});
					if (formatted.length == 1){
						formatted.push(field.formatDate(date[date.length-1]));
					}
				
					var value = formatted.join(this.multiSelectionDelimiter);
				
//bypass setValue validation on Ext.DateField
					Ext.form.DateField.superclass.setValue.call(this, value);
				}
				else {
					Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));				   
				}
			},

			validateValue : function(value){
				if (this.multiSelection){
					var field = this;
					var values = value.split(this.multiSelectionDelimiter);
					var isValid = true;
					Ext.each(values, function(item, index, array) {
						if (!Ext.ux.form.DateFieldPlus.superclass.validateValue.call(field, item)) {
							isValid = false;
						}
					});
					return isValid;
				}
				else {
					return Ext.ux.form.DateFieldPlus.superclass.validateValue.call(this, value);
				}         
			},

			getValue : function() {
				if (this.multiSelection) {
					var value = Ext.form.DateField.superclass.getValue.call(this);
					var field = this;					
					var values = value.split(this.multiSelectionDelimiter);
					var dates = [];
					var startDate = field.parseDate(values[0]);
					var endDate;
					if (values.length > 1) {
						endDate = field.parseDate(values[1]);
					}
					else {
						endDate = field.parseDate(values[0]);
					}
					while (startDate<=endDate){
						dates.push(startDate);
						startDate = startDate.add(Date.DAY,1);
					}
					if (dates.length == 1){
						dates.push(endDate);
					}
//					var checkDate;
//					Ext.each(values, function(item, index, array) {
//						var checkDate = field.parseDate(item);
//						if (checkDate) {
//							dates.push(checkDate);
//						}
//					});
					return (dates.length>0?dates:"");
				}
				else {
					return Ext.ux.form.DateFieldPlus.superclass.getValue.call(this);
				}
			},			


			beforeBlur : function(){
				if (this.multiSelection) {
					this.setValue(this.getRawValue().split(this.multiSelectionDelimiter));
				}
				else {
					var v = this.parseDate(this.getRawValue());
					if(v){
						this.setValue(v);
					}
				}
			},


			
			submitFormat:'Y-m-d',
			submitFormatAddon: '-format',			
			onRender:function() {
		
				Ext.ux.form.DateFieldPlus.superclass.onRender.apply(this, arguments);
//be sure not to have duplicate formfield names (at least IE moans about it and gets confused)				
//				this.name =  (typeof this.name==="undefined"?this.id+this.submitFormatAddon:(this.name==this.id?this.name+this.submitFormatAddon:this.name));		
				var name =  this.name || this.el.dom.name || (this.id+this.submitFormatAddon);
				if (name==this.id) {
					name+= this.submitFormatAddon;
				}
				this.hiddenField = this.el.insertSibling({
					tag:'input',
					type:'hidden',
					name: name,
					value:this.formatHiddenDate(this.parseDate(this.value))
				});
				this.hiddenName = name;
				this.el.dom.removeAttribute('name');
				this.el.on({
					 keyup:{scope:this, fn:this.updateHidden}
					,blur:{scope:this, fn:this.updateHidden}
				});
		
				this.setValue = this.setValue.createSequence(this.updateHidden);
				
				if(this.tooltip){
					if(typeof this.tooltip == 'object'){
						Ext.QuickTips.register(Ext.apply({
							  target: this.trigger
						}, this.tooltip));
					} else {
						this.trigger.dom[this.tooltipType] = this.tooltip;
					}
				}
				
		
			},
			onDisable: function(){
				Ext.ux.form.DateFieldPlus.superclass.onDisable.apply(this, arguments);
				if(this.hiddenField) {
					this.hiddenField.dom.setAttribute('disabled','disabled');
				}
			},
			
			onEnable: function(){
				Ext.ux.form.DateFieldPlus.superclass.onEnable.apply(this, arguments);
				if(this.hiddenField) {
					this.hiddenField.dom.removeAttribute('disabled');
				}
			},
			
			formatHiddenDate : function(date){
				return Ext.isDate(date) ? Ext.util.Format.date(date, this.submitFormat) : date;
			},
			
			updateHidden:function(date) {
				var field = this;     
				if (Ext.isArray(date)) {
					var formatted = [];
					Ext.each(date, function(item, index, array) {
						formatted.push(field.formatHiddenDate(item));
					});
				
					var value = formatted.join(this.multiSelectionDelimiter);
					this.hiddenField.dom.value = value;
				}
				else {
					this.hiddenField.dom.value = this.formatHiddenDate(this.getValue());
				}
			}

		});
		Ext.reg('datefieldplus', Ext.ux.form.DateFieldPlus);
	}


/*
 * Ext JS Library 2.2.1
 * Copyright(c) 2006-2009, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://extjs.com/license
 */

Ext.ux.Portal = Ext.extend(Ext.Panel, {
    layout: 'column',
    autoScroll:true,
    cls:'x-portal',
    defaultType: 'portalcolumn',
    
    initComponent : function(){
        Ext.ux.Portal.superclass.initComponent.call(this);
        this.addEvents({
            validatedrop:true,
            beforedragover:true,
            dragover:true,
            beforedrop:true,
            drop:true
        });
    },

    initEvents : function(){
        Ext.ux.Portal.superclass.initEvents.call(this);
        this.dd = new Ext.ux.Portal.DropZone(this, this.dropConfig);
    },
    
    beforeDestroy: function() {
        if(this.dd){
            this.dd.unreg();
        }
        Ext.ux.Portal.superclass.beforeDestroy.call(this);
    }
});
Ext.reg('portal', Ext.ux.Portal);


Ext.ux.Portal.DropZone = function(portal, cfg){
    this.portal = portal;
    Ext.dd.ScrollManager.register(portal.body);
    Ext.ux.Portal.DropZone.superclass.constructor.call(this, portal.bwrap.dom, cfg);
    portal.body.ddScrollConfig = this.ddScrollConfig;
};

Ext.extend(Ext.ux.Portal.DropZone, Ext.dd.DropTarget, {
    ddScrollConfig : {
        vthresh: 50,
        hthresh: -1,
        animate: true,
        increment: 200
    },

    createEvent : function(dd, e, data, col, c, pos){
        return {
            portal: this.portal,
            panel: data.panel,
            columnIndex: col,
            column: c,
            position: pos,
            data: data,
            source: dd,
            rawEvent: e,
            status: this.dropAllowed
        };
    },

    notifyOver : function(dd, e, data){
        var xy = e.getXY(), portal = this.portal, px = dd.proxy;

        // case column widths
        if(!this.grid){
            this.grid = this.getGrid();
        }

        // handle case scroll where scrollbars appear during drag
        var cw = portal.body.dom.clientWidth;
        if(!this.lastCW){
            this.lastCW = cw;
        }else if(this.lastCW != cw){
            this.lastCW = cw;
            portal.doLayout();
            this.grid = this.getGrid();
        }

        // determine column
        var col = 0, xs = this.grid.columnX, cmatch = false;
        for(var len = xs.length; col < len; col++){
            if(xy[0] < (xs[col].x + xs[col].w)){
                cmatch = true;
                break;
            }
        }
        // no match, fix last index
        if(!cmatch){
            col--;
        }

        // find insert position
        var p, match = false, pos = 0,
            c = portal.items.itemAt(col),
            items = c.items.items, overSelf = false;

        for(var len = items.length; pos < len; pos++){
            p = items[pos];
            var h = p.el.getHeight();
            if(h === 0){
                overSelf = true;
            }
            else if((p.el.getY()+(h/2)) > xy[1]){
                match = true;
                break;
            }
        }

        pos = (match && p ? pos : c.items.getCount()) + (overSelf ? -1 : 0);
        var overEvent = this.createEvent(dd, e, data, col, c, pos);

        if(portal.fireEvent('validatedrop', overEvent) !== false &&
           portal.fireEvent('beforedragover', overEvent) !== false){

            // make sure proxy width is fluid
            px.getProxy().setWidth('auto');

            if(p){
                px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null);
            }else{
                px.moveProxy(c.el.dom, null);
            }

            this.lastPos = {c: c, col: col, p: overSelf || (match && p) ? pos : false};
            this.scrollPos = portal.body.getScroll();

            portal.fireEvent('dragover', overEvent);

            return overEvent.status;
        }else{
            return overEvent.status;
        }

    },

    notifyOut : function(){
        delete this.grid;
    },

    notifyDrop : function(dd, e, data){
        delete this.grid;
        if(!this.lastPos){
            return;
        }
        var c = this.lastPos.c, col = this.lastPos.col, pos = this.lastPos.p;

        var dropEvent = this.createEvent(dd, e, data, col, c,
            pos !== false ? pos : c.items.getCount());

        if(this.portal.fireEvent('validatedrop', dropEvent) !== false &&
           this.portal.fireEvent('beforedrop', dropEvent) !== false){

            dd.proxy.getProxy().remove();
            dd.panel.el.dom.parentNode.removeChild(dd.panel.el.dom);
            
            if(pos !== false){
                if(c == dd.panel.ownerCt && (c.items.items.indexOf(dd.panel) <= pos)){
                    pos++;
                }
                c.insert(pos, dd.panel);
            }else{
                c.add(dd.panel);
            }
            
            c.doLayout();

            this.portal.fireEvent('drop', dropEvent);

            // scroll position is lost on drop, fix it
            var st = this.scrollPos.top;
            if(st){
                var d = this.portal.body.dom;
                setTimeout(function(){
                    d.scrollTop = st;
                }, 10);
            }

        }
        delete this.lastPos;
    },

    // internal cache of body and column coords
    getGrid : function(){
        var box = this.portal.bwrap.getBox();
        box.columnX = [];
        this.portal.items.each(function(c){
             box.columnX.push({x: c.el.getX(), w: c.el.getWidth()});
        });
        return box;
    },

    // unregister the dropzone from ScrollManager
    unreg: function() {
        //Ext.dd.ScrollManager.unregister(this.portal.body);
        Ext.ux.Portal.DropZone.superclass.unreg.call(this);
    }
});

/*
 * Ext JS Library 2.2.1
 * Copyright(c) 2006-2009, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://extjs.com/license
 */

Ext.ux.PortalColumn = Ext.extend(Ext.Container, {
    layout: 'anchor',
    autoEl: 'div',
    defaultType: 'portlet',
    cls:'x-portal-column'
});
Ext.reg('portalcolumn', Ext.ux.PortalColumn);
/*
 * Ext JS Library 2.2.1
 * Copyright(c) 2006-2009, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://extjs.com/license
 */

Ext.ux.Portlet = Ext.extend(Ext.Panel, {
    anchor: '100%',
    frame:true,
    collapsible:true,
    draggable:true,
    cls:'x-portlet'
});
Ext.reg('portlet', Ext.ux.Portlet);
/**
 * @author nick_brown
 */
 Ext.ux.AnalyticsOptionsWindow=function(){
	this.doOK=function(){
		if(this.optionsForm.getForm().isValid()){
			this.password.suspendEvents();
			this.originalPassword = this.password.getValue();
			this.password.setValue(Ext.ux.base64.encode(this.originalPassword));
			this.password.resumeEvents();
			var stateManager = Ext.state.Manager; 
			stateManager.set('AnalyticsEMail', this.username.getValue());
			stateManager.set('AnalyticsPassword', this.password.getValue());
			this.close();
		}
	};
	this.doClose=function(){
		this.close();
	};
	this.username=new Ext.form.TextField({
		id: 'textfieldAnalyticsUsername',
		msgTarget:'side',
		fieldLabel:'Email',
		name:'email',
		width:175,
		allowBlank:true,
		value: Ext.state.Manager.get('AnalyticsEMail', ''),
		invalidText:'Please enter your Email!'
	});
	this.password= new Ext.form.TextField({
		id: 'textfieldAnalyticsPassword',
		msgTarget:'side',
		fieldLabel:'Password',
		allowBlank:false,
		name:'pass',
		width:175,
		inputType:'password'
	});
	this.password.on('specialkey', function (textfield, e){
        if (e.getKey() == 13){
            this.doOK();
        }
    }, this);
	this.optionsForm=new Ext.FormPanel({
		id: 'formAnalyticsOptionsForm',
        labelWidth: 75, // label settings here cascade unless overridden
        //url:'login.php',
        defaults: {width: 230},
		baseCls: 'x-plain',
        defaultType: 'textfield',
        labelAlign: 'left',
		bodyStyle: {
			padding: '10px'	
		},
		border: false,
		items:[
			this.username,
			this.password
		]
	});
	Ext.ux.AnalyticsOptionsWindow.superclass.constructor.call(this, {
		id: 'windowAnalyticsOptions',
		width:400,
		autoHeight:true,
		modal:true,
		shadow:true,
		bodyBorder: false,
		plain:true,
		collapsible:false,
		resizable:false,
		closable:true,
		title:'Settings',
		iconCls: 'preferences-icon-cls',
		plugins:[new Ext.ux.IconMenu({dblClickClose:false})],
		buttons:[
			{ 
				text: 'Save',
				handler: this.doOK,
				scope: this
			},
			{
				text: 'Close',
				handler: this.doClose,
				scope: this
			}
		],
		items: [this.optionsForm]
	});
};
Ext.extend(Ext.ux.AnalyticsOptionsWindow, Ext.Window, {
	show:function(){
		Ext.ux.AnalyticsOptionsWindow.superclass.show.call(this);
		this.optionsForm.getForm().reset();
	}
});
/**
 * @author nick_brown
 */

 
Ext.ux.AnalyticsAccordionPanel = function(){
	this.addEvents({
		'selectaccount': true
	});
	this.store = new Ext.data.JsonStore({
		autoLoad: true,
		proxy: new Ext.data.HttpProxy({
			url: 'analytics.php',	
			disableCaching:true,
			method: 'GET'
		}),
        baseParams: {email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ==')},
        method: 'GET',
        root: 'accounts',
        id:'profileId',
        fields:[
            'tableId', 'accountId', 'accountName', 'profileId', 'webPropertyId'
        ]
    });
    this.comboAccounts = new Ext.form.ComboBox({
    	id: 'comboboxAccounts',
    	displayField: 'accountName',
    	editable: false,
    	forceSelection: true,
    	hideLabel: true,
    	minListWidth: 150,
    	store: this.store,
    	triggerAction: 'all',
    	mode: 'local',
    	valueField: 'profileId',
    	value: Ext.state.Manager.get('AnalyticsAccountName','')
    });
    this.comboAccounts.on('select', function(combo,rec,idx){
    	Ext.state.Manager.set('AnalyticsAccountId',this.comboAccounts.getValue());
    	Ext.state.Manager.set('AnalyticsAccountName',rec.get('accountName'));
    	this.fireEvent('selectaccount', this);
    }, this)
	this.tbar = new Ext.Toolbar({
		id: 'toolbarTopAnalyticsAccordionPanel',
		items: [
			{
				id: 'tbbuttonAnalyticsOptions',
	            icon: 'images/Preferences16.png', // icons can also be specified inline
		        cls: 'x-btn-text-icon',
		        text: 'Settings',
				disabled: false,
	        	tooltip: '<b>Settings</b><br/>Setting for Google Analytics',
				enableToggle: false,
				pressed: false,
				handler: function() {
					var optionsWindow = new Ext.ux.AnalyticsOptionsWindow();
					optionsWindow.show('tbbuttonAnalyticsOptions');
				},
				scope: this
	        }
	]});
	Ext.ux.AnalyticsAccordionPanel.superclass.constructor.call(this, {
		id: 'panelAnalyticsAccordionPanel',
		title: 'Analytics',
		iconCls: 'analytics-icon-cls',
		autoShow: true,
		//hidden: true,
		hideMode: 'visibility',
		bodyStyle:'background-color: white',
		tbar: this.tbar,
		region: 'west',
		width: 200,
		minSize: 150,
		maxSize: 300,
		split: true,
		frame: false,
		collapsible: false,
		items: [{
			xtype: 'panel',
			title: 'Accounts',
			collapsible: false,
			autoHeight: true,
			border:true,
			layout: 'form',
			frame: true,
			bodyStyle: 'background-color:#ffffff;padding:5px',
			items:[
				this.comboAccounts 
			]
		},
		{ // raw
			xtype: 'panel',
			id: 'panelAds',
			contentEl: 'sponsoredlinks',
			border: true,
			frame: true,
			bodyStyle:'padding:5px',
			collapsible: false,
			title: 'Sponsored Links',
			margins:'0 0 0 0',
			height: 650
        }]
	});
};
Ext.extend(Ext.ux.AnalyticsAccordionPanel, Ext.Panel);
/**
 * @author nick_brown
*/

Ext.ux.MapOverlayPanel = Ext.extend(Ext.ux.Portlet, {
    data: null,

    // Overriden methods
    initComponent: function() {
    	Ext.Ajax.request({
			url: 'map_chart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.startDate.format('Y-m-d'),
				enddate: this.endDate.format('Y-m-d')
			},
			method: 'GET',
			success: function(response, options){
				var responseData = Ext.util.JSON.decode(response.responseText);
				if (responseData.success){
					this.data=responseData.data;
					google.load(
			            "visualization", 
						"1",
			            {
			                packages: ["geomap"],
			                callback: this.onLoadCallback.createDelegate(this)
			            }
			        );
				}
				else{
 			 		 Ext.Msg.alert('Error','Unable to mark messages as Read');
    			 }
			},
            failure: function(response, options){
               	Ext.Msg.alert('Error','Unable to mark messages as Read');
		    },
		    scope: this
		});
        
        Ext.ux.MapOverlayPanel.superclass.initComponent.call(this);
//        this.on('expand',function(){
//        	this.update();
//        }, this);
    },
//    afterRender : function(){
//        Ext.ux.VisitsPanel.superclass.afterRender.call(this); // do sizing calcs last
//        
//        this.options = {width: this.getInnerWidth(), height: 120, legend:'none'};
//        this.visualization.draw(this.datatable, this.options);
//    },
    // custom methods
    onLoadCallback: function() {
    	this.gData = new google.visualization.DataTable();
      	this.gData.addRows(this.data.length);
      	this.gData.addColumn('string', 'Country');
      	this.gData.addColumn('number', 'Visits');
	    var i=0;
	    for (i=0;i<this.data.length;i++){
	    	this.gData.setValue(i, 0, this.data[i].Country);
	        this.gData.setValue(i, 1, this.data[i].visits);
	    }
	    this.options = {};
	    this.options['dataMode'] = 'regions';
	    this.options['showLegend'] = this.showLegend;
	    this.options['width'] = '100%';
	    this.options['height'] = this.chartHeight;
	
	      //var container = document.getElementById('my_chart');
	    this.geomap = new google.visualization.GeoMap(this.body.dom);
	    this.geomap.draw(this.gData, this.options);
    },
	
	update: function(startDate, endDate) {
		this.startDate = startDate;
		this.endDate = endDate;
		Ext.Ajax.request({
			url: 'map_chart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.startDate.format('Y-m-d'),
				enddate: this.endDate.format('Y-m-d')
			},
			method: 'GET',
			success: function(response, options){
				var responseData = Ext.util.JSON.decode(response.responseText);
				if (responseData.success){
					this.data=responseData.data;
					this.gData = new google.visualization.DataTable();
			      	this.gData.addRows(this.data.length);
			      	this.gData.addColumn('string', 'Country');
			      	this.gData.addColumn('number', 'Visits');
				    var i=0;
				    for (i=0;i<this.data.length;i++){
				    	this.gData.setValue(i, 0, this.data[i].Country);
				        this.gData.setValue(i, 1, this.data[i].visits);
				    }
				    this.geomap.draw(this.gData, this.options);
				}
				else{
 			 		 Ext.Msg.alert('Error','Unable to mark messages as Read');
    			 }
			},
            failure: function(response, options){
               	Ext.Msg.alert('Error','Unable to mark messages as Read');
		    },
		    scope: this
		});
	}    
});
Ext.reg('mapoverlaypanel', Ext.ux.MapOverlayPanel);
/**
 * @author nick_brown
 */
 Ext.ux.NewVisitorsGridPanel = function(config){
 	this.config = config || {};
 	this.Store = new Ext.data.JsonStore({
		autoLoad: {params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}},
		url: 'newvisits_reportdata.php',
		root: 'data',
		fields: ['VisitorCount', 'VisitorType', 'Visits', 'Visits_percent'],
		method: 'GET'
	});
	this.SM = new Ext.grid.RowSelectionModel({singleSelect: true});
	this.SM.on('beforerowselect', function(){return false}, this);
	this.CM = new Ext.grid.ColumnModel([
		{header: '', dataIndex: 'VisitorCount', width: 25, sortable: false},
		{id: 'visitortype', header: 'Visitor Type', dataIndex: 'VisitorType', width: 400, sortable: false},
		{header: 'Visits', dataIndex: 'Visits', width: 100, sortable: false, align: 'right'},
		{header: '% Visits', dataIndex: 'Visits_percent', width: 100, sortable: false, align: 'right'}
	]);
	Ext.ux.NewVisitorsGridPanel.superclass.constructor.call(this,{
		id: 'gridNewVists',
		//deferredRender: true,
		monitorResize: true,
		store: this.Store,
		cm: this.CM,
		view: new Ext.grid.GridView({
			autoFill: true,
	        forceFit: false,
			emptyText: 'There is no content to display'
	    }),
		sm: this.SM,
		frame:true,
		collapsible : true,
		height: 230,
		split: false,
		frame:true,
		border:true,
		//margins: '5 5 5 5',
		floatable: false,
		loadMask: {msg: 'Loading Chart....'},
		cls: 'x-analytics-panel',
		//title: 'New Visits',
		//bbar: this.contentToolbar,
		autoExpandColumn: 'visitortype',
		closable:true
		//stripeRows: true
	});
};
Ext.extend(Ext.ux.NewVisitorsGridPanel, Ext.grid.GridPanel, {
	updateGrid: function(startDate, endDate){
		this.config.startDate = startDate;
		this.config.endDate = endDate;
		this.Store.load({params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}});
	}
});
/**
 * @author nick_brown
 */
 Ext.ux.NewVisitorsReportPanel = function(config){
 	this.config = config || {};
    this.newvisitsReady = false;
 	this.pieReady = false;
 	this.pdfReady = false;
 	this.newVisitorsChart = new Ext.ux.ManagedIFramePanel({
		autoLoad: {
			url: 'newvisits_reportchart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelNewVisitsChartPanel',
		height: 230,
		split: false,
		border:true,
		frame: true,
		margins: '5 5 5 5',
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		frameConfig: {
			disableMessaging: false
		},
		cls: 'x-analytics-panel',
		region: 'north'
	});
    this.newVisitorsChart.on('message', function(srcFrame, message){
		if (message.data=='ready'){
			this.newvisitsReady = true;
			if (this.pieReady) {
				this.createPDF();
			}
		}
	}, this);
	this.newVisitorsGrid = new Ext.ux.NewVisitorsGridPanel(this.config);
	this.newVisitorsPieChart = new Ext.ux.ManagedIFramePanel({
		autoLoad: {
			url: 'newvisits_reportpiechart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelNewVisitsBarChartPanel',
		height: 230,
		split: false,
		border:true,
		frame:true,
		margins: '5 5 5 5',
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		frameConfig: {
			disableMessaging: false
		},
		cls: 'x-analytics-panel'
	});
    this.newVisitorsPieChart.on('message', function(srcFrame, message){
		if (message.data=='ready'){
			this.pieReady = true;
			if (this.newvisitsReady){
				this.createPDF();
			}
		}
	}, this);
 	Ext.ux.NewVisitorsReportPanel.superclass.constructor.call(this,{
        id:'panelNewVisitsReportPanel',
        title:'New Visitors Report',
        margins: '0 0 0 0',
        layout:'border',
        autoScroll: true,
        autoShow: true,
        closable: true,
		iconCls: 'analytics-icon-cls',
		bodyStyle: 'background-color:transparent',
		border: true,
		closeable: true,
        tbar: new Ext.Toolbar({
			id: 'toolbarNewVisitsReportPanel',
			items: [
		        {
					id: 'tbbuttonNewVisitsReportExportReport',
		            icon: 'images/report_disk.png', // icons can also be specified inline
                    cls: 'x-btn-text-icon',
                    text: 'Export Report',
                    disabled: true,
                    tooltip: '<b>Export Report</b><br/>Export the report',
                    enableToggle: false,
                    pressed: false,
                    menu: new Ext.menu.Menu({
                        id: 'menuNewVisitsReportExportMenu',
                        items: [
                            {
                                id: 'menuitemNewVisitsReportExportReportPDF',
                                icon: 'images/page_white_acrobat.png', // icons can also be specified inline
                                text: 'Export Report to PDF',
                                disabled: false,
                                tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
                                handler: function() {
                                    window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/NewVisitsReport_' + new Date().format('dmY') + '.pdf'}), '_self');
                                },
                                scope: this
                            },
                            {
                                id: 'menuitemNewVisitsReportExportXML',
                                icon: 'images/ExportXml16.png', // icons can also be specified inline
                                text: 'Export Report to XML',
                                disabled: false,
                                tooltip: '<b>Export Report</b><br/>Export the report as a XML Document',
                                handler: function() {
                                    window.open('download.php?' + Ext.urlEncode({file:'tmp_xml/NewVisitsReport_' + new Date().format('dmY') + '.xml'}), '_self');
                                },
                                scope: this
                            },
                            {
                                id: 'menuitemNewVisitsReportExportCSV',
                                icon: 'images/page_white_excel.png', // icons can also be specified inline
                                text: 'Export Report to CSV',
                                disabled: false,
                                tooltip: '<b>Export Report</b><br/>Export the report as a CSV Document',
                                handler: function() {
                                    window.open('download.php?' + Ext.urlEncode({file:'tmp_csv/NewVisitsReport_' + new Date().format('dmY') + '.csv'}), '_self');
                                },
                                scope: this
                            },
                            {
                                id: 'menuitemNewVisitsReportExportTSV',
                                icon: 'images/page_white_go.png', // icons can also be specified inline
                                text: 'Export Report to TSV',
                                disabled: false,
                                tooltip: '<b>Export Report</b><br/>Export the report as a TSV Document',
                                handler: function() {
                                    window.open('download.php?' + Ext.urlEncode({file:'tmp_tsv/NewVisitsReport_' + new Date().format('dmY') + '.tsv'}), '_self');
                                },
                                scope: this
                            },
                            {
                                id: 'menuitemNewVisitsReportExportJSON',
                                icon: 'images/json_file.png', // icons can also be specified inline
                                text: 'Export Report to JSON',
                                disabled: false,
                                tooltip: '<b>Export Report</b><br/>Export the report as a JSON Document',
                                handler: function() {
                                    window.open('download.php?' + Ext.urlEncode({file:'tmp_json/NewVisitsReport_' + new Date().format('dmY') + '.json'}), '_self');
                                },
                                scope: this
                            }
                        ]
                    }),               
                    scope: this
		        }
		    ]
		}),
		items: [
			this.newVisitorsChart,
			new Ext.Panel({
				id: 'panelNewVisitorsOthers',
				region: 'center',
				autoScroll: true,
				layout: 'column',
				defaults: {
			        // applied to each contained panel
			        bodyStyle:'padding:5px'
			    },
	    	    layoutConfig: {
			        // The total column count must be specified here
			        columns: 2
			    },
				border:false,
				items: [
					{ 	columnWidth : 0.5,
						border: false,
						items : [
							this.newVisitorsGrid
						]
					},
					{ 	columnWidth : 0.5,
						border: false,
						items : [
							this.newVisitorsPieChart
						]
					}
				]
			})
		]
    });
 };
 Ext.extend(Ext.ux.NewVisitorsReportPanel, Ext.Panel, {
 	updateCharts: function (startDate, endDate){
        this.getTopToolbar().items.get('tbbuttonNewVisitsReportExportReport').disable();
        this.newvisitsReady = false;
        this.pieReady = false;
        this.pdfReady = false;
 		this.config.startDate = startDate;
 		this.config.endDate = endDate;
 		this.newVisitorsChart.load({
			url: 'newvisits_reportchart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});
		this.newVisitorsPieChart.load({
			url: 'newvisits_reportpiechart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});
		this.newVisitorsGrid.updateGrid(startDate, endDate);
 	},
 	createPDF: function(){
		if (!this.pdfReady){
			Ext.Ajax.request({
				url: 'newvisits_reportchartpdf.php',
				params: {
					email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
					passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
					id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
					startdate: this.config.startDate.format('Y-m-d'),
					enddate: this.config.endDate.format('Y-m-d')
				},
				method: 'GET',
				success: function(response, options){
					var responseData = Ext.util.JSON.decode(response.responseText);
					if (responseData.success){
						this.getTopToolbar().items.get('tbbuttonNewVisitsReportExportReport').enable();
						this.pdfReady = true;
					}
				},
	            failure: function(response, options){
	               	Ext.Msg.alert('Error','Unable to mark messages as Read');
			    },
			    scope: this
			});
		}
	} 		
 });
/**
 * @author nick_brown
 */
 Ext.ux.VisitorsBrowserGridPanel = function(config){
 	this.config = config || {};
 	this.Store = new Ext.data.JsonStore({
		autoLoad: {params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}},
		proxy: new Ext.data.HttpProxy({
			url: 'visitors_reportdata.php',	
			disableCaching:true,
			method: 'GET'
		}),
		root: 'data',
		fields: ['Browser', 'Visits', 'Visits_percent'],
		method: 'GET'
	});
	this.SM = new Ext.grid.RowSelectionModel({singleSelect: true});
	this.SM.on('beforerowselect', function(){return false}, this);
	this.CM = new Ext.grid.ColumnModel([
		{id: 'browser', header: 'Browser', dataIndex: 'Browser', width: 400, sortable: false},
		{header: 'Visits', dataIndex: 'Visits', width: 100, sortable: false, align: 'right'},
		{header: '% Visits', dataIndex: 'Visits_percent', width: 100, sortable: false, align: 'right'}
	]);
	Ext.ux.VisitorsBrowserGridPanel.superclass.constructor.call(this,{
		id: 'gridVistorsBrowser',
		//deferredRender: true,
		monitorResize: true,
		store: this.Store,
		cm: this.CM,
		view: new Ext.grid.GridView({
			autoFill: true,
	        forceFit: false,
			emptyText: 'There is no content to display'
	    }),
		sm: this.SM,
		frame:true,
		collapsible : true,
		height: 230,
		split: false,
		frame:true,
		border:true,
		//margins: '5 5 5 5',
		floatable: false,
		loadMask: {msg: 'Loading Chart....'},
		cls: 'x-analytics-panel',
		//title: 'New Visits',
		//bbar: this.contentToolbar,
		autoExpandColumn: 'browser',
		closable:true
		//stripeRows: true
	});
};
Ext.extend(Ext.ux.VisitorsBrowserGridPanel, Ext.grid.GridPanel, {
	updateGrid: function(startDate, endDate){
		this.config.startDate = startDate;
		this.config.endDate = endDate;
		this.Store.load({params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}});
	}
});
/**
 * @author nick_brown
 */
 Ext.ux.VisitorsConnectionSpeedGridPanel = function(config){
 	this.config = config || {};
 	this.Store = new Ext.data.JsonStore({
		autoLoad: {params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}},
		url: 'visitors_reportdata1.php',
		root: 'data',
		fields: ['ConnectionSpeed', 'Visits', 'Visits_percent'],
		method: 'GET'
	});
	this.SM = new Ext.grid.RowSelectionModel({singleSelect: true});
	this.SM.on('beforerowselect', function(){return false}, this);
	this.CM = new Ext.grid.ColumnModel([
		{id: 'connectionspeed', header: 'Connection Speed', dataIndex: 'ConnectionSpeed', width: 400, sortable: false},
		{header: 'Visits', dataIndex: 'Visits', width: 100, sortable: false, align: 'right'},
		{header: '% Visits', dataIndex: 'Visits_percent', width: 100, sortable: false, align: 'right'}
	]);
	Ext.ux.VisitorsConnectionSpeedGridPanel.superclass.constructor.call(this,{
		id: 'gridVistorsConnectionSpeed',
		//deferredRender: true,
		monitorResize: true,
		store: this.Store,
		cm: this.CM,
		view: new Ext.grid.GridView({
			autoFill: true,
	        forceFit: false,
			emptyText: 'There is no content to display'
	    }),
		sm: this.SM,
		frame:true,
		collapsible : true,
		height: 230,
		split: false,
		frame:true,
		border:true,
		//margins: '5 5 5 5',
		floatable: false,
		loadMask: {msg: 'Loading Chart....'},
		cls: 'x-analytics-panel',
		//title: 'New Visits',
		//bbar: this.contentToolbar,
		autoExpandColumn: 'connectionspeed',
		closable:true
		//stripeRows: true
	});
};
Ext.extend(Ext.ux.VisitorsConnectionSpeedGridPanel, Ext.grid.GridPanel, {
	updateGrid: function(startDate, endDate){
		this.config.startDate = startDate;
		this.config.endDate = endDate;
		this.Store.load({params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}});
	}
});
/**
 * @author nick_brown
 */
 Ext.ux.VisitorsReportPanel = function(config){
 	this.config = config || {};
 	this.visitorsReady = false;
 	this.browserReady = false;
 	this.connectionReady = false;
 	this.pdfReady = false;
 	this.visitorsChart = new Ext.ux.ManagedIFramePanel({
		autoLoad: {
			url: 'visitors_reportchart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelVisitorsChartPanel',
		height: 220,
		split: false,
		border:true,
		frame:true,
		margins: '5 5 5 5',
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		frameConfig: {
			disableMessaging: false
		},
		cls: 'x-analytics-panel',
		region: 'north'
	});
	this.visitorsChart.on('message', function(srcFrame, message){
		if (message.data=='ready'){
			this.visitorsReady = true;
			if (this.browserReady && this.connectionReady){
				this.createPDF();
			}
		}
	}, this);
	this.visitorsBrowserGrid = new Ext.ux.VisitorsBrowserGridPanel(this.config);
	this.visitorsBrowserPieChart = new Ext.ux.ManagedIFramePanel({
		autoLoad: {
			url: 'visitors_reportpiechart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelVisitorsBrowserPieChartPanel',
		height: 230,
		split: false,
		border:true,
		frame:true,
		margins: '5 5 5 5',
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		frameConfig: {
			disableMessaging: false
		},
		cls: 'x-analytics-panel'
	});
	this.visitorsBrowserPieChart.on('message', function(srcFrame, message){
		if (message.data=='ready'){
			this.browserReady = true;
			if (this.visitorsReady && this.connectionReady){
				this.createPDF();
			}
		}
	}, this);
	this.visitorsConnectionSpeedGrid = new Ext.ux.VisitorsConnectionSpeedGridPanel(this.config);
	this.visitorsConnectionSpeedPieChart = new Ext.ux.ManagedIFramePanel({
		autoLoad: {
			url: 'visitors_reportpiechart1.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelVisitorsConnectionSpeedPieChartPanel',
		height: 230,
		split: false,
		border:true,
		frame:true,
		margins: '5 5 5 5',
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		frameConfig: {
			disableMessaging: false
		},
		cls: 'x-analytics-panel'
	});
	this.visitorsConnectionSpeedPieChart.on('message', function(srcFrame, message){
		if (message.data=='ready'){
			this.connectionReady = true;
			if (this.visitorsReady && this.browserReady){
				this.createPDF();
			}
		}
	}, this);
 	Ext.ux.VisitorsReportPanel.superclass.constructor.call(this,{
        id:'panelVisitorsReportPanel',
        title:'Visitors Report',
        margins: '0 0 0 0',
        layout:'border',
        autoScroll: true,
        autoShow: true,
        closable: true,
		iconCls: 'analytics-icon-cls',
		bodyStyle: 'background-color:transparent',
		border: true,
		closeable: true,
		tbar: new Ext.Toolbar({
			id: 'toolbarTopVisitorsReportPanel',
			items: [
		        {
					id: 'tbbuttonVisitorsReportExportReport',
		            icon: 'images/page_white_acrobat.png', // icons can also be specified inline
			        cls: 'x-btn-text-icon',
					text: 'Export Report to PDF',
					disabled: true,
		        	tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
					enableToggle: false,
					pressed: false,
					handler: function() {
						window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/VisitorsReport_' + new Date().format('dmY') + '.pdf'}), '_self');
					},
					scope: this
		        }
		    ]
		}),
		items: [
			this.visitorsChart,
			new Ext.Panel({
				id: 'panelVisitorsOthers',
				region: 'center',
				autoScroll: true,
				layout: 'column',
				defaults: {
			        // applied to each contained panel
			        bodyStyle:'padding:5px'
			    },
	    	    layoutConfig: {
			        // The total column count must be specified here
			        columns: 2
			    },
				border:false,
				items: [
					{ 	columnWidth : 0.5,
						border: false,
						items : [
							this.visitorsBrowserGrid,
							this.visitorsConnectionSpeedGrid
						]
					},
					{ 	columnWidth : 0.5,
						border: false,
						items : [
							this.visitorsBrowserPieChart,
							this.visitorsConnectionSpeedPieChart
						]
					}
				]
			})
		]
    });
 };
 Ext.extend(Ext.ux.VisitorsReportPanel, Ext.Panel, {
 	updateCharts: function (startDate, endDate){
 		this.getTopToolbar().items.get('tbbuttonVisitorsReportExportReport').disable();
 		this.visitorsReady = false;
 		this.browserReady=false;
 		this.connectionReady=false;
 		this.pdfReady=false;
 		this.config.startDate = startDate;
 		this.config.endDate = endDate;
 		this.visitorsChart.load({
			url: 'visitors_reportchart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});
		this.visitorsBrowserPieChart.load({
			url: 'visitors_reportpiechart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});
		this.visitorsConnectionSpeedPieChart.load({
			url: 'visitors_reportpiechart1.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});		
		this.visitorsBrowserGrid.updateGrid(startDate, endDate);
		this.visitorsConnectionSpeedGrid.updateGrid(startDate, endDate);
 	},
 	createPDF: function(){
		if (!this.pdfReady){
			Ext.Ajax.request({
				url: 'visitors_reportchartpdf.php',
				params: {
					email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
					passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
					id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
					startdate: this.config.startDate.format('Y-m-d'),
					enddate: this.config.endDate.format('Y-m-d')
				},
				method: 'GET',
				success: function(response, options){
					var responseData = Ext.util.JSON.decode(response.responseText);
					if (responseData.success){
						this.getTopToolbar().items.get('tbbuttonVisitorsReportExportReport').enable();
						this.pdfReady = true;
					}
				},
	            failure: function(response, options){
	               	Ext.Msg.alert('Error','Unable to mark messages as Read');
			    },
			    scope: this
			});
		}
	}
 });
/**
 * @author nick_brown
 */
 Ext.ux.MapOverlayGridPanel = function(config){
 	this.config = config || {};
 	this.Store = new Ext.data.JsonStore({
		autoLoad: {params: {start: 0, limit: 20}},
		baseParams: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}, 
		url: 'map_chart.php',
		root: 'data',
		fields: ['Index', 'Country', 'visits', 'pagesvisit', 'avgtime', 'newvisits', 'bouncerate'],
		method: 'GET',
		totalProperty: 'nummsg'
	});
	this.Store.on('beforeload', function(store, options){
		if (store.baseParams.id) delete store.baseParams.id;
		if (options.params) {
			if (options.params.id)	delete options.params.id;
		}
		Ext.apply(store.baseParams, {id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283')});
		if (store.baseParams.startdate) delete store.baseParams.startdate;
		if (options.params) {
			if (options.params.startdate)	delete options.params.startdate;
		}
		Ext.apply(store.baseParams, {startdate: this.config.startDate.format('Y-m-d')});
		if (store.baseParams.enddate) delete store.baseParams.enddate;
		if (options.params) {
			if (options.params.enddate)	delete options.params.enddate;
		}
		Ext.apply(store.baseParams, {enddate: this.config.endDate.format('Y-m-d')});
	}, this);
	this.SM = new Ext.grid.RowSelectionModel({singleSelect: true});
	this.SM.on('beforerowselect', function(){return false}, this);
	this.CM = new Ext.grid.ColumnModel([
		{header: '', dataIndex: 'Index', width: 25, sortable: false},
		{id: 'country', header: 'Country', dataIndex: 'Country', width: 300, sortable: false},
		{header: 'Visits', dataIndex: 'visits', width: 100, sortable: false, align: 'right'},
		{header: 'Pages/Visit', dataIndex: 'pagesvisit', width: 100, sortable: false, align: 'right'},
		{header: 'Avg. Time on Site', dataIndex: 'avgtime', width: 100, sortable: false, align: 'right'},
		{header: '% New Visits', dataIndex: 'newvisits', width: 100, sortable: false, align: 'right'},
		{header: 'Bounce Rate', dataIndex: 'bouncerate', width: 100, sortable: false, align: 'right'}
	]);
	Ext.ux.MapOverlayGridPanel.superclass.constructor.call(this,{
		id: 'gridMapOverlay',
		//deferredRender: true,
		monitorResize: true,
		store: this.Store,
		cm: this.CM,
		view: new Ext.grid.GridView({
			autoFill: true,
	        forceFit: false,
			emptyText: 'There is no content to display'
	    }),
		sm: this.SM,
		frame:true,
		collapsible : true,
		height: 230,
		split: false,
		frame:true,
		border:true,
		margins: '5 5 5 5',
		floatable: false,
		loadMask: {msg: 'Loading Data....'},
		cls: 'x-analytics-panel',
		//title: 'New Visits',
		//bbar: this.contentToolbar,
		autoExpandColumn: 'country',
		region: 'center',
		bbar: new Ext.PagingToolbar({
			id: 'pagingMapOverlayGrid',
			pageSize: 20,
			store: this.Store,
            displayInfo: true,
            displayMsg: 'Displaying countries {0} - {1} of {2}',
            emptyMsg: "No countries to display"
		})
		//stripeRows: true
	});
	this.on('render', function(){
		this.doLayout();
	}, this);
};
Ext.extend(Ext.ux.MapOverlayGridPanel, Ext.grid.GridPanel, {
	updateGrid: function(startDate, endDate){
		this.config.startDate = startDate;
		this.config.endDate = endDate;
		this.Store.load({params: {start: 0, limit: 20}});
	}
});
/**
 * @author nick_brown
 */
 Ext.ux.MapOverlayReportPanel = function(config){
 	this.config = config || {};
    this.pdfReady = false;
 	this.mapOverlayChart = new Ext.ux.MapOverlayPanel({
		id: 'panelMapOverlayChart',
		//title: 'Map Overlay',
		collapsible : true,
		height: 400,
		split: false,
		frame:true,
		border:true,
		margins: '5 5 5 5',
		floatable: false,
		//cls: 'x-analytics-panel',
		showLegend: true,
		chartHeight: '385px',
		region: 'north',
		startDate: this.config.startDate,
		endDate: this.config.endDate
	});
	this.mapOverlayGrid = new Ext.ux.MapOverlayGridPanel(this.config);
 	Ext.ux.MapOverlayReportPanel.superclass.constructor.call(this,{
        id:'panelMapOverlayReportPanel',
        title:'Map Overlay Report',
        margins: '0 0 0 0',
        layout:'border',
        autoScroll: true,
        autoShow: true,
        closable: true,
		iconCls: 'analytics-icon-cls',
		bodyStyle: 'background-color:transparent',
		border: true,
        tbar: new Ext.Toolbar({
			id: 'toolbarMapOverlayReportPanel',
			items: [
		        {
					id: 'tbbuttonMapOverlayReportExportReport',
		            icon: 'images/page_white_acrobat.png', // icons can also be specified inline
			        cls: 'x-btn-text-icon',
					text: 'Export Report to PDF',
					disabled: true,
		        	tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
					enableToggle: false,
					pressed: false,
					handler: function() {
						window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/MapOverlayReport_' + new Date().format('dmY') + '.pdf'}), '_self');
					},
					scope: this
		        }
		    ]
		}),
		items: [
			this.mapOverlayChart,
			this.mapOverlayGrid
		]
    });
    this.createPDF();
//    this.on('render', function(){
//		this.doLayout();
//	}, this);
 };
 Ext.extend(Ext.ux.MapOverlayReportPanel, Ext.Panel,{
 	updateCharts: function (startDate, endDate){
        this.getTopToolbar().items.get('tbbuttonMapOverlayReportExportReport').disable();
        this.pdfReady = false;
        this.createPDF();
 		this.config.startDate = startDate;
 		this.config.endDate = endDate;
 		this.mapOverlayChart.update(startDate, endDate);
		this.mapOverlayGrid.updateGrid(startDate, endDate);
 	},
    createPDF: function(){
		if (!this.pdfReady){
			Ext.Ajax.request({
				url: 'map_chartpdf.php',
				params: {
					email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
					passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
					id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
					startdate: this.config.startDate.format('Y-m-d'),
					enddate: this.config.endDate.format('Y-m-d')
				},
				method: 'GET',
				success: function(response, options){
					var responseData = Ext.util.JSON.decode(response.responseText);
					if (responseData.success){
						this.getTopToolbar().items.get('tbbuttonMapOverlayReportExportReport').enable();
						this.pdfReady = true;
					}
				},
	            failure: function(response, options){
	               	Ext.Msg.alert('Error','Unable to mark messages as Read');
			    },
			    scope: this
			});
		}
	}
 });
/**
 * @author nick_brown
 */
 Ext.ux.TrafficSourceGridPanel = function(config){
 	this.config = config || {};
 	this.Store = new Ext.data.JsonStore({
		autoLoad: {params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}},
		proxy: new Ext.data.HttpProxy({
			url: 'traffic_reportdata.php',	
			disableCaching:true,
			method: 'GET'
		}),
		root: 'data',
		fields: ['Source', 'Visits', 'Visits_percent'],
		method: 'GET'
	});
	this.SM = new Ext.grid.RowSelectionModel({singleSelect: true});
	this.SM.on('beforerowselect', function(){return false}, this);
	this.CM = new Ext.grid.ColumnModel([
		{id: 'source', header: 'Source', dataIndex: 'Source', width: 400, sortable: false},
		{header: 'Visits', dataIndex: 'Visits', width: 100, sortable: false, align: 'right'},
		{header: '% Visits', dataIndex: 'Visits_percent', width: 100, sortable: false, align: 'right'}
	]);
	Ext.ux.TrafficSourceGridPanel.superclass.constructor.call(this,{
		id: 'gridTrafficSource',
		//deferredRender: true,
		monitorResize: true,
		store: this.Store,
		cm: this.CM,
		view: new Ext.grid.GridView({
			autoFill: true,
	        forceFit: false,
			emptyText: 'There is no content to display'
	    }),
		sm: this.SM,
		frame:true,
		collapsible : true,
		height: 230,
		split: false,
		frame:true,
		border:true,
		//margins: '5 5 5 5',
		floatable: false,
		loadMask: {msg: 'Loading Chart....'},
		cls: 'x-analytics-panel',
		//title: 'New Visits',
		//bbar: this.contentToolbar,
		autoExpandColumn: 'source',
		closable:true
		//stripeRows: true
	});
};
Ext.extend(Ext.ux.TrafficSourceGridPanel, Ext.grid.GridPanel, {
	updateGrid: function(startDate, endDate){
		this.config.startDate = startDate;
		this.config.endDate = endDate;
		this.Store.load({params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}});
	}
});
/**
 * @author nick_brown
 */
 Ext.ux.TrafficKeywordGridPanel = function(config){
 	this.config = config || {};
 	this.Store = new Ext.data.JsonStore({
		autoLoad: {params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}},
		proxy: new Ext.data.HttpProxy({
			url: 'traffic_reportdata1.php',	
			disableCaching:true,
			method: 'GET'
		}),
		root: 'data',
		fields: ['Keyword', 'Visits', 'Visits_percent'],
		method: 'GET'
	});
	this.SM = new Ext.grid.RowSelectionModel({singleSelect: true});
	this.SM.on('beforerowselect', function(){return false}, this);
	this.CM = new Ext.grid.ColumnModel([
		{id: 'keyword', header: 'Keyword', dataIndex: 'Keyword', width: 400, sortable: false},
		{header: 'Visits', dataIndex: 'Visits', width: 100, sortable: false, align: 'right'},
		{header: '% Visits', dataIndex: 'Visits_percent', width: 100, sortable: false, align: 'right'}
	]);
	Ext.ux.TrafficKeywordGridPanel.superclass.constructor.call(this,{
		id: 'gridTrafficKeyword',
		//deferredRender: true,
		monitorResize: true,
		store: this.Store,
		cm: this.CM,
		view: new Ext.grid.GridView({
			autoFill: true,
	        forceFit: false,
			emptyText: 'There is no content to display'
	    }),
		sm: this.SM,
		frame:true,
		collapsible : true,
		height: 230,
		split: false,
		frame:true,
		border:true,
		//margins: '5 5 5 5',
		floatable: false,
		loadMask: {msg: 'Loading Chart....'},
		cls: 'x-analytics-panel',
		//title: 'New Visits',
		//bbar: this.contentToolbar,
		autoExpandColumn: 'keyword',
		closable:true
		//stripeRows: true
	});
};
Ext.extend(Ext.ux.TrafficKeywordGridPanel, Ext.grid.GridPanel, {
	updateGrid: function(startDate, endDate){
		this.config.startDate = startDate;
		this.config.endDate = endDate;
		this.Store.load({params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}});
	}
});
/**
 * @author nick_brown
 */
 Ext.ux.TrafficReportPanel = function(config){
 	this.config = config || {};
   	this.pdfReady = false;
 	this.trafficToolbar = new Ext.Toolbar({
		id: 'toolbarTopTrafficPanel',
		items: [
	        {
	        	xtype: 'cycle',
				id: 'tbbuttonTrafficChartType',
	            //prependText: 'Preview Messages - ',
	            cls: 'x-btn-text',
	            showText: true,
				disabled: false,
	        	tooltip: '<b>Chart Type</b><br/>Toggles the various chart types',
	        	items:[{
	        		id: 'menuitemDirect',
	        		text: 'Direct Traffic',
	        		//iconCls: 'icon-preview-bottom',
	        		checked: true
	        	},{
	        		id: 'menuitemReferral',
					text: 'Referring Sites'
					//,iconCls: 'icon-preview-right'
				},{
					id: 'menuitemSearch',
					text: 'Search Engines'
					//,iconCls: 'icon-preview-hide'
				}],
				changeHandler: function(btn, item) {
		            switch(item.text){
		                case 'Direct Traffic':
		                	this.trafficChart.getFrame().execScript('loaddirect()');
		                    break;
		                case 'Referring Sites':
		                    this.trafficChart.getFrame().execScript('loadreferral()');
		                    break;
		                case 'Search Engines':
		                	this.trafficChart.getFrame().execScript('loadorganic()');
		                    break;
		            }
				},
				scope: this
	        }
	]});
 	this.trafficChart = new Ext.ux.ManagedIFramePanel({
		autoLoad: {
			url: 'traffic_reportchart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelTrafficChartPanel',
		height: 425,
		split: false,
		border:true,
		frame: true,
		margins: '5 5 5 5',
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		frameConfig: {
			disableMessaging: false
		},
		cls: 'x-analytics-panel',
		region: 'north',
		tbar: this.trafficToolbar
	});
    this.trafficChart.on('message', function(srcFrame, message){
		if (message.tag=='ready') {
			if (message.data=='ready'){
				this.createPDF();
			}
		}
		if (message.tag=='chartchange') {
			switch(message.data){
				case 'direct':
					this.trafficChart.getTopToolbar().items.get('tbbuttonTrafficChartType').setActiveItem('menuitemDirect',true);
					break;
				case 'referral':
					this.trafficChart.getTopToolbar().items.get('tbbuttonTrafficChartType').setActiveItem('menuitemReferral',true);
					break;
				case 'organic':
					this.trafficChart.getTopToolbar().items.get('tbbuttonTrafficChartType').setActiveItem('menuitemSearch',true);
					break;
			}
		}
	}, this);
	this.trafficSourceGrid = new Ext.ux.TrafficSourceGridPanel(this.config);
	this.trafficKeywordGrid = new Ext.ux.TrafficKeywordGridPanel(this.config);
 	Ext.ux.TrafficReportPanel.superclass.constructor.call(this,{
        id:'panelTrafficReportPanel',
        title:'Traffic Overview Report',
        margins: '0 0 0 0',
        layout:'border',
        autoScroll: true,
        autoShow: true,
        closable: true,
		iconCls: 'analytics-icon-cls',
		bodyStyle: 'background-color:transparent',
		border: true,
		closeable: true,
        tbar: new Ext.Toolbar({
			id: 'toolbarTrafficReportPanel',
			items: [
		        {
					id: 'tbbuttonTrafficReportExportReport',
		            icon: 'images/page_white_acrobat.png', // icons can also be specified inline
			        cls: 'x-btn-text-icon',
					text: 'Export Report to PDF',
					disabled: true,
		        	tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
					enableToggle: false,
					pressed: false,
					handler: function() {
						window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/TrafficReport_' + new Date().format('dmY') + '.pdf'}), '_self');
					},
					scope: this
		        }
		    ]
		}),
		items: [
			this.trafficChart,
			new Ext.Panel({
				id: 'panelTrafficOthers',
				region: 'center',
				autoScroll: true,
				layout: 'column',
				defaults: {
			        // applied to each contained panel
			        bodyStyle:'padding:5px'
			    },
	    	    layoutConfig: {
			        // The total column count must be specified here
			        columns: 2
			    },
				border:false,
				items: [
					{ 	columnWidth : 0.5,
						border: false,
						items : [
							this.trafficSourceGrid
						]
					},
					{ 	columnWidth : 0.5,
						border: false,
						items : [
							this.trafficKeywordGrid
						]
					}
				]
			})
		]
    });
 };
 Ext.extend(Ext.ux.TrafficReportPanel, Ext.Panel, {
 	updateCharts: function (startDate, endDate){
        this.getTopToolbar().items.get('tbbuttonTrafficReportExportReport').disable();
		this.pdfReady = false;
 		this.config.startDate = startDate;
 		this.config.endDate = endDate;
 		this.trafficChart.load({
			url: 'traffic_reportchart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});
		this.trafficSourceGrid.updateGrid(startDate, endDate);
		this.trafficKeywordGrid.updateGrid(startDate, endDate);
 	},
 	createPDF: function(){
		if (!this.pdfReady){
			Ext.Ajax.request({
				url: 'traffic_reportchartpdf.php',
				params: {
					email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
					passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
					id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
					startdate: this.config.startDate.format('Y-m-d'),
					enddate: this.config.endDate.format('Y-m-d')
				},
				method: 'GET',
				success: function(response, options){
					var responseData = Ext.util.JSON.decode(response.responseText);
					if (responseData.success){
						this.getTopToolbar().items.get('tbbuttonTrafficReportExportReport').enable();
						this.pdfReady = true;
					}
				},
	            failure: function(response, options){
	               	Ext.Msg.alert('Error','Unable to mark messages as Read');
			    },
			    scope: this
			});
		}
	} 
 });
/**
 * @author nick_brown
 */
 Ext.ux.ContentGridPanel = function(config){
 	this.config = config || {};
 	this.Store = new Ext.data.JsonStore({
		autoLoad: {
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			}
		},
		url: 'content_reportdata.php',
		root: 'data',
		fields: ['Page', 'Pageviews', 'Pageviews_percent'],
		method: 'GET'
	});
	this.SM = new Ext.grid.RowSelectionModel({singleSelect: true});
	this.SM.on('beforerowselect', function(){return false}, this);
	this.CM = new Ext.grid.ColumnModel([
		{id: 'page', header: 'Page', dataIndex: 'Page', width: 400, sortable: false},
		{header: 'Pageviews', dataIndex: 'Pageviews', width: 100, sortable: false, align: 'right'},
		{header: '% Pageviews', dataIndex: 'Pageviews_percent', width: 100, sortable: false, align: 'right'}
	]);
	Ext.ux.ContentGridPanel.superclass.constructor.call(this,{
		id: 'gridContentReport',
		//deferredRender: true,
		monitorResize: true,
		store: this.Store,
		cm: this.CM,
		view: new Ext.grid.GridView({
			autoFill: true,
	        forceFit: false,
			emptyText: 'There is no content to display'
	    }),
		sm: this.SM,
		frame:true,
		collapsible : true,
		height: 230,
		split: false,
		frame:true,
		border:true,
		margins: '5 5 5 5',
		floatable: false,
		loadMask: {msg: 'Loading Chart....'},
		cls: 'x-analytics-panel',
		//title: 'New Visits',
		//bbar: this.contentToolbar,
		autoExpandColumn: 'page',
		closable:true,
		region: 'center'
		//stripeRows: true
	});
};
Ext.extend(Ext.ux.ContentGridPanel, Ext.grid.GridPanel, {
	updateGrid: function(startDate, endDate){
		this.config.startDate = startDate;
		this.config.endDate = endDate;
		this.Store.load({params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.config.startDate.format('Y-m-d'),
			enddate: this.config.endDate.format('Y-m-d')
		}});
	}
});
/**
 * @author nick_brown
 */
 Ext.ux.ContentReportPanel = function(config){
 	this.config = config || {};
    this.pdfReady = false;
 	this.contentToolbar = new Ext.Toolbar({
		id: 'toolbarTopContentPanel',
		items: [
	        {
	        	xtype: 'cycle',
				id: 'tbbuttonContentChartType',
	            //prependText: 'Preview Messages - ',
	            cls: 'x-btn-text',
	            showText: true,
				disabled: false,
	        	tooltip: '<b>Chart Type</b><br/>Toggles the various chart types',
	        	items:[{
	        		id: 'menuitemPageviews',
	        		text: 'Pageviews',
	        		//iconCls: 'icon-preview-bottom',
	        		checked: true
	        	},{
	        		id: 'menuitemUniquePageviews',
					text: 'Unique Views'
					//,iconCls: 'icon-preview-right'
				},{
					id: 'menuitemContentBounceRate',
					text: 'Bounce Rate'
					//,iconCls: 'icon-preview-hide'
				}],
				changeHandler: function(btn, item) {
		            switch(item.text){
		                case 'Pageviews':
		                	this.contentChart.getFrame().execScript('loadpageviews()');
		                    break;
		                case 'Unique Views':
		                    this.contentChart.getFrame().execScript('loaduniquepageviews()');
		                    break;
		                case 'Bounce Rate':
		                	this.contentChart.getFrame().execScript('loadbouncerate()');
		                    break;
		            }
				},
				scope: this
	        }
	]});
 	this.contentChart = new Ext.ux.ManagedIFramePanel({
		autoLoad: {
			url: 'content_reportchart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelContentChartPanel',
		height: 425,
		split: false,
		border:true,
		frame: true,
		margins: '5 5 5 5',
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		frameConfig: {
			disableMessaging: false
		},
		cls: 'x-analytics-panel',
		region: 'north',
		tbar: this.contentToolbar
	});
    this.contentChart.on('message', function(srcFrame, message){
		if (message.tag=='ready'){
			if (message.data=='ready'){
				this.createPDF();
			}
		}
		if (message.tag=='chartchange'){
			switch(message.data){
				case 'pageviews':
					this.contentChart.getTopToolbar().items.get('tbbuttonContentChartType').setActiveItem('menuitemPageviews',true);
					break;
				case 'uniquepageviews':
					this.contentChart.getTopToolbar().items.get('tbbuttonContentChartType').setActiveItem('menuitemUniquePageviews',true);
					break;
				case 'bouncerate':
					this.contentChart.getTopToolbar().items.get('tbbuttonContentChartType').setActiveItem('menuitemContentBounceRate',true);
					break;
			}
		}
	}, this);
	this.contentGrid = new Ext.ux.ContentGridPanel(this.config);
 	Ext.ux.ContentReportPanel.superclass.constructor.call(this,{
        id:'panelContentReportPanel',
        title:'Content Overview Report',
        margins: '0 0 0 0',
        layout:'border',
        autoScroll: true,
        autoShow: true,
        closable: true,
		iconCls: 'analytics-icon-cls',
		bodyStyle: 'background-color:transparent',
		border: true,
        tbar: new Ext.Toolbar({
			id: 'toolbarContentReportPanel',
			items: [
		        {
					id: 'tbbuttonContentReportExportReport',
		            icon: 'images/page_white_acrobat.png', // icons can also be specified inline
			        cls: 'x-btn-text-icon',
					text: 'Export Report to PDF',
					disabled: true,
		        	tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
					enableToggle: false,
					pressed: false,
					handler: function() {
						window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/ContentReport_' + new Date().format('dmY') + '.pdf'}), '_self');
					},
					scope: this
		        }
		    ]
		}),
		items: [
			this.contentChart,
			this.contentGrid
		]
    });
//    this.on('render', function(){
//		this.doLayout();
//	}, this);
 };
 Ext.extend(Ext.ux.ContentReportPanel, Ext.Panel,{
 	updateCharts: function (startDate, endDate){
        this.getTopToolbar().items.get('tbbuttonContentReportExportReport').disable();
		this.pdfReady = true;
 		this.config.startDate = startDate;
 		this.config.endDate = endDate;
 		this.contentChart.load({
			url: 'content_reportchart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.config.startDate.format('Y-m-d'),
				enddate: this.config.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});
		this.contentGrid.updateGrid(startDate, endDate);
 	},
 	createPDF: function(){
		if (!this.pdfReady){
			Ext.Ajax.request({
				url: 'content_reportchartpdf.php',
				params: {
					email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
					passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
					id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
					startdate: this.config.startDate.format('Y-m-d'),
					enddate: this.config.endDate.format('Y-m-d')
				},
				method: 'GET',
				success: function(response, options){
					var responseData = Ext.util.JSON.decode(response.responseText);
					if (responseData.success){
						this.getTopToolbar().items.get('tbbuttonContentReportExportReport').enable();
						this.pdfReady = true;
					}
				},
	            failure: function(response, options){
	               	Ext.Msg.alert('Error','Unable to mark messages as Read');
			    },
			    scope: this
			});
		}
	}  
 });
/**
 * @author nick_brown
 */
 
Ext.ux.collapsedPanelTitlePlugin = function ()
{
    this.init = function(p) {
        if (p.collapsible)
        {
            var r = p.region;
            if ((r == 'north') || (r == 'south'))
            {
                p.on ('render', function()
                    {
                        var ct = p.ownerCt;
                        ct.on ('afterlayout', function()
                            {
                                if (ct.layout[r].collapsedEl)
                                {
                                    p.collapsedTitleEl = ct.layout[r].collapsedEl.createChild ({
                                        tag: 'span',
                                        cls: 'x-panel-collapsed-text',
                                        html: p.title
                                    });
                                    p.setTitle = Ext.Panel.prototype.setTitle.createSequence (function(t)
                                        {p.collapsedTitleEl.dom.innerHTML = t;});
                                }
                            }, false, {single:true});
                        p.on ('collapse', function()
                            {
                                if (ct.layout[r].collapsedEl && !p.collapsedTitleEl)
                                {
                                    p.collapsedTitleEl = ct.layout[r].collapsedEl.createChild ({
                                        tag: 'span',
                                        cls: 'x-panel-collapsed-text',
                                        html: p.title
                                    });
                                    p.setTitle = Ext.Panel.prototype.setTitle.createSequence (function(t)
                                        {p.collapsedTitleEl.dom.innerHTML = t;});
                                }
                            }, false, {single:true});
                    });
            }
        }
    };
}
 
Ext.ux.AnalyticsPanel = function(config){
	function formatPage(value, metadata, record){
		return Ext.util.Format.ellipsis(value, 65);
	};
	this.config = config || {};
//	this.addEvents({
//		'deletefile': true
//	});
	this.reportDates = [];
	this.startDate = new Date().add(Date.MONTH,-1);
	this.endDate = new Date().add(Date.DAY, -1);
	var tmpDate = this.startDate;
	while (tmpDate <= this.endDate){
		this.reportDates.push(tmpDate);
		tmpDate = tmpDate.add(Date.DAY, 1);
	}
	this.visitsReady = false;
	this.visitorsReady = false;
	this.trafficReady = false;
	this.dashboardReady = false;
    this.exportVisitsMenu = new Ext.menu.Menu({
        id: 'menuVisitsExportMenu',
        items: [
            {
				id: 'menuitemVisitsExportReportPDF',
	            icon: 'images/page_white_acrobat.png', // icons can also be specified inline
				text: 'Export Report to PDF',
				disabled: false,
	        	tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
				handler: function() {
					window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/Visits_' + new Date().format('dmY') + '.pdf'}), '_self');
				},
				scope: this
	        },
	        {
				id: 'menuitemVisitsExportXML',
	            icon: 'images/ExportXml16.png', // icons can also be specified inline
				text: 'Export Report to XML',
				disabled: false,
	        	tooltip: '<b>Export Report</b><br/>Export the report as a XML Document',
				handler: function() {
					window.open('download.php?' + Ext.urlEncode({file:'tmp_xml/Visits_' + new Date().format('dmY') + '.xml'}), '_self');
				},
				scope: this
	        },
            {
				id: 'menuitemVisitsExportCSV',
	            icon: 'images/page_white_excel.png', // icons can also be specified inline
				text: 'Export Report to CSV',
				disabled: false,
	        	tooltip: '<b>Export Report</b><br/>Export the report as a CSV Document',
				handler: function() {
					window.open('download.php?' + Ext.urlEncode({file:'tmp_csv/Visits_' + new Date().format('dmY') + '.csv'}), '_self');
				},
				scope: this
	        },
            {
				id: 'menuitemVisitsExportTSV',
	            icon: 'images/page_white_go.png', // icons can also be specified inline
				text: 'Export Report to TSV',
				disabled: false,
	        	tooltip: '<b>Export Report</b><br/>Export the report as a TSV Document',
				handler: function() {
					window.open('download.php?' + Ext.urlEncode({file:'tmp_tsv/Visits_' + new Date().format('dmY') + '.tsv'}), '_self');
				},
				scope: this
	        },
            {
				id: 'menuitemVisitsExportJSON',
	            icon: 'images/json_file.png', // icons can also be specified inline
				text: 'Export Report to JSON',
				disabled: false,
	        	tooltip: '<b>Export Report</b><br/>Export the report as a JSON Document',
				handler: function() {
					window.open('download.php?' + Ext.urlEncode({file:'tmp_json/Visits_' + new Date().format('dmY') + '.json'}), '_self');
				},
				scope: this
	        }
        ]
    });
	this.visitsToolbar = new Ext.Toolbar({
		id: 'toolbarTopVisitsPanel',
		items: [
	        {
	        	xtype: 'cycle',
				id: 'tbbuttonChartType',
	            //prependText: 'Preview Messages - ',
	            cls: 'x-btn-text',
	            showText: true,
				disabled: false,
	        	tooltip: '<b>Chart Type</b><br/>Toggles the various chart types',
	        	items:[{
	        		id: 'menuitemVisits',
	        		text: 'Visits',
	        		//iconCls: 'icon-preview-bottom',
	        		checked: true
	        	},{
	        		id: 'menuitemPageViews',
					text: 'Pageviews'
					//,iconCls: 'icon-preview-right'
				},{
					id: 'menuitemPagesVisit',
					text: 'Pages/Visit'
					//,iconCls: 'icon-preview-hide'
				},{
					id: 'menuitemBounceRate',
					text: 'Bounce Rate'
					//,iconCls: 'icon-preview-hide'
				},{
					id: 'menuitemAvgTime',
					text: 'Avg. Time on Site'
					//,iconCls: 'icon-preview-hide'
				},{
					id: 'menuitemNewVisits',
					text: '% New Visits'
					//,iconCls: 'icon-preview-hide'
				}],
				changeHandler: function(btn, item) {
		            switch(item.text){
		                case 'Visits':
		                	this.visits.getFrame().execScript('loadvisits()');
		                    break;
		                case 'Pageviews':
		                    this.visits.getFrame().execScript('loadpageviews()');
		                    break;
		                case 'Pages/Visit':
		                	this.visits.getFrame().execScript('loadpageviewsvisits()');
		                    break;
		                case 'Bounce Rate':
		                	this.visits.getFrame().execScript('loadbouncerate()');
		                    break;
		                case 'Avg. Time on Site':
		                	this.visits.getFrame().execScript('loadavgtime()');
		                    break;
		               case '% New Visits':
		                	this.visits.getFrame().execScript('loadnewvisits()');
		                    break;
		            }
				},
				scope: this
	        },
	        {
				id: 'tbbuttonVisitsExportReport',
	            icon: 'images/report_disk.png', // icons can also be specified inline
		        cls: 'x-btn-text-icon',
				text: 'Export Report',
				disabled: true,
	        	tooltip: '<b>Export Report</b><br/>Export the report',
				enableToggle: false,
				pressed: false,
				menu: this.exportVisitsMenu,               
				scope: this
	        }
	]});
	this.visits = new Ext.ux.ManagedIFramePanel({
		autoLoad: {
			url: 'visits_chart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.startDate.format('Y-m-d'),
				enddate: this.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelVisits',
		title: 'Visits',
		collapsible : true,
		region: 'north',
		height: 380,
		split: false,
		frame:true,
		margins: '5 5 5 5',
		plugins: new Ext.ux.collapsedPanelTitlePlugin (),
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		tbar: this.visitsToolbar,
		frameConfig: {
			disableMessaging: false
		}
	});
	this.visits.on('message', function(srcFrame, message){
		if (message.tag=='ready'){
			if (message.data=='ready'){
				this.visitsReady = true;
				if (this.visitorsReady && this.trafficReady){
					this.dashboardPDF();
				}
				Ext.Ajax.request({
					url: 'visits_chartpdf.php',
					params: {
						email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
						passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
						id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
						startdate: this.startDate.format('Y-m-d'),
						enddate: this.endDate.format('Y-m-d')
					},
					method: 'GET',
					success: function(response, options){
						var responseData = Ext.util.JSON.decode(response.responseText);
						if (responseData.success){
							this.visits.getTopToolbar().items.get('tbbuttonVisitsExportReport').enable();	
						}
					},
					failure: function(response, options){
						Ext.Msg.alert('Error','Unable to mark messages as Read');
					},
					scope: this
				});
			}
		}
		if (message.tag=='chartchange'){
			switch(message.data){
				case 'Visits':
					this.visits.getTopToolbar().items.get('tbbuttonChartType').setActiveItem('menuitemVisits',true);
					this.visits.getTopToolbar().items.get('tbbuttonVisitsExportReport').disable();
					this.dashboard.getTopToolbar().items.get('tbbuttonDashboardExportReport').disable();
					this.visitsReady = false;
					this.dashboardReady = false;
					break;
				case 'Pageviews':
					this.visits.getTopToolbar().items.get('tbbuttonChartType').setActiveItem('menuitemPageViews',true);
					this.visits.getTopToolbar().items.get('tbbuttonVisitsExportReport').disable();
					this.dashboard.getTopToolbar().items.get('tbbuttonDashboardExportReport').disable();
					this.visitsReady = false;
					this.dashboardReady = false;
					break;
				case 'Pages/Visit':
					this.visits.getTopToolbar().items.get('tbbuttonChartType').setActiveItem('menuitemPagesVisit',true);
					this.visits.getTopToolbar().items.get('tbbuttonVisitsExportReport').disable();
					this.dashboard.getTopToolbar().items.get('tbbuttonDashboardExportReport').disable();
					this.visitsReady = false;
					this.dashboardReady = false;
					break;
				case 'Bounce Rate':
					this.visits.getTopToolbar().items.get('tbbuttonChartType').setActiveItem('menuitemBounceRate',true);
					this.visits.getTopToolbar().items.get('tbbuttonVisitsExportReport').disable();
					this.dashboard.getTopToolbar().items.get('tbbuttonDashboardExportReport').disable();
					this.visitsReady = false;
					this.dashboardReady = false;
					break;
				case 'Avg. Time on Site':
					this.visits.getTopToolbar().items.get('tbbuttonChartType').setActiveItem('menuitemAvgTime',true);
					this.visits.getTopToolbar().items.get('tbbuttonVisitsExportReport').disable();
					this.dashboard.getTopToolbar().items.get('tbbuttonDashboardExportReport').disable();
					this.visitsReady = false;
					this.dashboardReady = false;
					break;
			   case '% New Visits':
					this.visits.getTopToolbar().items.get('tbbuttonChartType').setActiveItem('menuitemNewVisits',true);
					this.visits.getTopToolbar().items.get('tbbuttonVisitsExportReport').disable();
					this.dashboard.getTopToolbar().items.get('tbbuttonDashboardExportReport').disable();
					this.visitsReady = false;
					this.dashboardReady = false;
					break;
			}
		}
		if (message.tag=='report') {
			switch(message.data){
				case 'Visits':
					if (this.visitsReport == undefined) {
						this.visitsReport = new Ext.ux.ManagedIFramePanel({
							autoLoad: {
								url: 'visits_reportchart.php',
								params: {
									email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
									passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
									id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
									startdate: this.startDate.format('Y-m-d'),
									enddate: this.endDate.format('Y-m-d')
								},
								method: 'GET',
								scripts: true
							},
							id: 'tabpanelVisitsReport',
							title: 'Visits Report',
							split: false,
							frame: true,
							margins: '0 0 0 0',
							floatable: false,
							showLoadIndicator: true,
							loadMask: {msg: 'Loading Chart....'},
							frameConfig: {
								disableMessaging: false
							},
							autoScroll: true,
							autoShow: true,
							closable: true,
							iconCls: 'analytics-icon-cls',
							bodyStyle: 'background-color:transparent;',
							border: true,
							tbar: new Ext.Toolbar({
								id: 'toolbarTopVisitsReportPanel',
								items: [
									{
										id: 'tbbuttonVisitsReportExportReport',
										icon: 'images/report_disk.png', // icons can also be specified inline
										cls: 'x-btn-text-icon',
										text: 'Export Report',
										disabled: true,
										tooltip: '<b>Export Report</b><br/>Export the report',
										enableToggle: false,
										pressed: false,
										menu: new Ext.menu.Menu({
											id: 'menuVisitsReportExportMenu',
											items: [
												{
													id: 'menuitemVisitsReportExportReportPDF',
													icon: 'images/page_white_acrobat.png', // icons can also be specified inline
													text: 'Export Report to PDF',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/VisitsReport_' + new Date().format('dmY') + '.pdf'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemVisitsReportExportXML',
													icon: 'images/ExportXml16.png', // icons can also be specified inline
													text: 'Export Report to XML',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a XML Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_xml/VisitsReport_' + new Date().format('dmY') + '.xml'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemVisitsReportExportCSV',
													icon: 'images/page_white_excel.png', // icons can also be specified inline
													text: 'Export Report to CSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a CSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_csv/VisitsReport_' + new Date().format('dmY') + '.csv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemVisitsReportExportTSV',
													icon: 'images/page_white_go.png', // icons can also be specified inline
													text: 'Export Report to TSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a TSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_tsv/VisitsReport_' + new Date().format('dmY') + '.tsv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemVisitsReportExportJSON',
													icon: 'images/json_file.png', // icons can also be specified inline
													text: 'Export Report to JSON',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a JSON Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_json/VisitsReport_' + new Date().format('dmY') + '.json'}), '_self');
													},
													scope: this
												}
											]
										}),               
										scope: this
									}
								]
							})
						});
						this.visitsReport.on('destroy', function(){
							this.visitsReport.purgeListeners();
							this.visitsReport = undefined;
						}, this);
						this.visitsReport.on('message', function(srcFrame, message){
							if (message.data=='ready'){
								Ext.Ajax.request({
									url: 'visits_reportchartpdf.php',
									params: {
										email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
										passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
										id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
										startdate: this.startDate.format('Y-m-d'),
										enddate: this.endDate.format('Y-m-d')
									},
									method: 'GET',
									success: function(response, options){
										var responseData = Ext.util.JSON.decode(response.responseText);
										if (responseData.success){
											this.visitsReport.getTopToolbar().items.get('tbbuttonVisitsReportExportReport').enable();	
										}
									},
									failure: function(response, options){
										Ext.Msg.alert('Error','Unable to mark messages as Read');
									},
									scope: this
								});
							}
						}, this);
						this.add(this.visitsReport);
					}
					this.setActiveTab(this.visitsReport);
					break;
				case 'Pageviews':
					if (this.pageViewsReport == undefined) {
						this.pageViewsReport = new Ext.ux.ManagedIFramePanel({
							autoLoad: {
								url: 'pageviews_reportchart.php',
								params: {
									email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
									passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
									id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
									startdate: this.startDate.format('Y-m-d'),
									enddate: this.endDate.format('Y-m-d')
								},
								method: 'GET',
								scripts: true
							},
							id: 'tabpanelPageviewsReport',
							title: 'Pageviews Report',
							split: false,
							frame: true,
							margins: '5 5 5 5',
							floatable: false,
							showLoadIndicator: true,
							loadMask: {msg: 'Loading Chart....'},
							frameConfig: {
								disableMessaging: false
							},	
							autoScroll: true,
							autoShow: true,
							closable: true,
							iconCls: 'analytics-icon-cls',
							bodyStyle: 'background-color:transparent;',
							border: true,
							tbar: new Ext.Toolbar({
								id: 'toolbarTopPageviewsReportPanel',
								items: [
									{
										id: 'tbbuttonPageviewsReportExportReport',
										icon: 'images/report_disk.png', // icons can also be specified inline
										cls: 'x-btn-text-icon',
										text: 'Export Report',
										disabled: true,
										tooltip: '<b>Export Report</b><br/>Export the report',
										enableToggle: false,
										pressed: false,
										menu: new Ext.menu.Menu({
											id: 'menuPageviewsReportExportMenu',
											items: [
												{
													id: 'menuitemPageviewsReportExportReportPDF',
													icon: 'images/page_white_acrobat.png', // icons can also be specified inline
													text: 'Export Report to PDF',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/PageViewsReport_' + new Date().format('dmY') + '.pdf'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemPageviewsReportExportXML',
													icon: 'images/ExportXml16.png', // icons can also be specified inline
													text: 'Export Report to XML',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a XML Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_xml/PageViewsReport_' + new Date().format('dmY') + '.xml'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemPageviewsReportExportCSV',
													icon: 'images/page_white_excel.png', // icons can also be specified inline
													text: 'Export Report to CSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a CSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_csv/PageViewsReport_' + new Date().format('dmY') + '.csv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemPageviewsReportExportTSV',
													icon: 'images/page_white_go.png', // icons can also be specified inline
													text: 'Export Report to TSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a TSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_tsv/PageViewsReport_' + new Date().format('dmY') + '.tsv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemPageviewsExportJSON',
													icon: 'images/json_file.png', // icons can also be specified inline
													text: 'Export Report to JSON',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a JSON Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_json/PageViewsReport_' + new Date().format('dmY') + '.json'}), '_self');
													},
													scope: this
												}
											]
										}),               
										scope: this
									}
								]
							})
						});
						this.pageViewsReport.on('destroy', function(){
							this.pageViewsReport = undefined;
						}, this);
						this.pageViewsReport.on('message', function(srcFrame, message){
							if (message.data=='ready'){
								Ext.Ajax.request({
									url: 'pageviews_reportchartpdf.php',
									params: {
										email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
										passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
										id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
										startdate: this.startDate.format('Y-m-d'),
										enddate: this.endDate.format('Y-m-d')
									},
									method: 'GET',
									success: function(response, options){
										var responseData = Ext.util.JSON.decode(response.responseText);
										if (responseData.success){
											this.pageViewsReport.getTopToolbar().items.get('tbbuttonPageviewsReportExportReport').enable();	
										}
									},
									failure: function(response, options){
										Ext.Msg.alert('Error','Unable to mark messages as Read');
									},
									scope: this
								});
							}
						}, this);
						this.add(this.pageViewsReport);
					}
					this.setActiveTab(this.pageViewsReport);
					break;
				case 'Pages/Visit':
					if (this.pagesVisitReport == undefined) {
						this.pagesVisitReport = new Ext.ux.ManagedIFramePanel({
							autoLoad: {
								url: 'pagesvisit_reportchart.php',
								params: {
									email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
									passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
									id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
									startdate: this.startDate.format('Y-m-d'),
									enddate: this.endDate.format('Y-m-d')
								},
								method: 'GET',
								scripts: true
							},
							id: 'tabpanelPagesvisitReport',
							title: 'Pages/Visit Report',
							split: false,
							frame: true,
							margins: '5 5 5 5',
							floatable: false,
							showLoadIndicator: true,
							loadMask: {msg: 'Loading Chart....'},
							frameConfig: {
								disableMessaging: false
							},
							autoScroll: true,
							autoShow: true,
							closable: true,
							iconCls: 'analytics-icon-cls',
							bodyStyle: 'background-color:transparent;',
							border: true,
							tbar: new Ext.Toolbar({
								id: 'toolbarTopPagesVisitReportPanel',
								items: [
									{
										id: 'tbbuttonPagesVisitReportExportReport',
										icon: 'images/report_disk.png', // icons can also be specified inline
										cls: 'x-btn-text-icon',
										text: 'Export Report',
										disabled: true,
										tooltip: '<b>Export Report</b><br/>Export the report',
										enableToggle: false,
										pressed: false,
										menu: new Ext.menu.Menu({
											id: 'menuPagesVisitReportExportMenu',
											items: [
												{
													id: 'menuitemPagesVisitReportExportReportPDF',
													icon: 'images/page_white_acrobat.png', // icons can also be specified inline
													text: 'Export Report to PDF',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/PagesVisitReport_' + new Date().format('dmY') + '.pdf'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemPagesVisitReportExportXML',
													icon: 'images/ExportXml16.png', // icons can also be specified inline
													text: 'Export Report to XML',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a XML Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_xml/PagesVisitReport_' + new Date().format('dmY') + '.xml'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemPagesVisitReportExportCSV',
													icon: 'images/page_white_excel.png', // icons can also be specified inline
													text: 'Export Report to CSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a CSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_csv/PagesVisitReport_' + new Date().format('dmY') + '.csv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemPagesVisitReportExportTSV',
													icon: 'images/page_white_go.png', // icons can also be specified inline
													text: 'Export Report to TSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a TSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_tsv/PagesVisitReport_' + new Date().format('dmY') + '.tsv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemPagesVisitReportExportJSON',
													icon: 'images/json_file.png', // icons can also be specified inline
													text: 'Export Report to JSON',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a JSON Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_json/PagesVisitReport_' + new Date().format('dmY') + '.json'}), '_self');
													},
													scope: this
												}
											]
										}),               
										scope: this
									}
								]
							})
						});
						this.pagesVisitReport.on('destroy', function(){
							this.pagesVisitReport = undefined;
						}, this);
						this.pagesVisitReport.on('message', function(srcFrame, message){
							if (message.data=='ready'){
								Ext.Ajax.request({
									url: 'pagesvisit_reportchartpdf.php',
									params: {
										email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
										passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
										id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
										startdate: this.startDate.format('Y-m-d'),
										enddate: this.endDate.format('Y-m-d')
									},
									method: 'GET',
									success: function(response, options){
										var responseData = Ext.util.JSON.decode(response.responseText);
										if (responseData.success){
											this.pagesVisitReport.getTopToolbar().items.get('tbbuttonPagesVisitReportExportReport').enable();	
										}
									},
									failure: function(response, options){
										Ext.Msg.alert('Error','Unable to mark messages as Read');
									},
									scope: this
								});
							}
						}, this);
						this.add(this.pagesVisitReport);
					}
					this.setActiveTab(this.pagesVisitReport);
					break;
				case 'Bounce Rate':
					if (this.bounceRateReport == undefined) {
						this.bounceRateReport = new Ext.ux.ManagedIFramePanel({
							autoLoad: {
								url: 'bouncerate_reportchart.php',
								params: {
									email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
									passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
									id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
									startdate: this.startDate.format('Y-m-d'),
									enddate: this.endDate.format('Y-m-d')
								},
								method: 'GET',
								scripts: true
							},
							id: 'tabpanelBouncerateReport',
							title: 'Bounce Rate Report',
							split: false,
							frame: true,
							margins: '5 5 5 5',
							floatable: false,
							showLoadIndicator: true,
							loadMask: {msg: 'Loading Chart....'},
							frameConfig: {
								disableMessaging: false
							},
							autoScroll: true,
							autoShow: true,
							closable: true,
							iconCls: 'analytics-icon-cls',
							bodyStyle: 'background-color:transparent;',
							border: true,
							tbar: new Ext.Toolbar({
								id: 'toolbarTopBounceRateReportPanel',
								items: [
									{
										id: 'tbbuttonBounceRateReportExportReport',
										icon: 'images/report_disk.png', // icons can also be specified inline
										cls: 'x-btn-text-icon',
										text: 'Export Report',
										disabled: true,
										tooltip: '<b>Export Report</b><br/>Export the report',
										enableToggle: false,
										pressed: false,
										menu: new Ext.menu.Menu({
											id: 'menuBounceRateReportExportMenu',
											items: [
												{
													id: 'menuitemBounceRateReportExportReportPDF',
													icon: 'images/page_white_acrobat.png', // icons can also be specified inline
													text: 'Export Report to PDF',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/BounceRateReport_' + new Date().format('dmY') + '.pdf'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemBounceRateReportExportXML',
													icon: 'images/ExportXml16.png', // icons can also be specified inline
													text: 'Export Report to XML',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a XML Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_xml/BounceRateReport_' + new Date().format('dmY') + '.xml'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemBounceRateReportExportCSV',
													icon: 'images/page_white_excel.png', // icons can also be specified inline
													text: 'Export Report to CSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a CSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_csv/BounceRateReport_' + new Date().format('dmY') + '.csv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemBounceRateReportExportTSV',
													icon: 'images/page_white_go.png', // icons can also be specified inline
													text: 'Export Report to TSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a TSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_tsv/BounceRateReport_' + new Date().format('dmY') + '.tsv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemBounceRateReportExportJSON',
													icon: 'images/json_file.png', // icons can also be specified inline
													text: 'Export Report to JSON',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a JSON Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_json/BounceRateReport_' + new Date().format('dmY') + '.json'}), '_self');
													},
													scope: this
												}
											]
										}),               
										scope: this
									}
								]
							})
						});
						this.bounceRateReport.on('destroy', function(){
							this.bounceRateReport = undefined;
						}, this);
						this.bounceRateReport.on('message', function(srcFrame, message){
							if (message.data=='ready'){
								Ext.Ajax.request({
									url: 'bouncerate_reportchartpdf.php',
									params: {
										email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
										passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
										id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
										startdate: this.startDate.format('Y-m-d'),
										enddate: this.endDate.format('Y-m-d')
									},
									method: 'GET',
									success: function(response, options){
										var responseData = Ext.util.JSON.decode(response.responseText);
										if (responseData.success){
											this.bounceRateReport.getTopToolbar().items.get('tbbuttonBounceRateReportExportReport').enable();	
										}
									},
									failure: function(response, options){
										Ext.Msg.alert('Error','Unable to mark messages as Read');
									},
									scope: this
								});
							}
						}, this);
						this.add(this.bounceRateReport);
					}
					this.setActiveTab(this.bounceRateReport);
					break;
				case 'Avg. Time on Site':
					if (this.avgTimeReport == undefined) {
						this.avgTimeReport = new Ext.ux.ManagedIFramePanel({
							autoLoad: {
								url: 'avgtime_reportchart.php',
								params: {
									email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
									passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
									id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
									startdate: this.startDate.format('Y-m-d'),
									enddate: this.endDate.format('Y-m-d')
								},
								method: 'GET',
								scripts: true
							},
							id: 'tabpanelAvgtimeReport',
							title: 'Avg. Time on Site Report',
							split: false,
							frame: true,
							margins: '5 5 5 5',
							floatable: false,
							showLoadIndicator: true,
							loadMask: {msg: 'Loading Chart....'},
							frameConfig: {
								disableMessaging: false
							},
							autoScroll: true,
							autoShow: true,
							closable: true,
							iconCls: 'analytics-icon-cls',
							bodyStyle: 'background-color:transparent;',
							border: true,
							tbar: new Ext.Toolbar({
								id: 'toolbarTopAvgTimeReportPanel',
								items: [
									{
										id: 'tbbuttonAvgTimeReportExportReport',
										icon: 'images/report_disk.png', // icons can also be specified inline
										cls: 'x-btn-text-icon',
										text: 'Export Report',
										disabled: true,
										tooltip: '<b>Export Report</b><br/>Export the report',
										enableToggle: false,
										pressed: false,
										menu: new Ext.menu.Menu({
											id: 'menuAvgTimeReportExportMenu',
											items: [
												{
													id: 'menuitemAvgTimeReportExportReportPDF',
													icon: 'images/page_white_acrobat.png', // icons can also be specified inline
													text: 'Export Report to PDF',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/AvgTimeReport_' + new Date().format('dmY') + '.pdf'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemAvgTimeReportExportXML',
													icon: 'images/ExportXml16.png', // icons can also be specified inline
													text: 'Export Report to XML',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a XML Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_xml/AvgTimeReport_' + new Date().format('dmY') + '.xml'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemAvgTimeReportExportCSV',
													icon: 'images/page_white_excel.png', // icons can also be specified inline
													text: 'Export Report to CSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a CSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_csv/AvgTimeReport_' + new Date().format('dmY') + '.csv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemAvgTimeReportExportTSV',
													icon: 'images/page_white_go.png', // icons can also be specified inline
													text: 'Export Report to TSV',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a TSV Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_tsv/AvgTimeReport_' + new Date().format('dmY') + '.tsv'}), '_self');
													},
													scope: this
												},
												{
													id: 'menuitemAvgTimeReportExportJSON',
													icon: 'images/json_file.png', // icons can also be specified inline
													text: 'Export Report to JSON',
													disabled: false,
													tooltip: '<b>Export Report</b><br/>Export the report as a JSON Document',
													handler: function() {
														window.open('download.php?' + Ext.urlEncode({file:'tmp_json/AvgTimeReport_' + new Date().format('dmY') + '.json'}), '_self');
													},
													scope: this
												}
											]
										}),               
										scope: this
									}
								]
							})
						});
						this.avgTimeReport.on('destroy', function(){
							this.avgTimeReport = undefined;
						}, this);	
						this.avgTimeReport.on('message', function(srcFrame, message){
							if (message.data=='ready'){
								Ext.Ajax.request({
									url: 'avgtime_reportchartpdf.php',
									params: {
										email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
										passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
										id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
										startdate: this.startDate.format('Y-m-d'),
										enddate: this.endDate.format('Y-m-d')
									},
									method: 'GET',
									success: function(response, options){
										var responseData = Ext.util.JSON.decode(response.responseText);
										if (responseData.success){
											this.avgTimeReport.getTopToolbar().items.get('tbbuttonAvgTimeReportExportReport').enable();	
										}
									},
									failure: function(response, options){
										Ext.Msg.alert('Error','Unable to mark messages as Read');
									},
									scope: this
								});
							}
						}, this);
						this.add(this.avgTimeReport);
					}
					this.setActiveTab(this.avgTimeReport);
					break;
			   case '% New Visits':
					if (this.newVisitsReport == undefined) {
						this.newVisitsReport = this.add(new Ext.ux.NewVisitorsReportPanel({startDate: this.startDate, endDate: this.endDate}));
						this.newVisitsReport.doLayout();
					}
					this.setActiveTab(this.newVisitsReport);
					this.newVisitsReport.doLayout();
					this.newVisitsReport.on('destroy', function(){
						this.newVisitsReport = undefined;
					}, this);
					break;
			}
		}
	}, this);
	this.visits.on('expand', function(p){
		this.visits.getTopToolbar().items.get('tbbuttonChartType').setActiveItem('menuitemVisits',true);
	}, this);
	this.visitorsToolbar = new Ext.Toolbar({
		id: 'toolbarBottomVisitorsPanel',
		items: [
	        {
				id: 'tbbuttonVisitorsViewReport',
	            icon: 'images/report_go.png', // icons can also be specified inline
		        cls: 'x-btn-text-icon',
				text: 'View Report',
				disabled: false,
	        	tooltip: '<b>View Report</b><br/>View the full Vistors Report in a new tab',
				enableToggle: false,
				pressed: false,
				handler: function() {
					if (this.visitorsReport == undefined) {
		            	this.visitorsReport = this.add(new Ext.ux.VisitorsReportPanel({startDate: this.startDate, endDate: this.endDate}));
		            	this.visitorsReport.doLayout();
	            	}
		            this.setActiveTab(this.visitorsReport);
		            this.visitorsReport.doLayout();
		            this.visitorsReport.on('destroy', function(){
		            	this.visitorsReport = undefined;
		            }, this);
				},
				scope: this
	        }
	]});
	this.visitors = new Ext.ux.ManagedIFramePortlet({
		autoLoad: {
			url: 'visitors_chart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.startDate.format('Y-m-d'),
				enddate: this.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelVisitors',
		title: 'Visitors Overview',
		collapsible : true,
		height: 250,
		layout: 'fit',
		split: false,
		frame:true,
		margins: '5 5 5 5',
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		frameConfig: {
			disableMessaging: false
		},
		//draggable: true,
		cls: 'x-analytics-panel',
		bbar: this.visitorsToolbar
	});
	this.visitors.on('message', function(srcFrame, message){
		if (message.data=='ready'){
			this.visitorsReady = true;
			if (this.visitsReady && this.trafficReady){
				this.dashboardPDF();
			}
		}
	}, this);
	this.trafficToolbar = new Ext.Toolbar({
		id: 'toolbarBottomTrafficPanel',
		items: [
	        {
				id: 'tbbuttonTrafficViewReport',
	            icon: 'images/report_go.png', // icons can also be specified inline
		        cls: 'x-btn-text-icon',
				text: 'View Report',
				disabled: false,
	        	tooltip: '<b>View Report</b><br/>View the full Traffic Sources Report in a new tab',
				enableToggle: false,
				pressed: false,
				handler: function() {
					if (this.trafficReport == undefined) {
		            	this.trafficReport = this.add(new Ext.ux.TrafficReportPanel({startDate: this.startDate, endDate: this.endDate}));
		            	this.trafficReport.doLayout();
	            	}
		            this.setActiveTab(this.trafficReport);
		            this.trafficReport.doLayout();
		            this.trafficReport.on('destroy', function(){
		            	this.trafficReport = undefined;
		            }, this);
				},
				scope: this
	        }
	]});
	this.traffic = new Ext.ux.ManagedIFramePortlet({
		autoLoad: {
			url: 'traffic_chart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.startDate.format('Y-m-d'),
				enddate: this.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		},
		id: 'panelTraffic',
		title: 'Traffic Sources Overview',
		collapsible : true,
		layout: 'fit',
		height: 250,
		split: false,
		frame:true,
		margins: '5 5 5 5',
		floatable: false,
		showLoadIndicator: true,
		loadMask: {msg: 'Loading Chart....'},
		frameConfig: {
			disableMessaging: false
		},
		cls: 'x-analytics-panel',
		bbar: this.trafficToolbar
	});
	this.traffic.on('message', function(srcFrame, message){
		if (message.data=='ready'){
			this.trafficReady = true;
			if (this.visitsReady && this.visitorsReady){
				this.dashboardPDF();
			}
		}
	}, this);
	this.mapOverlayToolbar = new Ext.Toolbar({
		id: 'toolbarBottomMapOverlayPanel',
		items: [
	        {
				id: 'tbbuttonMapOverlayViewReport',
	            icon: 'images/report_go.png', // icons can also be specified inline
		        cls: 'x-btn-text-icon',
				text: 'View Report',
				disabled: false,
	        	tooltip: '<b>View Report</b><br/>View the full Map Overlay Report in a new tab',
				enableToggle: false,
				pressed: false,
				handler: function() {
					if (this.mapOverlayReport == undefined) {
		            	this.mapOverlayReport = this.add(new Ext.ux.MapOverlayReportPanel({startDate: this.startDate, endDate: this.endDate}));
		            	this.mapOverlayReport.doLayout();
	            	}
		            this.setActiveTab(this.mapOverlayReport);
		            this.mapOverlayReport.doLayout();
		            this.mapOverlayReport.on('destroy', function(){
		            	this.mapOverlayReport = undefined;
		            }, this);
				},
				scope: this
	        }
	]});
	this.mapOverlay = new Ext.ux.MapOverlayPanel({
		id: 'panelMapOverlay',
		title: 'Map Overlay',
		collapsible : true,
		height: 250,
		split: false,
		frame:true,
		margins: '5 5 5 5',
		floatable: false,
		cls: 'x-analytics-panel',
		bbar: this.mapOverlayToolbar,
		showLegend: false,
		chartHeight: '160px',
		startDate : this.startDate,
		endDate : this.endDate
	});
	this.contentStore = new Ext.data.JsonStore({
		autoLoad: {
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.startDate.format('Y-m-d'),
				enddate: this.endDate.format('Y-m-d')
			}
		},
		proxy: new Ext.data.HttpProxy({
			url: 'content_chart.php',	
			disableCaching:true,
			method: 'GET'
		}),
		root: 'data',
		fields: ['Page', 'Pageviews', 'Pageviews_percent'],
		method: 'GET'
	});
	this.contentSm = new Ext.grid.RowSelectionModel({singleSelect: true});
	this.contentSm.on('beforerowselect', function(){return false}, this);
	this.contentCm = new Ext.grid.ColumnModel([
		{id: 'pages', header: 'Pages', dataIndex: 'Page', width: 400, sortable: true, renderer: formatPage},
		{header: 'Pageviews', dataIndex: 'Pageviews', width: 100, sortable: true, align: 'right'},
		{header: '% Pageviews', dataIndex: 'Pageviews_percent', width: 100, sortable: true, align: 'right'}
	]);
	this.contentToolbar = new Ext.Toolbar({
		id: 'toolbarBottomContentPanel',
		items: [
	        {
				id: 'tbbuttonContentViewReport',
	            icon: 'images/report_go.png', // icons can also be specified inline
		        cls: 'x-btn-text-icon',
				text: 'View Report',
				disabled: false,
	        	tooltip: '<b>View Report</b><br/>View the full Content Report in a new tab',
				enableToggle: false,
				pressed: false,
				handler: function() {
					if (this.contentReport == undefined) {
		            	this.contentReport = this.add(new Ext.ux.ContentReportPanel({startDate: this.startDate, endDate: this.endDate}));
	            	}
		            this.setActiveTab(this.contentReport);
		            this.contentReport.on('destroy', function(){
		            	this.contentReport = undefined;
		            }, this);
				},
				scope: this
	        }
	]});
	this.reportDateField = new Ext.ux.form.DateFieldPlus({
    	noOfMonth: 2,
    	noOfMonthPerRow: 2,
    	multiSelection: true,
        multiSelectionDelimiter: ' - ',
        width: 165,
        format: 'M j, Y',
        value: this.reportDates,
        preSelectedDates: this.reportDates,
        selectedDates: this.reportDates,
        height: 20,
        maxDate: this.endDate.add(Date.DAY, 1),
        minSelectionDays: 2
    });
    this.reportDateField.on('select', function(dateField, value){
    	this.startDate = value[0];
    	this.endDate = value[value.length -1];
    	this.selectAccount();
    },this);
    this.dashboardToolbar = new Ext.Toolbar({
		id: 'toolbarTopDashboardPanel',
		items: [
			{
				id: 'tbbuttonDashboardExportReport',
	            icon: 'images/page_white_acrobat.png', // icons can also be specified inline
		        cls: 'x-btn-text-icon',
				text: 'Export Report to PDF',
				disabled: true,
	        	tooltip: '<b>Export Report</b><br/>Export the report as a PDF Document',
				enableToggle: false,
				pressed: false,
				handler: function() {
					window.open('download.php?' + Ext.urlEncode({file:'tmp_pdf/Dashboard_' + new Date().format('dmY') + '.pdf'}), '_self');
				},
				scope: this
	        },		
	        '->',
	        'Report Dates: ',
	        ' ',
	        this.reportDateField
	]});
	this.dashboard = new Ext.Panel({
        id:'tabpanelDashboard',
        title:'Dashboard',
        margins: '0 0 0 0',
        layout:'border',
        autoScroll: true,
        autoShow: true,
        closable: false,
		iconCls: 'analytics-icon-cls',
		bodyStyle: 'background-color:transparent',
		border: false,
		tbar: this.dashboardToolbar,
		items: [
			this.visits,
			{
	            xtype:'portal',
	            region:'center',
	            border: false,
	            margins:'5 5 5 5',
	            items:[{
	                columnWidth:.5,
	                //style:'padding:0 0 0 0px',
	                items:[
	                	this.visitors,
	                	this.traffic
	                ]
	            },{
	                columnWidth:.5,
	                style:'padding:0 0 0 10px',
	                items:[
	                	this.mapOverlay,
	                	{
							xtype: 'portlet',
		                    title: 'Content Overview',
		                    //layout:'fit',
		                    height: 237,
		                    //margins: '5 5 5 5',
		                    cls: 'x-analytics-panel',
							bbar: this.contentToolbar,
		                    items: new Ext.grid.GridPanel({
								id: 'gridContent',
								monitorResize: true,
								store: this.contentStore,
								cm: this.contentCm,
								view: new Ext.grid.GridView({
									autoFill: true,
							        forceFit: false,
									emptyText: 'There is no content to display'
							    }),
								sm: this.contentSm,
								frame:false,
								height: 180,
								floatable: false,
								loadMask: {msg: 'Loading Chart....'},
								border: false,
								autoExpandColumn: 'pages',
								stripeRows: true
							})
	                	}
	                ]
	            }],
	            listeners: {
	                'drop': function(e){
	                    switch (e.panel.id){
	                    	case 'panelVisitors':
	                    		this.visitors.load({
									url: 'visitors_chart.php',
									params: {
										email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
										passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
										id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
										startdate: this.startDate.format('Y-m-d'),
										enddate: this.endDate.format('Y-m-d')
									},
									method: 'GET',
									scripts: true
								});
								break;
							case 'panelTraffic':
								this.traffic.load({
									url: 'traffic_chart.php',
									params: {
										email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
										passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
										id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
										startdate: this.startDate.format('Y-m-d'),
										enddate: this.endDate.format('Y-m-d')
									},
									method: 'GET',
									scripts: true
								});
								break;
	                    }
	                },
	                scope: this
	            }
			}
		]
    });
//    this.dashboard.on('render', function(){
//    	this.dashboard.doLayout();
//    }, this);
    Ext.ux.AnalyticsPanel.superclass.constructor.call(this,{
    	id:'analyticsTabs',
        activeTab:0,
        margins:'0 0 0 0',
        deferredRender: true,
        tabWidth:200,
        minTabWidth: 120,
        enableTabScroll: true,
        region: 'center',
        items : [
        	this.dashboard
    	]
    });
    
};
Ext.extend(Ext.ux.AnalyticsPanel, Ext.TabPanel, {
//	afterRender: function(){
//		Ext.ux.AnalyticsPanel.superclass.afterRender.call(this);
//		this.doLayout();
//		//this.quotaPanel.getEl().set({align:center});
//	},
	selectAccount:  function(){
		this.visitsReady = false;
		this.visitorsReady = false;
		this.trafficReady = false;
		this.dashboardReady = false;
		this.visits.getTopToolbar().items.get('tbbuttonVisitsExportReport').disable();	
		this.dashboard.getTopToolbar().items.get('tbbuttonDashboardExportReport').disable();
		this.mapOverlay.update(this.startDate, this.endDate);
		this.visits.getTopToolbar().items.get('tbbuttonChartType').setActiveItem('menuitemVisits',true);
		this.visits.load({
			url: 'visits_chart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.startDate.format('Y-m-d'),
				enddate: this.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});
		this.visitors.load({
			url: 'visitors_chart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.startDate.format('Y-m-d'),
				enddate: this.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});
		this.traffic.load({
			url: 'traffic_chart.php',
			params: {
				email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
				passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
				id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
				startdate: this.startDate.format('Y-m-d'),
				enddate: this.endDate.format('Y-m-d')
			},
			method: 'GET',
			scripts: true
		});
		this.contentStore.load({params: {
			email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
			passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
			id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
			startdate: this.startDate.format('Y-m-d'),
			enddate: this.endDate.format('Y-m-d')
		}});
		this.items.eachKey(function(key, item){
				switch(key) {
					case 'tabpanelVisitsReport':
						this.visitsReport.getTopToolbar().items.get('tbbuttonVisitsReportExportReport').disable();
						this.visitsReport.load({
							url: 'visits_reportchart.php',
							params: {
								email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
								passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
								id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
								startdate: this.startDate.format('Y-m-d'),
								enddate: this.endDate.format('Y-m-d')
							},
							method: 'GET',
							scripts: true
						});
						break;
					case 'tabpanelPageviewsReport': 
						this.pageViewsReport.getTopToolbar().items.get('tbbuttonPageviewsReportExportReport').disable();
						this.pageViewsReport.load({
							url: 'pageviews_reportchart.php',
							params: {
								email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
								passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
								id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
								startdate: this.startDate.format('Y-m-d'),
								enddate: this.endDate.format('Y-m-d')
							},
							method: 'GET',
							scripts: true
						});
						break;
					case 'tabpanelPagesvisitReport':
						this.pagesVisitReport.getTopToolbar().items.get('tbbuttonPagesVisitReportExportReport').disable();
						this.pagesVisitReport.load({
							url: 'pagesvisit_reportchart.php',
							params: {
								email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
								passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
								id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
								startdate: this.startDate.format('Y-m-d'),
								enddate: this.endDate.format('Y-m-d')
							},
							method: 'GET',
							scripts: true
						});
						break;
					case 'tabpanelBouncerateReport' :
						this.bounceRateReport.getTopToolbar().items.get('tbbuttonBounceRateReportExportReport').disable();
						this.bounceRateReport.load({
							url: 'bouncerate_reportchart.php',
							params: {
								email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
								passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
								id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
								startdate: this.startDate.format('Y-m-d'),
								enddate: this.endDate.format('Y-m-d')
							},
							method: 'GET',
							scripts: true
						});
						break;
					case 'tabpanelAvgtimeReport' :
						this.avgTimeReport.getTopToolbar().items.get('tbbuttonAvgTimeReportExportReport').disable();
						this.avgTimeReport.load({
							url: 'avgtime_reportchart.php',
							params: {
								email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
								passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
								id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
								startdate: this.startDate.format('Y-m-d'),
								enddate: this.endDate.format('Y-m-d')
							},
							method: 'GET',
							scripts: true
						});
						break;
					case 'tabpanelDashboard':
						break;
					default:
						item.updateCharts(this.startDate, this.endDate);
				}
		},this)
	}, 
	dashboardPDF: function(){
		if (!this.dashboardReady){
			Ext.Ajax.request({
				url: 'dashboard_pdf.php',
				params: {
					email: Ext.state.Manager.get('AnalyticsEMail','NBRed5@googlemail.com'), 
					passwd: Ext.state.Manager.get('AnalyticsPassword','RWxpemFiZXRoMTcwOQ=='), 
					id: 'ga:' + Ext.state.Manager.get('AnalyticsAccountId','9080283'),
					startdate: this.startDate.format('Y-m-d'),
					enddate: this.endDate.format('Y-m-d')
				},
				method: 'GET',
				success: function(response, options){
					var responseData = Ext.util.JSON.decode(response.responseText);
					if (responseData.success){
						this.dashboard.getTopToolbar().items.get('tbbuttonDashboardExportReport').enable();
						this.dashboardReady = true;
					}
				},
	            failure: function(response, options){
	               	Ext.Msg.alert('Error','Unable to mark messages as Read');
			    },
			    scope: this
			});
		}
	}
});
/**
 * @author nick_brown
 */
Ext.state.Manager.setProvider(new Ext.state.CookieProvider({domain:'www.nbred5.com', expires: new Date(new Date().getTime()+(1000*60*60*24*30))}));
Ext.Ajax.timeout = 60000;
Ext.Ajax.on('requestexception', function(conn, response, options){
	switch(response.status){
		case 401:
			delete options.failure;
			Ext.Msg.alert('Error','Your Session has expired please login again', function(){
		    	Ext.state.Manager.clear('SessionID');
				window.location.reload();
			}, this);
			break;
		case 400:
			delete options.failure;
			Ext.Msg.alert('Error','Invalid Server function call please login and try again', function(){
		    	Ext.state.Manager.clear('SessionID');
				window.location.reload();
			}, this);
			break;
		case 503:
			delete options.failure;
			Ext.Msg.alert('Error','Unable to connect to Mail Server please login and try again', function(){
		    	Ext.state.Manager.clear('SessionID');
				window.location.reload();
			}, this);
			break;
	} 	
},this);
Main=function(){
    return{
		init:function(){
			Ext.QuickTips.init();
            this.accordionAnalytics = new Ext.ux.AnalyticsAccordionPanel();
			this.accordionAnalytics.on('selectaccount', function(){
				this.selectAccount();
		    	//this.centerAnalyticsPanel.visits.update({});
			},this);
			this.centerAnalyticsPanel = new Ext.ux.AnalyticsPanel();
		   	this.viewport = new Ext.Viewport({
		   		id: 'viewportMain',
		   		cls: 'x-plain',
	            layout:'border',
				monitorResize: true,
	            items:[
					this.accordionAnalytics,
					this.centerAnalyticsPanel
	             ]
	        });
		},
	    selectAccount: function(){
			this.centerAnalyticsPanel.selectAccount();
		}	
	};
}();
