X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/b37ceabb82336ee82757cd32efe353cfab8ec267..f5240829880f87e0cf581c6a296e436fdef0ef80:/ext-all-debug-w-comments.js diff --git a/ext-all-debug-w-comments.js b/ext-all-debug-w-comments.js index bd9c2bc3..f1a97491 100644 --- a/ext-all-debug-w-comments.js +++ b/ext-all-debug-w-comments.js @@ -1,281 +1,803 @@ /*! - * Ext JS Library 3.2.2 + * Ext JS Library 3.3.0 * Copyright(c) 2006-2010 Ext JS, Inc. * licensing@extjs.com * http://www.extjs.com/license */ +(function(){ + +var EXTUTIL = Ext.util, + EACH = Ext.each, + TRUE = true, + FALSE = false; /** - * @class Ext.DomHelper - *

The DomHelper class provides a layer of abstraction from DOM and transparently supports creating - * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates - * from your DOM building code.

- * - *

DomHelper element specification object

- *

A specification object is used when creating elements. Attributes of this object - * are assumed to be element attributes, except for 4 special attributes: - *

- * - *

Insertion methods

- *

Commonly used insertion methods: - *

- * - *

Example

- *

This is an example, where an unordered list with 3 children items is appended to an existing - * element with id 'my-div':
-


-var dh = Ext.DomHelper; // create shorthand alias
-// specification object
-var spec = {
-    id: 'my-ul',
-    tag: 'ul',
-    cls: 'my-list',
-    // append children after creating
-    children: [     // may also specify 'cn' instead of 'children'
-        {tag: 'li', id: 'item0', html: 'List Item 0'},
-        {tag: 'li', id: 'item1', html: 'List Item 1'},
-        {tag: 'li', id: 'item2', html: 'List Item 2'}
-    ]
-};
-var list = dh.append(
-    'my-div', // the context element 'my-div' can either be the id or the actual node
-    spec      // the specification object
-);
- 

- *

Element creation specification parameters in this class may also be passed as an Array of - * specification objects. This can be used to insert multiple sibling nodes into an existing - * container very efficiently. For example, to add more list items to the example above:


-dh.append('my-ul', [
-    {tag: 'li', id: 'item3', html: 'List Item 3'},
-    {tag: 'li', id: 'item4', html: 'List Item 4'}
-]);
- * 

- * - *

Templating

- *

The real power is in the built-in templating. Instead of creating or appending any elements, - * {@link #createTemplate} returns a Template object which can be used over and over to - * insert new elements. Revisiting the example above, we could utilize templating this time: + * @class Ext.util.Observable + * Base class that provides a common interface for publishing events. Subclasses are expected to + * to have a property "events" with all the events defined, and, optionally, a property "listeners" + * with configured listeners defined.
+ * For example: *


-// create the node
-var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
-// get template
-var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
-
-for(var i = 0; i < 5, i++){
-    tpl.append(list, [i]); // use template to append to the actual node
-}
- * 

- *

An example using a template:


-var html = '{2}';
+Employee = Ext.extend(Ext.util.Observable, {
+    constructor: function(config){
+        this.name = config.name;
+        this.addEvents({
+            "fired" : true,
+            "quit" : true
+        });
 
-var tpl = new Ext.DomHelper.createTemplate(html);
-tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack's Site"]);
-tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
- * 

- * - *

The same example using named parameters:


-var html = '{text}';
+        // Copy configured listeners into *this* object so that the base class's
+        // constructor will add them.
+        this.listeners = config.listeners;
 
-var tpl = new Ext.DomHelper.createTemplate(html);
-tpl.append('blog-roll', {
-    id: 'link1',
-    url: 'http://www.jackslocum.com/',
-    text: "Jack's Site"
+        // Call our superclass constructor to complete construction process.
+        Employee.superclass.constructor.call(this, config)
+    }
 });
-tpl.append('blog-roll', {
-    id: 'link2',
-    url: 'http://www.dustindiaz.com/',
-    text: "Dustin's Site"
+
+ * This could then be used like this:

+var newEmployee = new Employee({
+    name: employeeName,
+    listeners: {
+        quit: function() {
+            // By default, "this" will be the object that fired the event.
+            alert(this.name + " has quit!");
+        }
+    }
 });
- * 

- * - *

Compiling Templates

- *

Templates are applied using regular expressions. The performance is great, but if - * you are adding a bunch of DOM elements using the same template, you can increase - * performance even further by {@link Ext.Template#compile "compiling"} the template. - * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and - * broken up at the different variable points and a dynamic function is created and eval'ed. - * The generated function performs string concatenation of these parts and the passed - * variables instead of using regular expressions. - *


-var html = '{text}';
+
+ */ +EXTUTIL.Observable = function(){ + /** + * @cfg {Object} listeners (optional)

A config object containing one or more event handlers to be added to this + * object during initialization. This should be a valid listeners config object as specified in the + * {@link #addListener} example for attaching multiple handlers at once.

+ *

DOM events from ExtJs {@link Ext.Component Components}

+ *

While some ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this + * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s + * {@link Ext.DataView#click click} event passing the node clicked on. To access DOM + * events directly from a Component's HTMLElement, listeners must be added to the {@link Ext.Component#getEl Element} after the Component + * has been rendered. A plugin can simplify this step:


+// Plugin is configured with a listeners config object.
+// The Component is appended to the argument list of all handler functions.
+Ext.DomObserver = Ext.extend(Object, {
+    constructor: function(config) {
+        this.listeners = config.listeners ? config.listeners : config;
+    },
 
-var tpl = new Ext.DomHelper.createTemplate(html);
-tpl.compile();
+    // Component passes itself into plugin's init method
+    init: function(c) {
+        var p, l = this.listeners;
+        for (p in l) {
+            if (Ext.isFunction(l[p])) {
+                l[p] = this.createHandler(l[p], c);
+            } else {
+                l[p].fn = this.createHandler(l[p].fn, c);
+            }
+        }
 
-//... use template like normal
- * 

- * - *

Performance Boost

- *

DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead - * of DOM can significantly boost performance.

- *

Element creation specification parameters may also be strings. If {@link #useDom} is false, - * then the string is used as innerHTML. If {@link #useDom} is true, a string specification - * results in the creation of a text node. Usage:

- *

-Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
- * 
- * @singleton - */ -Ext.DomHelper = function(){ - var tempTableEl = null, - emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i, - tableRe = /^table|tbody|tr|td$/i, - confRe = /tag|children|cn|html$/i, - tableElRe = /td|tr|tbody/i, - cssRe = /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi, - endRe = /end/i, - pub, - // kill repeat to save bytes - afterbegin = 'afterbegin', - afterend = 'afterend', - beforebegin = 'beforebegin', - beforeend = 'beforeend', - ts = '', - te = '
', - tbs = ts+'', - tbe = ''+te, - trs = tbs + '', - tre = ''+tbe; + // Add the listeners to the Element immediately following the render call + c.render = c.render.{@link Function#createSequence createSequence}(function() { + var e = c.getEl(); + if (e) { + e.on(l); + } + }); + }, - // private - function doInsert(el, o, returnElement, pos, sibling, append){ - var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o)); - return returnElement ? Ext.get(newNode, true) : newNode; + createHandler: function(fn, c) { + return function(e) { + fn.call(this, e, c); + }; } +}); - // build as innerHTML where available - function createHtml(o){ - var b = '', - attr, - val, - key, - cn; +var combo = new Ext.form.ComboBox({ - if(typeof o == "string"){ - b = o; - } else if (Ext.isArray(o)) { - for (var i=0; i < o.length; i++) { - if(o[i]) { - b += createHtml(o[i]); + // Collapse combo when its element is clicked on + plugins: [ new Ext.DomObserver({ + click: function(evt, comp) { + comp.collapse(); + } + })], + store: myStore, + typeAhead: true, + mode: 'local', + triggerAction: 'all' +}); + *

+ */ + var me = this, e = me.events; + if(me.listeners){ + me.on(me.listeners); + delete me.listeners; + } + me.events = e || {}; +}; + +EXTUTIL.Observable.prototype = { + // private + filterOptRe : /^(?:scope|delay|buffer|single)$/, + + /** + *

Fires the specified event with the passed parameters (minus the event name).

+ *

An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) + * by calling {@link #enableBubble}.

+ * @param {String} eventName The name of the event to fire. + * @param {Object...} args Variable number of parameters are passed to handlers. + * @return {Boolean} returns false if any of the handlers return false otherwise it returns true. + */ + fireEvent : function(){ + var a = Array.prototype.slice.call(arguments, 0), + ename = a[0].toLowerCase(), + me = this, + ret = TRUE, + ce = me.events[ename], + cc, + q, + c; + if (me.eventsSuspended === TRUE) { + if (q = me.eventQueue) { + q.push(a); + } + } + else if(typeof ce == 'object') { + if (ce.bubble){ + if(ce.fire.apply(ce, a.slice(1)) === FALSE) { + return FALSE; } - }; - } else { - b += '<' + (o.tag = o.tag || 'div'); - for (attr in o) { - val = o[attr]; - if(!confRe.test(attr)){ - if (typeof val == "object") { - b += ' ' + attr + '="'; - for (key in val) { - b += key + ':' + val[key] + ';'; - }; - b += '"'; - }else{ - b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"'; + c = me.getBubbleTarget && me.getBubbleTarget(); + if(c && c.enableBubble) { + cc = c.events[ename]; + if(!cc || typeof cc != 'object' || !cc.bubble) { + c.enableBubble(ename); } + return c.fireEvent.apply(c, a); } - }; - // Now either just close the tag or try to add children and close the tag. - if (emptyTags.test(o.tag)) { - b += '/>'; - } else { - b += '>'; - if ((cn = o.children || o.cn)) { - b += createHtml(cn); - } else if(o.html){ - b += o.html; - } - b += ''; + } + else { + a.shift(); + ret = ce.fire.apply(ce, a); } } - return b; - } + return ret; + }, - function ieTable(depth, s, h, e){ - tempTableEl.innerHTML = [s, h, e].join(''); - var i = -1, - el = tempTableEl, - ns; - while(++i < depth){ - el = el.firstChild; - } -// If the result is multiple siblings, then encapsulate them into one fragment. - if(ns = el.nextSibling){ - var df = document.createDocumentFragment(); - while(el){ - ns = el.nextSibling; - df.appendChild(el); - el = ns; + /** + * Appends an event handler to this object. + * @param {String} eventName The name of the event to listen for. + * @param {Function} handler The method the event invokes. + * @param {Object} scope (optional) The scope (this reference) in which the handler function is executed. + * If omitted, defaults to the object which fired the event. + * @param {Object} options (optional) An object containing handler configuration. + * properties. This may contain any of the following properties:
+ *

+ * Combining Options
+ * Using the options argument, it is possible to combine different types of listeners:
+ *
+ * A delayed, one-time listener. + *


+myDataView.on('click', this.onClick, this, {
+single: true,
+delay: 100
+});
+ *

+ * Attaching multiple handlers in 1 call
+ * The method also allows for a single argument to be passed which is a config object containing properties + * which specify multiple handlers. + *

+ *


+myGridPanel.on({
+'click' : {
+    fn: this.onClick,
+    scope: this,
+    delay: 100
+},
+'mouseover' : {
+    fn: this.onMouseOver,
+    scope: this
+},
+'mouseout' : {
+    fn: this.onMouseOut,
+    scope: this
+}
+});
+ *

+ * Or a shorthand syntax:
+ *


+myGridPanel.on({
+'click' : this.onClick,
+'mouseover' : this.onMouseOver,
+'mouseout' : this.onMouseOut,
+ scope: this
+});
+ */ + addListener : function(eventName, fn, scope, o){ + var me = this, + e, + oe, + ce; + + if (typeof eventName == 'object') { + o = eventName; + for (e in o) { + oe = o[e]; + if (!me.filterOptRe.test(e)) { + me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o); + } } - el = df; + } else { + eventName = eventName.toLowerCase(); + ce = me.events[eventName] || TRUE; + if (typeof ce == 'boolean') { + me.events[eventName] = ce = new EXTUTIL.Event(me, eventName); + } + ce.addListener(fn, scope, typeof o == 'object' ? o : {}); } - return el; - } + }, /** - * @ignore - * Nasty code for IE's broken table implementation + * Removes an event handler. + * @param {String} eventName The type of event the handler was associated with. + * @param {Function} handler The handler to remove. This must be a reference to the function passed into the {@link #addListener} call. + * @param {Object} scope (optional) The scope originally specified for the handler. */ - function insertIntoTable(tag, where, el, html) { - var node, - before; - - tempTableEl = tempTableEl || document.createElement('div'); - - if(tag == 'td' && (where == afterbegin || where == beforeend) || - !tableElRe.test(tag) && (where == beforebegin || where == afterend)) { - return; + removeListener : function(eventName, fn, scope){ + var ce = this.events[eventName.toLowerCase()]; + if (typeof ce == 'object') { + ce.removeListener(fn, scope); } - before = where == beforebegin ? el : - where == afterend ? el.nextSibling : - where == afterbegin ? el.firstChild : null; + }, - if (where == beforebegin || where == afterend) { - el = el.parentNode; + /** + * Removes all listeners for this object + */ + purgeListeners : function(){ + var events = this.events, + evt, + key; + for(key in events){ + evt = events[key]; + if(typeof evt == 'object'){ + evt.clearListeners(); + } } + }, - if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) { - node = ieTable(4, trs, html, tre); - } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) || - (tag == 'tr' && (where == beforebegin || where == afterend))) { - node = ieTable(3, tbs, html, tbe); + /** + * Adds the specified events to the list of events which this Observable may fire. + * @param {Object|String} o Either an object with event names as properties with a value of true + * or the first event name string if multiple event names are being passed as separate parameters. + * @param {string} Optional. Event name if multiple event names are being passed as separate parameters. + * Usage:

+this.addEvents('storeloaded', 'storecleared');
+
+ */ + addEvents : function(o){ + var me = this; + me.events = me.events || {}; + if (typeof o == 'string') { + var a = arguments, + i = a.length; + while(i--) { + me.events[a[i]] = me.events[a[i]] || TRUE; + } } else { - node = ieTable(2, ts, html, te); + Ext.applyIf(me.events, o); } - el.insertBefore(node, before); - return node; - } - + }, - pub = { - /** - * Returns the markup for the passed Element(s) config. - * @param {Object} o The DOM object spec (and children) - * @return {String} - */ - markup : function(o){ - return createHtml(o); - }, + /** + * Checks to see if this object has any listeners for a specified event + * @param {String} eventName The name of the event to check for + * @return {Boolean} True if the event is being listened for, else false + */ + hasListener : function(eventName){ + var e = this.events[eventName.toLowerCase()]; + return typeof e == 'object' && e.listeners.length > 0; + }, - /** - * Applies a style specification to an element. - * @param {String/HTMLElement} el The element to apply styles to + /** + * Suspend the firing of all events. (see {@link #resumeEvents}) + * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired + * after the {@link #resumeEvents} call instead of discarding all suspended events; + */ + suspendEvents : function(queueSuspended){ + this.eventsSuspended = TRUE; + if(queueSuspended && !this.eventQueue){ + this.eventQueue = []; + } + }, + + /** + * Resume firing events. (see {@link #suspendEvents}) + * If events were suspended using the queueSuspended parameter, then all + * events fired during event suspension will be sent to any listeners now. + */ + resumeEvents : function(){ + var me = this, + queued = me.eventQueue || []; + me.eventsSuspended = FALSE; + delete me.eventQueue; + EACH(queued, function(e) { + me.fireEvent.apply(me, e); + }); + } +}; + +var OBSERVABLE = EXTUTIL.Observable.prototype; +/** + * Appends an event handler to this object (shorthand for {@link #addListener}.) + * @param {String} eventName The type of event to listen for + * @param {Function} handler The method the event invokes + * @param {Object} scope (optional) The scope (this reference) in which the handler function is executed. + * If omitted, defaults to the object which fired the event. + * @param {Object} options (optional) An object containing handler configuration. + * @method + */ +OBSERVABLE.on = OBSERVABLE.addListener; +/** + * Removes an event handler (shorthand for {@link #removeListener}.) + * @param {String} eventName The type of event the handler was associated with. + * @param {Function} handler The handler to remove. This must be a reference to the function passed into the {@link #addListener} call. + * @param {Object} scope (optional) The scope originally specified for the handler. + * @method + */ +OBSERVABLE.un = OBSERVABLE.removeListener; + +/** + * Removes all added captures from the Observable. + * @param {Observable} o The Observable to release + * @static + */ +EXTUTIL.Observable.releaseCapture = function(o){ + o.fireEvent = OBSERVABLE.fireEvent; +}; + +function createTargeted(h, o, scope){ + return function(){ + if(o.target == arguments[0]){ + h.apply(scope, Array.prototype.slice.call(arguments, 0)); + } + }; +}; + +function createBuffered(h, o, l, scope){ + l.task = new EXTUTIL.DelayedTask(); + return function(){ + l.task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0)); + }; +}; + +function createSingle(h, e, fn, scope){ + return function(){ + e.removeListener(fn, scope); + return h.apply(scope, arguments); + }; +}; + +function createDelayed(h, o, l, scope){ + return function(){ + var task = new EXTUTIL.DelayedTask(), + args = Array.prototype.slice.call(arguments, 0); + if(!l.tasks) { + l.tasks = []; + } + l.tasks.push(task); + task.delay(o.delay || 10, function(){ + l.tasks.remove(task); + h.apply(scope, args); + }, scope); + }; +}; + +EXTUTIL.Event = function(obj, name){ + this.name = name; + this.obj = obj; + this.listeners = []; +}; + +EXTUTIL.Event.prototype = { + addListener : function(fn, scope, options){ + var me = this, + l; + scope = scope || me.obj; + if(!me.isListening(fn, scope)){ + l = me.createListener(fn, scope, options); + if(me.firing){ // if we are currently firing this event, don't disturb the listener loop + me.listeners = me.listeners.slice(0); + } + me.listeners.push(l); + } + }, + + createListener: function(fn, scope, o){ + o = o || {}; + scope = scope || this.obj; + var l = { + fn: fn, + scope: scope, + options: o + }, h = fn; + if(o.target){ + h = createTargeted(h, o, scope); + } + if(o.delay){ + h = createDelayed(h, o, l, scope); + } + if(o.single){ + h = createSingle(h, this, fn, scope); + } + if(o.buffer){ + h = createBuffered(h, o, l, scope); + } + l.fireFn = h; + return l; + }, + + findListener : function(fn, scope){ + var list = this.listeners, + i = list.length, + l; + + scope = scope || this.obj; + while(i--){ + l = list[i]; + if(l){ + if(l.fn == fn && l.scope == scope){ + return i; + } + } + } + return -1; + }, + + isListening : function(fn, scope){ + return this.findListener(fn, scope) != -1; + }, + + removeListener : function(fn, scope){ + var index, + l, + k, + me = this, + ret = FALSE; + if((index = me.findListener(fn, scope)) != -1){ + if (me.firing) { + me.listeners = me.listeners.slice(0); + } + l = me.listeners[index]; + if(l.task) { + l.task.cancel(); + delete l.task; + } + k = l.tasks && l.tasks.length; + if(k) { + while(k--) { + l.tasks[k].cancel(); + } + delete l.tasks; + } + me.listeners.splice(index, 1); + ret = TRUE; + } + return ret; + }, + + // Iterate to stop any buffered/delayed events + clearListeners : function(){ + var me = this, + l = me.listeners, + i = l.length; + while(i--) { + me.removeListener(l[i].fn, l[i].scope); + } + }, + + fire : function(){ + var me = this, + listeners = me.listeners, + len = listeners.length, + i = 0, + l; + + if(len > 0){ + me.firing = TRUE; + var args = Array.prototype.slice.call(arguments, 0); + for (; i < len; i++) { + l = listeners[i]; + if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) { + return (me.firing = FALSE); + } + } + } + me.firing = FALSE; + return TRUE; + } + +}; +})(); +/** + * @class Ext.DomHelper + *

The DomHelper class provides a layer of abstraction from DOM and transparently supports creating + * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates + * from your DOM building code.

+ * + *

DomHelper element specification object

+ *

A specification object is used when creating elements. Attributes of this object + * are assumed to be element attributes, except for 4 special attributes: + *

+ * + *

Insertion methods

+ *

Commonly used insertion methods: + *

+ * + *

Example

+ *

This is an example, where an unordered list with 3 children items is appended to an existing + * element with id 'my-div':
+


+var dh = Ext.DomHelper; // create shorthand alias
+// specification object
+var spec = {
+    id: 'my-ul',
+    tag: 'ul',
+    cls: 'my-list',
+    // append children after creating
+    children: [     // may also specify 'cn' instead of 'children'
+        {tag: 'li', id: 'item0', html: 'List Item 0'},
+        {tag: 'li', id: 'item1', html: 'List Item 1'},
+        {tag: 'li', id: 'item2', html: 'List Item 2'}
+    ]
+};
+var list = dh.append(
+    'my-div', // the context element 'my-div' can either be the id or the actual node
+    spec      // the specification object
+);
+ 

+ *

Element creation specification parameters in this class may also be passed as an Array of + * specification objects. This can be used to insert multiple sibling nodes into an existing + * container very efficiently. For example, to add more list items to the example above:


+dh.append('my-ul', [
+    {tag: 'li', id: 'item3', html: 'List Item 3'},
+    {tag: 'li', id: 'item4', html: 'List Item 4'}
+]);
+ * 

+ * + *

Templating

+ *

The real power is in the built-in templating. Instead of creating or appending any elements, + * {@link #createTemplate} returns a Template object which can be used over and over to + * insert new elements. Revisiting the example above, we could utilize templating this time: + *


+// create the node
+var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
+// get template
+var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
+
+for(var i = 0; i < 5, i++){
+    tpl.append(list, [i]); // use template to append to the actual node
+}
+ * 

+ *

An example using a template:


+var html = '{2}';
+
+var tpl = new Ext.DomHelper.createTemplate(html);
+tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack's Site"]);
+tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
+ * 

+ * + *

The same example using named parameters:


+var html = '{text}';
+
+var tpl = new Ext.DomHelper.createTemplate(html);
+tpl.append('blog-roll', {
+    id: 'link1',
+    url: 'http://www.jackslocum.com/',
+    text: "Jack's Site"
+});
+tpl.append('blog-roll', {
+    id: 'link2',
+    url: 'http://www.dustindiaz.com/',
+    text: "Dustin's Site"
+});
+ * 

+ * + *

Compiling Templates

+ *

Templates are applied using regular expressions. The performance is great, but if + * you are adding a bunch of DOM elements using the same template, you can increase + * performance even further by {@link Ext.Template#compile "compiling"} the template. + * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and + * broken up at the different variable points and a dynamic function is created and eval'ed. + * The generated function performs string concatenation of these parts and the passed + * variables instead of using regular expressions. + *


+var html = '{text}';
+
+var tpl = new Ext.DomHelper.createTemplate(html);
+tpl.compile();
+
+//... use template like normal
+ * 

+ * + *

Performance Boost

+ *

DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead + * of DOM can significantly boost performance.

+ *

Element creation specification parameters may also be strings. If {@link #useDom} is false, + * then the string is used as innerHTML. If {@link #useDom} is true, a string specification + * results in the creation of a text node. Usage:

+ *

+Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
+ * 
+ * @singleton + */ +Ext.DomHelper = function(){ + var tempTableEl = null, + emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i, + tableRe = /^table|tbody|tr|td$/i, + confRe = /tag|children|cn|html$/i, + tableElRe = /td|tr|tbody/i, + cssRe = /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi, + endRe = /end/i, + pub, + // kill repeat to save bytes + afterbegin = 'afterbegin', + afterend = 'afterend', + beforebegin = 'beforebegin', + beforeend = 'beforeend', + ts = '', + te = '
', + tbs = ts+'', + tbe = ''+te, + trs = tbs + '', + tre = ''+tbe; + + // private + function doInsert(el, o, returnElement, pos, sibling, append){ + var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o)); + return returnElement ? Ext.get(newNode, true) : newNode; + } + + // build as innerHTML where available + function createHtml(o){ + var b = '', + attr, + val, + key, + cn; + + if(typeof o == "string"){ + b = o; + } else if (Ext.isArray(o)) { + for (var i=0; i < o.length; i++) { + if(o[i]) { + b += createHtml(o[i]); + } + }; + } else { + b += '<' + (o.tag = o.tag || 'div'); + for (attr in o) { + val = o[attr]; + if(!confRe.test(attr)){ + if (typeof val == "object") { + b += ' ' + attr + '="'; + for (key in val) { + b += key + ':' + val[key] + ';'; + }; + b += '"'; + }else{ + b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"'; + } + } + }; + // Now either just close the tag or try to add children and close the tag. + if (emptyTags.test(o.tag)) { + b += '/>'; + } else { + b += '>'; + if ((cn = o.children || o.cn)) { + b += createHtml(cn); + } else if(o.html){ + b += o.html; + } + b += ''; + } + } + return b; + } + + function ieTable(depth, s, h, e){ + tempTableEl.innerHTML = [s, h, e].join(''); + var i = -1, + el = tempTableEl, + ns; + while(++i < depth){ + el = el.firstChild; + } +// If the result is multiple siblings, then encapsulate them into one fragment. + if(ns = el.nextSibling){ + var df = document.createDocumentFragment(); + while(el){ + ns = el.nextSibling; + df.appendChild(el); + el = ns; + } + el = df; + } + return el; + } + + /** + * @ignore + * Nasty code for IE's broken table implementation + */ + function insertIntoTable(tag, where, el, html) { + var node, + before; + + tempTableEl = tempTableEl || document.createElement('div'); + + if(tag == 'td' && (where == afterbegin || where == beforeend) || + !tableElRe.test(tag) && (where == beforebegin || where == afterend)) { + return; + } + before = where == beforebegin ? el : + where == afterend ? el.nextSibling : + where == afterbegin ? el.firstChild : null; + + if (where == beforebegin || where == afterend) { + el = el.parentNode; + } + + if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) { + node = ieTable(4, trs, html, tre); + } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) || + (tag == 'tr' && (where == beforebegin || where == afterend))) { + node = ieTable(3, tbs, html, tbe); + } else { + node = ieTable(2, ts, html, te); + } + el.insertBefore(node, before); + return node; + } + + + pub = { + /** + * Returns the markup for the passed Element(s) config. + * @param {Object} o The DOM object spec (and children) + * @return {String} + */ + markup : function(o){ + return createHtml(o); + }, + + /** + * Applies a style specification to an element. + * @param {String/HTMLElement} el The element to apply styles to * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or * a function which returns such a specification. */ @@ -288,6 +810,13 @@ Ext.DomHelper = function(){ styles = styles.call(); } if (typeof styles == "string") { + /** + * Since we're using the g flag on the regex, we need to set the lastIndex. + * This automatically happens on some implementations, but not others, see: + * http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls + * http://blog.stevenlevithan.com/archives/fixing-javascript-regexp + */ + cssRe.lastIndex = 0; while ((matches = cssRe.exec(styles))) { el.setStyle(matches[1], matches[2]); } @@ -417,154 +946,6 @@ Ext.DomHelper = function(){ }; return pub; }(); -/** - * @class Ext.DomHelper - */ -Ext.apply(Ext.DomHelper, -function(){ - var pub, - afterbegin = 'afterbegin', - afterend = 'afterend', - beforebegin = 'beforebegin', - beforeend = 'beforeend', - confRe = /tag|children|cn|html$/i; - - // private - function doInsert(el, o, returnElement, pos, sibling, append){ - el = Ext.getDom(el); - var newNode; - if (pub.useDom) { - newNode = createDom(o, null); - if (append) { - el.appendChild(newNode); - } else { - (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el); - } - } else { - newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o)); - } - return returnElement ? Ext.get(newNode, true) : newNode; - } - - // build as dom - /** @ignore */ - function createDom(o, parentNode){ - var el, - doc = document, - useSet, - attr, - val, - cn; - - if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted - el = doc.createDocumentFragment(); // in one shot using a DocumentFragment - for (var i = 0, l = o.length; i < l; i++) { - createDom(o[i], el); - } - } else if (typeof o == 'string') { // Allow a string as a child spec. - el = doc.createTextNode(o); - } else { - el = doc.createElement( o.tag || 'div' ); - useSet = !!el.setAttribute; // In IE some elements don't have setAttribute - for (var attr in o) { - if(!confRe.test(attr)){ - val = o[attr]; - if(attr == 'cls'){ - el.className = val; - }else{ - if(useSet){ - el.setAttribute(attr, val); - }else{ - el[attr] = val; - } - } - } - } - Ext.DomHelper.applyStyles(el, o.style); - - if ((cn = o.children || o.cn)) { - createDom(cn, el); - } else if (o.html) { - el.innerHTML = o.html; - } - } - if(parentNode){ - parentNode.appendChild(el); - } - return el; - } - - pub = { - /** - * Creates a new Ext.Template from the DOM object spec. - * @param {Object} o The DOM object spec (and children) - * @return {Ext.Template} The new template - */ - createTemplate : function(o){ - var html = Ext.DomHelper.createHtml(o); - return new Ext.Template(html); - }, - - /** True to force the use of DOM instead of html fragments @type Boolean */ - useDom : false, - - /** - * Creates new DOM element(s) and inserts them before el. - * @param {Mixed} el The context element - * @param {Object/String} o The DOM object spec (and children) or raw HTML blob - * @param {Boolean} returnElement (optional) true to return a Ext.Element - * @return {HTMLElement/Ext.Element} The new node - * @hide (repeat) - */ - insertBefore : function(el, o, returnElement){ - return doInsert(el, o, returnElement, beforebegin); - }, - - /** - * Creates new DOM element(s) and inserts them after el. - * @param {Mixed} el The context element - * @param {Object} o The DOM object spec (and children) - * @param {Boolean} returnElement (optional) true to return a Ext.Element - * @return {HTMLElement/Ext.Element} The new node - * @hide (repeat) - */ - insertAfter : function(el, o, returnElement){ - return doInsert(el, o, returnElement, afterend, 'nextSibling'); - }, - - /** - * Creates new DOM element(s) and inserts them as the first child of el. - * @param {Mixed} el The context element - * @param {Object/String} o The DOM object spec (and children) or raw HTML blob - * @param {Boolean} returnElement (optional) true to return a Ext.Element - * @return {HTMLElement/Ext.Element} The new node - * @hide (repeat) - */ - insertFirst : function(el, o, returnElement){ - return doInsert(el, o, returnElement, afterbegin, 'firstChild'); - }, - - /** - * Creates new DOM element(s) and appends them to el. - * @param {Mixed} el The context element - * @param {Object/String} o The DOM object spec (and children) or raw HTML blob - * @param {Boolean} returnElement (optional) true to return a Ext.Element - * @return {HTMLElement/Ext.Element} The new node - * @hide (repeat) - */ - append: function(el, o, returnElement){ - return doInsert(el, o, returnElement, beforeend, '', true); - }, - - /** - * Creates new DOM element(s) without inserting them to the document. - * @param {Object/String} o The DOM object spec (and children) or raw HTML blob - * @return {HTMLElement} The new uninserted node - */ - createDom: createDom - }; - return pub; -}()); /** * @class Ext.Template *

Represents an HTML fragment template. Templates may be {@link #compile precompiled} @@ -805,137 +1186,6 @@ Ext.Template.from = function(el, config){ el = Ext.getDom(el); return new Ext.Template(el.value || el.innerHTML, config || ''); }; -/** - * @class Ext.Template - */ -Ext.apply(Ext.Template.prototype, { - /** - * @cfg {Boolean} disableFormats Specify true to disable format - * functions in the template. If the template does not contain - * {@link Ext.util.Format format functions}, setting disableFormats - * to true will reduce {@link #apply} time. Defaults to false. - *


-var t = new Ext.Template(
-    '<div name="{id}">',
-        '<span class="{cls}">{name} {value}</span>',
-    '</div>',
-    {
-        compiled: true,      // {@link #compile} immediately
-        disableFormats: true // reduce {@link #apply} time since no formatting
-    }
-);
-     * 
- * For a list of available format functions, see {@link Ext.util.Format}. - */ - disableFormats : false, - /** - * See {@link #disableFormats}. - * @type Boolean - * @property disableFormats - */ - - /** - * The regular expression used to match template variables - * @type RegExp - * @property - * @hide repeat doc - */ - re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g, - argsRe : /^\s*['"](.*)["']\s*$/, - compileARe : /\\/g, - compileBRe : /(\r\n|\n)/g, - compileCRe : /'/g, - - /** - * Returns an HTML fragment of this template with the specified values applied. - * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}) - * @return {String} The HTML fragment - * @hide repeat doc - */ - applyTemplate : function(values){ - var me = this, - useF = me.disableFormats !== true, - fm = Ext.util.Format, - tpl = me; - - if(me.compiled){ - return me.compiled(values); - } - function fn(m, name, format, args){ - if (format && useF) { - if (format.substr(0, 5) == "this.") { - return tpl.call(format.substr(5), values[name], values); - } else { - if (args) { - // quoted values are required for strings in compiled templates, - // but for non compiled we need to strip them - // quoted reversed for jsmin - var re = me.argsRe; - args = args.split(','); - for(var i = 0, len = args.length; i < len; i++){ - args[i] = args[i].replace(re, "$1"); - } - args = [values[name]].concat(args); - } else { - args = [values[name]]; - } - return fm[format].apply(fm, args); - } - } else { - return values[name] !== undefined ? values[name] : ""; - } - } - return me.html.replace(me.re, fn); - }, - - /** - * Compiles the template into an internal function, eliminating the RegEx overhead. - * @return {Ext.Template} this - * @hide repeat doc - */ - compile : function(){ - var me = this, - fm = Ext.util.Format, - useF = me.disableFormats !== true, - sep = Ext.isGecko ? "+" : ",", - body; - - function fn(m, name, format, args){ - if(format && useF){ - args = args ? ',' + args : ""; - if(format.substr(0, 5) != "this."){ - format = "fm." + format + '('; - }else{ - format = 'this.call("'+ format.substr(5) + '", '; - args = ", values"; - } - }else{ - args= ''; format = "(values['" + name + "'] == undefined ? '' : "; - } - return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'"; - } - - // branched to use + in gecko and [].join() in others - if(Ext.isGecko){ - body = "this.compiled = function(values){ return '" + - me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn) + - "';};"; - }else{ - body = ["this.compiled = function(values){ return ['"]; - body.push(me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn)); - body.push("'].join('');};"); - body = body.join(''); - } - eval(body); - return me; - }, - - // private function used to call members - call : function(fnName, value, allValues){ - return this[fnName](value, allValues); - } -}); -Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; /* * This is code is also distributed under MIT license for use * with jQuery and prototype JavaScript libraries. @@ -967,7 +1217,7 @@ All selectors, attribute filters and pseudos below can be combined infinitely in
  • E[foo$=bar] has an attribute "foo" that ends with "bar"
  • E[foo*=bar] has an attribute "foo" that contains the substring "bar"
  • E[foo%=2] has an attribute "foo" that is evenly divisible by 2
  • -
  • E[foo!=bar] has an attribute "foo" that does not equal "bar"
  • +
  • E[foo!=bar] attribute "foo" does not equal "bar"
  • Pseudo Classes:

    + * @return {CompositeElement} this + */ + filter : function(selector){ + var els = [], + me = this, + fn = Ext.isFunction(selector) ? selector + : function(el){ + return el.is(selector); + }; + + me.each(function(el, self, i) { + if (fn(el, i) !== false) { + els[els.length] = me.transformElement(el); + } + }); + + me.elements = els; + return me; + }, + + /** + * Find the index of the passed element within the composite collection. + * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection. + * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found. + */ + indexOf : function(el){ + return this.elements.indexOf(this.transformElement(el)); + }, + + /** + * Replaces the specified element with the passed element. + * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite + * to replace. + * @param {Mixed} replacement The id of an element or the Element itself. + * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too. + * @return {CompositeElement} this + */ + replaceElement : function(el, replacement, domReplace){ + var index = !isNaN(el) ? el : this.indexOf(el), + d; + if(index > -1){ + replacement = Ext.getDom(replacement); + if(domReplace){ + d = this.elements[index]; + d.parentNode.insertBefore(replacement, d); + Ext.removeNode(d); + } + this.elements.splice(index, 1, replacement); + } + return this; + }, + + /** + * Removes all elements. + */ + clear : function(){ + this.elements = []; + } +}; + +Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener; + +/** + * @private + * Copies all of the functions from Ext.Element's prototype onto CompositeElementLite's prototype. + * This is called twice - once immediately below, and once again after additional Ext.Element + * are added in Ext JS + */ +Ext.CompositeElementLite.importElementMethods = function() { + var fnName, + ElProto = Ext.Element.prototype, + CelProto = Ext.CompositeElementLite.prototype; + + for (fnName in ElProto) { + if (typeof ElProto[fnName] == 'function'){ + (function(fnName) { + CelProto[fnName] = CelProto[fnName] || function() { + return this.invoke(fnName, arguments); + }; + }).call(CelProto, fnName); + + } + } +}; + +Ext.CompositeElementLite.importElementMethods(); + +if(Ext.DomQuery){ + Ext.Element.selectorFunction = Ext.DomQuery.select; +} + +/** + * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods + * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or + * {@link Ext.CompositeElementLite CompositeElementLite} object. + * @param {String/Array} selector The CSS selector or an array of elements + * @param {HTMLElement/String} root (optional) The root element of the query or id of the root + * @return {CompositeElementLite/CompositeElement} + * @member Ext.Element + * @method select + */ +Ext.Element.select = function(selector, root){ + var els; + if(typeof selector == "string"){ + els = Ext.Element.selectorFunction(selector, root); + }else if(selector.length !== undefined){ + els = selector; + }else{ + throw "Invalid selector"; + } + return new Ext.CompositeElementLite(els); +}; +/** + * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods + * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or + * {@link Ext.CompositeElementLite CompositeElementLite} object. + * @param {String/Array} selector The CSS selector or an array of elements + * @param {HTMLElement/String} root (optional) The root element of the query or id of the root + * @return {CompositeElementLite/CompositeElement} + * @member Ext + * @method select + */ +Ext.select = Ext.Element.select; +(function(){ + var BEFOREREQUEST = "beforerequest", + REQUESTCOMPLETE = "requestcomplete", + REQUESTEXCEPTION = "requestexception", + UNDEFINED = undefined, + LOAD = 'load', + POST = 'POST', + GET = 'GET', + WINDOW = window; + + /** + * @class Ext.data.Connection + * @extends Ext.util.Observable + *

    The class encapsulates a connection to the page's originating domain, allowing requests to be made + * either to a configured URL, or to a URL specified at request time.

    + *

    Requests made by this class are asynchronous, and will return immediately. No data from + * the server will be available to the statement immediately following the {@link #request} call. + * To process returned data, use a + * success callback + * in the request options object, + * or an {@link #requestcomplete event listener}.

    + *

    File Uploads

    File uploads are not performed using normal "Ajax" techniques, that + * is they are not performed using XMLHttpRequests. Instead the form is submitted in the standard + * manner with the DOM <form> element temporarily modified to have its + * target set to refer + * to a dynamically generated, hidden <iframe> which is inserted into the document + * but removed after the return data has been gathered.

    + *

    The server response is parsed by the browser to create the document for the IFRAME. If the + * server is using JSON to send the return object, then the + * Content-Type header + * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.

    + *

    Characters which are significant to an HTML parser must be sent as HTML entities, so encode + * "<" as "&lt;", "&" as "&amp;" etc.

    + *

    The response text is retrieved from the document, and a fake XMLHttpRequest object + * is created containing a responseText property in order to conform to the + * requirements of event handlers and callbacks.

    + *

    Be aware that file upload packets are sent with the content type multipart/form + * and some server technologies (notably JEE) may require some custom processing in order to + * retrieve parameter names and parameter values from the packet content.

    + *

    Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.

    + * @constructor + * @param {Object} config a configuration object. + */ + Ext.data.Connection = function(config){ + Ext.apply(this, config); + this.addEvents( + /** + * @event beforerequest + * Fires before a network request is made to retrieve a data object. + * @param {Connection} conn This Connection object. + * @param {Object} options The options config object passed to the {@link #request} method. + */ + BEFOREREQUEST, + /** + * @event requestcomplete + * Fires if the request was successfully completed. + * @param {Connection} conn This Connection object. + * @param {Object} response The XHR object containing the response data. + * See The XMLHttpRequest Object + * for details. + * @param {Object} options The options config object passed to the {@link #request} method. + */ + REQUESTCOMPLETE, + /** + * @event requestexception + * Fires if an error HTTP status was returned from the server. + * See HTTP Status Code Definitions + * for details of HTTP status codes. + * @param {Connection} conn This Connection object. + * @param {Object} response The XHR object containing the response data. + * See The XMLHttpRequest Object + * for details. + * @param {Object} options The options config object passed to the {@link #request} method. + */ + REQUESTEXCEPTION + ); + Ext.data.Connection.superclass.constructor.call(this); + }; + + Ext.extend(Ext.data.Connection, Ext.util.Observable, { + /** + * @cfg {String} url (Optional)

    The default URL to be used for requests to the server. Defaults to undefined.

    + *

    The url config may be a function which returns the URL to use for the Ajax request. The scope + * (this reference) of the function is the scope option passed to the {@link #request} method.

    + */ + /** + * @cfg {Object} extraParams (Optional) An object containing properties which are used as + * extra parameters to each request made by this object. (defaults to undefined) + */ + /** + * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added + * to each request made by this object. (defaults to undefined) + */ + /** + * @cfg {String} method (Optional) The default HTTP method to be used for requests. + * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used; + * otherwise, GET will be used.) + */ + /** + * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000) + */ + timeout : 30000, + /** + * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false) + * @type Boolean + */ + autoAbort:false, + + /** + * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true) + * @type Boolean + */ + disableCaching: true, + + /** + * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching + * through a cache buster. Defaults to '_dc' + * @type String + */ + disableCachingParam: '_dc', + + /** + *

    Sends an HTTP request to a remote server.

    + *

    Important: Ajax server requests are asynchronous, and this call will + * return before the response has been received. Process any returned data + * in a callback function.

    + *
    
    +Ext.Ajax.request({
    +   url: 'ajax_demo/sample.json',
    +   success: function(response, opts) {
    +      var obj = Ext.decode(response.responseText);
    +      console.dir(obj);
    +   },
    +   failure: function(response, opts) {
    +      console.log('server-side failure with status code ' + response.status);
    +   }
    +});
    +         * 
    + *

    To execute a callback function in the correct scope, use the scope option.

    + * @param {Object} options An object which may contain the following properties:

    + *

    The options object may also contain any other property which might be needed to perform + * postprocessing in a callback because it is passed to callback functions.

    + * @return {Number} transactionId The id of the server transaction. This may be used + * to cancel the request. + */ + request : function(o){ + var me = this; + if(me.fireEvent(BEFOREREQUEST, me, o)){ + if (o.el) { + if(!Ext.isEmpty(o.indicatorText)){ + me.indicatorText = '
    '+o.indicatorText+"
    "; + } + if(me.indicatorText) { + Ext.getDom(o.el).innerHTML = me.indicatorText; + } + o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) { + Ext.getDom(o.el).innerHTML = response.responseText; + }); + } + + var p = o.params, + url = o.url || me.url, + method, + cb = {success: me.handleResponse, + failure: me.handleFailure, + scope: me, + argument: {options: o}, + timeout : Ext.num(o.timeout, me.timeout) + }, + form, + serForm; + + + if (Ext.isFunction(p)) { + p = p.call(o.scope||WINDOW, o); + } + + p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p); + + if (Ext.isFunction(url)) { + url = url.call(o.scope || WINDOW, o); + } + + if((form = Ext.getDom(o.form))){ + url = url || form.action; + if(o.isUpload || (/multipart\/form-data/i.test(form.getAttribute("enctype")))) { + return me.doFormUpload.call(me, o, p, url); + } + serForm = Ext.lib.Ajax.serializeForm(form); + p = p ? (p + '&' + serForm) : serForm; + } - // first validate right/bottom - if((x + w) > vr){ - x = vr - w; - moved = true; + method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET); + + if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){ + var dcp = o.disableCachingParam || me.disableCachingParam; + url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime())); + } + + o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {}); + + if(o.autoAbort === true || me.autoAbort) { + me.abort(); + } + + if((method == GET || o.xmlData || o.jsonData) && p){ + url = Ext.urlAppend(url, p); + p = ''; + } + return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o)); + }else{ + return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null; } - if((y + h) > vb){ - y = vb - h; - moved = true; + }, + + /** + * Determine whether this object has a request outstanding. + * @param {Number} transactionId (Optional) defaults to the last transaction + * @return {Boolean} True if there is an outstanding request. + */ + isLoading : function(transId){ + return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId; + }, + + /** + * Aborts any outstanding request. + * @param {Number} transactionId (Optional) defaults to the last transaction + */ + abort : function(transId){ + if(transId || this.isLoading()){ + Ext.lib.Ajax.abort(transId || this.transId); } - // then make sure top/left isn't negative - if(x < vx){ - x = vx; - moved = true; + }, + + // private + handleResponse : function(response){ + this.transId = false; + var options = response.argument.options; + response.argument = options ? options.argument : null; + this.fireEvent(REQUESTCOMPLETE, this, response, options); + if(options.success){ + options.success.call(options.scope, response, options); } - if(y < vy){ - y = vy; - moved = true; + if(options.callback){ + options.callback.call(options.scope, options, true, response); } - return moved ? [x, y] : false; - }; - }(), - - - -// el = Ext.get(el); -// offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0}); + }, -// var me = this, -// doc = document, -// s = el.getScroll(), -// vxy = el.getXY(), -// vx = offsets.left + s.left, -// vy = offsets.top + s.top, -// vw = -offsets.right, -// vh = -offsets.bottom, -// vr, -// vb, -// xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]), -// x = xy[0], -// y = xy[1], -// w = me.dom.offsetWidth, h = me.dom.offsetHeight, -// moved = false; // only move it if it needs it -// -// -// if(el.dom == doc.body || el.dom == doc){ -// vw += Ext.lib.Dom.getViewWidth(); -// vh += Ext.lib.Dom.getViewHeight(); -// }else{ -// vw += el.dom.clientWidth; -// vh += el.dom.clientHeight; -// if(!local){ -// vx += vxy[0]; -// vy += vxy[1]; -// } -// } + // private + handleFailure : function(response, e){ + this.transId = false; + var options = response.argument.options; + response.argument = options ? options.argument : null; + this.fireEvent(REQUESTEXCEPTION, this, response, options, e); + if(options.failure){ + options.failure.call(options.scope, response, options); + } + if(options.callback){ + options.callback.call(options.scope, options, false, response); + } + }, -// // first validate right/bottom -// if(x + w > vx + vw){ -// x = vx + vw - w; -// moved = true; -// } -// if(y + h > vy + vh){ -// y = vy + vh - 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; -// }, + // private + doFormUpload : function(o, ps, url){ + var id = Ext.id(), + doc = document, + frame = doc.createElement('iframe'), + form = Ext.getDom(o.form), + hiddens = [], + hd, + encoding = 'multipart/form-data', + buf = { + target: form.target, + method: form.method, + encoding: form.encoding, + enctype: form.enctype, + action: form.action + }; - // private, used internally - getConstrainOffset : function(){ - return 0; - }, - - /** - * Calculates the x, y to center this element on the screen - * @return {Array} The x, y values [x, y] - */ - getCenterXY : function(){ - return this.getAlignToXY(document, 'c-c'); - }, + /* + * Originally this behaviour was modified for Opera 10 to apply the secure URL after + * the frame had been added to the document. It seems this has since been corrected in + * Opera so the behaviour has been reverted, the URL will be set before being added. + */ + Ext.fly(frame).set({ + id: id, + name: id, + cls: 'x-hidden', + src: Ext.SSL_SECURE_URL + }); - /** - * 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 || document, 'c-c'); - } -}); -/** - * @class Ext.Element - */ -Ext.Element.addMethods(function(){ - var PARENTNODE = 'parentNode', - NEXTSIBLING = 'nextSibling', - PREVIOUSSIBLING = 'previousSibling', - DQ = Ext.DomQuery, - GET = Ext.get; - - return { - /** - * 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, - b = document.body, - depth = 0, - stopEl; - if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') { - return null; + doc.body.appendChild(frame); + + // This is required so that IE doesn't pop the response up in a new window. + if(Ext.isIE){ + document.frames[id].name = id; } - maxDepth = maxDepth || 50; - if (isNaN(maxDepth)) { - stopEl = Ext.getDom(maxDepth); - maxDepth = Number.MAX_VALUE; - } - while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){ - if(DQ.is(p, simpleSelector)){ - return returnEl ? GET(p) : p; - } - depth++; - p = p.parentNode; - } - return null; - }, - - /** - * Looks 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 10 || 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) - */ - findParentNode : function(simpleSelector, maxDepth, returnEl){ - var p = Ext.fly(this.dom.parentNode, '_internal'); - return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null; - }, - - /** - * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child). - * This is a shortcut for findParentNode() that always returns an Ext.Element. - * @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 10 || document.body) - * @return {Ext.Element} The matching DOM node (or null if no match was found) - */ - up : function(simpleSelector, maxDepth){ - return this.findParentNode(simpleSelector, maxDepth, true); - }, - - /** - * 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 - * @return {CompositeElement/CompositeElementLite} The composite element - */ - select : function(selector){ - return Ext.Element.select(selector, this.dom); - }, - - /** - * Selects 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){ - return DQ.select(selector, this.dom); - }, - - /** - * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id). - * @param {String} selector The CSS selector - * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false) - * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true) - */ - child : function(selector, returnDom){ - var n = DQ.selectNode(selector, this.dom); - return returnDom ? n : GET(n); - }, - - /** - * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id). - * @param {String} selector The CSS selector - * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false) - * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true) - */ - down : function(selector, returnDom){ - var n = DQ.selectNode(" > " + selector, this.dom); - return returnDom ? n : GET(n); - }, - - /** - * Gets the parent node for this element, optionally chaining up trying to match a selector - * @param {String} selector (optional) Find a parent node that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element - * @return {Ext.Element/HTMLElement} The parent node or null - */ - parent : function(selector, returnDom){ - return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom); - }, - - /** - * Gets the next sibling, skipping text nodes - * @param {String} selector (optional) Find the next sibling that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element - * @return {Ext.Element/HTMLElement} The next sibling or null - */ - next : function(selector, returnDom){ - return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom); - }, - - /** - * Gets the previous sibling, skipping text nodes - * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element - * @return {Ext.Element/HTMLElement} The previous sibling or null - */ - prev : function(selector, returnDom){ - return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom); - }, - - - /** - * Gets the first child, skipping text nodes - * @param {String} selector (optional) Find the next sibling that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element - * @return {Ext.Element/HTMLElement} The first child or null - */ - first : function(selector, returnDom){ - return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom); - }, - - /** - * Gets the last child, skipping text nodes - * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector - * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element - * @return {Ext.Element/HTMLElement} The last child or null - */ - last : function(selector, returnDom){ - return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom); - }, - - matchNode : function(dir, start, selector, returnDom){ - var n = this.dom[start]; - while(n){ - if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){ - return !returnDom ? GET(n) : n; - } - n = n[dir]; - } - return null; - } - } -}());/** - * @class Ext.Element + + + Ext.fly(form).set({ + target: id, + method: POST, + enctype: encoding, + encoding: encoding, + action: url || buf.action + }); + + // add dynamic params + Ext.iterate(Ext.urlDecode(ps, false), function(k, v){ + hd = doc.createElement('input'); + Ext.fly(hd).set({ + type: 'hidden', + value: v, + name: k + }); + form.appendChild(hd); + hiddens.push(hd); + }); + + function cb(){ + var me = this, + // bogus response object + r = {responseText : '', + responseXML : null, + argument : o.argument}, + doc, + firstChild; + + try{ + doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document; + if(doc){ + if(doc.body){ + if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea + r.responseText = firstChild.value; + }else{ + r.responseText = doc.body.innerHTML; + } + } + //in IE the document may still have a body even if returns XML. + r.responseXML = doc.XMLDocument || doc; + } + } + catch(e) {} + + Ext.EventManager.removeListener(frame, LOAD, cb, me); + + me.fireEvent(REQUESTCOMPLETE, me, r, o); + + function runCallback(fn, scope, args){ + if(Ext.isFunction(fn)){ + fn.apply(scope, args); + } + } + + runCallback(o.success, o.scope, [r, o]); + runCallback(o.callback, o.scope, [o, true, r]); + + if(!me.debugUploads){ + setTimeout(function(){Ext.removeNode(frame);}, 100); + } + } + + Ext.EventManager.on(frame, LOAD, cb, this); + form.submit(); + + Ext.fly(form).set(buf); + Ext.each(hiddens, function(h) { + Ext.removeNode(h); + }); + } + }); +})(); + +/** + * @class Ext.Ajax + * @extends Ext.data.Connection + *

    The global Ajax request class that provides a simple way to make Ajax requests + * with maximum flexibility.

    + *

    Since Ext.Ajax is a singleton, you can set common properties/events for it once + * and override them at the request function level only if necessary.

    + *

    Common Properties you may want to set are:

    + *
    
    +// Default headers to pass in every request
    +Ext.Ajax.defaultHeaders = {
    +    'Powered-By': 'Ext'
    +};
    + * 
    + *

    + *

    Common Events you may want to set are:

    + *
    
    +// Example: show a spinner during all Ajax requests
    +Ext.Ajax.on('beforerequest', this.showSpinner, this);
    +Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
    +Ext.Ajax.on('requestexception', this.hideSpinner, this);
    + * 
    + *

    + *

    An example request:

    + *
    
    +// Basic request
    +Ext.Ajax.{@link Ext.data.Connection#request request}({
    +   url: 'foo.php',
    +   success: someFn,
    +   failure: otherFn,
    +   headers: {
    +       'my-header': 'foo'
    +   },
    +   params: { foo: 'bar' }
    +});
    +
    +// Simple ajax form submission
    +Ext.Ajax.{@link Ext.data.Connection#request request}({
    +    form: 'some-form',
    +    params: 'foo=bar'
    +});
    + * 
    + *

    + * @singleton */ -Ext.Element.addMethods({ +Ext.Ajax = new Ext.data.Connection({ + /** + * @cfg {String} url @hide + */ + /** + * @cfg {Object} extraParams @hide + */ + /** + * @cfg {Object} defaultHeaders @hide + */ + /** + * @cfg {String} method (Optional) @hide + */ + /** + * @cfg {Number} timeout (Optional) @hide + */ + /** + * @cfg {Boolean} autoAbort (Optional) @hide + */ + + /** + * @cfg {Boolean} disableCaching (Optional) @hide + */ + + /** + * @property disableCaching + * True to add a unique cache-buster param to GET requests. (defaults to true) + * @type Boolean + */ /** - * 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 {CompositeElement/CompositeElementLite} The composite element + * @property url + * The default URL to be used for requests to the server. (defaults to undefined) + * If the server receives all requests through one URL, setting this once is easier than + * entering it on every request. + * @type String */ - select : function(selector, unique){ - return Ext.Element.select(selector, unique, this.dom); + /** + * @property extraParams + * An object containing properties which are used as extra parameters to each request made + * by this object (defaults to undefined). Session information and other data that you need + * to pass with each request are commonly put here. + * @type Object + */ + /** + * @property defaultHeaders + * An object containing request headers which are added to each request made by this object + * (defaults to undefined). + * @type Object + */ + /** + * @property method + * The default HTTP method to be used for requests. Note that this is case-sensitive and + * should be all caps (defaults to undefined; if not set but params are present will use + * "POST", otherwise will use "GET".) + * @type String + */ + /** + * @property timeout + * The timeout in milliseconds to be used for requests. (defaults to 30000) + * @type Number + */ + + /** + * @property autoAbort + * Whether a new request should abort any pending requests. (defaults to false) + * @type Boolean + */ + autoAbort : false, + + /** + * Serialize the passed form into a url encoded string + * @param {String/HTMLElement} form + * @return {String} + */ + serializeForm : function(form){ + return Ext.lib.Ajax.serializeForm(form); } -});/** - * @class Ext.Element +}); +/** + * @class Ext.util.JSON + * Modified version of Douglas Crockford"s json.js that doesn"t + * mess with the Object prototype + * http://www.json.org/js.html + * @singleton */ -Ext.Element.addMethods( -function() { - var GETDOM = Ext.getDom, - GET = Ext.get, - DH = Ext.DomHelper; - - return { - /** - * Appends the passed element(s) to this element - * @param {String/HTMLElement/Array/Element/CompositeElement} el - * @return {Ext.Element} this - */ - appendChild: function(el){ - return GET(el).appendTo(this); - }, - - /** - * Appends this element to the passed element - * @param {Mixed} el The new parent element - * @return {Ext.Element} this - */ - appendTo: function(el){ - GETDOM(el).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 - * @return {Ext.Element} this - */ - insertBefore: function(el){ - (el = GETDOM(el)).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 - * @return {Ext.Element} this - */ - insertAfter: function(el){ - (el = GETDOM(el)).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 - * @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); +Ext.util.JSON = new (function(){ + var useHasOwn = !!{}.hasOwnProperty, + isNative = function() { + var useNative = null; + + return function() { + if (useNative === null) { + useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]'; + } + + return useNative; + }; + }(), + pad = function(n) { + return n < 10 ? "0" + n : n; + }, + doDecode = function(json){ + return eval("(" + json + ")"); + }, + doEncode = function(o){ + if(!Ext.isDefined(o) || o === null){ + return "null"; + }else if(Ext.isArray(o)){ + return encodeArray(o); + }else if(Ext.isDate(o)){ + return Ext.util.JSON.encodeDate(o); + }else if(Ext.isString(o)){ + return encodeString(o); + }else if(typeof o == "number"){ + //don't use isNumber here, since finite checks happen inside isNumber + return isFinite(o) ? String(o) : "null"; + }else if(Ext.isBoolean(o)){ + return String(o); + }else { + var a = ["{"], b, i, v; + for (i in o) { + // don't encode DOM objects + if(!o.getElementsByTagName){ + if(!useHasOwn || o.hasOwnProperty(i)) { + v = o[i]; + switch (typeof v) { + case "undefined": + case "function": + case "unknown": + break; + default: + if(b){ + a.push(','); + } + a.push(doEncode(i), ":", + v === null ? "null" : doEncode(v)); + b = true; + } + } + } + } + a.push("}"); + return a.join(""); + } + }, + m = { + "\b": '\\b', + "\t": '\\t', + "\n": '\\n', + "\f": '\\f', + "\r": '\\r', + '"' : '\\"', + "\\": '\\\\' + }, + encodeString = function(s){ + if (/["\\\x00-\x1f]/.test(s)) { + return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) { + var c = m[b]; + if(c){ + return c; + } + c = b.charCodeAt(); + return "\\u00" + + Math.floor(c / 16).toString(16) + + (c % 16).toString(16); + }) + '"'; } + return '"' + s + '"'; }, - - /** - * Replaces the passed element with this element - * @param {Mixed} el The element to replace - * @return {Ext.Element} this - */ - replace: function(el){ - el = GET(el); - 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 - * @return {Ext.Element} this - */ - replaceWith: function(el){ - var me = this; - - if(el.nodeType || el.dom || typeof el == 'string'){ - el = GETDOM(el); - me.dom.parentNode.insertBefore(el, me.dom); - }else{ - el = DH.insertBefore(me.dom, el); + encodeArray = function(o){ + var a = ["["], b, i, l = o.length, v; + for (i = 0; i < l; i += 1) { + v = o[i]; + switch (typeof v) { + case "undefined": + case "function": + case "unknown": + break; + default: + if (b) { + a.push(','); + } + a.push(v === null ? "null" : Ext.util.JSON.encode(v)); + b = true; + } + } + a.push("]"); + return a.join(""); + }; + + /** + *

    Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression. + * The returned value includes enclosing double quotation marks.

    + *

    The default return format is "yyyy-mm-ddThh:mm:ss".

    + *

    To override this:

    
    +Ext.util.JSON.encodeDate = function(d) {
    +    return d.format('"Y-m-d"');
    +};
    +
    + * @param {Date} d The Date to encode + * @return {String} The string literal to use in a JSON string. + */ + this.encodeDate = function(o){ + return '"' + o.getFullYear() + "-" + + pad(o.getMonth() + 1) + "-" + + pad(o.getDate()) + "T" + + pad(o.getHours()) + ":" + + pad(o.getMinutes()) + ":" + + pad(o.getSeconds()) + '"'; + }; + + /** + * Encodes an Object, Array or other value + * @param {Mixed} o The variable to encode + * @return {String} The JSON string + */ + this.encode = function() { + var ec; + return function(o) { + if (!ec) { + // setup encoding function on first access + ec = isNative() ? JSON.stringify : doEncode; } - - delete Ext.elCache[me.id]; - Ext.removeNode(me.dom); - me.id = Ext.id(me.dom = el); - Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me); - return me; - }, - - /** - * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element. - * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be - * automatically generated with the specified attributes. - * @param {HTMLElement} insertBefore (optional) a child element of this element - * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element - * @return {Ext.Element} The new child element - */ - createChild: function(config, insertBefore, returnDom){ - config = config || {tag:'div'}; - return insertBefore ? - DH.insertBefore(insertBefore, config, returnDom !== true) : - DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true); - }, - - /** - * Creates and wraps this element with another element - * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div - * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element - * @return {HTMLElement/Element} The newly created wrapper element - */ - wrap: function(config, returnDom){ - var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom); - newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom); - return newEl; - }, - - /** - * 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) : el; - } - } -}());/** - * @class Ext.Element + return ec(o); + }; + }(); + + + /** + * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set. + * @param {String} json The JSON string + * @return {Object} The resulting object + */ + this.decode = function() { + var dc; + return function(json) { + if (!dc) { + // setup decoding function on first access + dc = isNative() ? JSON.parse : doDecode; + } + return dc(json); + }; + }(); + +})(); +/** + * Shorthand for {@link Ext.util.JSON#encode} + * @param {Mixed} o The variable to encode + * @return {String} The JSON string + * @member Ext + * @method encode */ -Ext.apply(Ext.Element.prototype, function() { - var GETDOM = Ext.getDom, - GET = Ext.get, - DH = Ext.DomHelper; - - return { - /** - * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element - * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those. - * @param {String} where (optional) 'before' or 'after' defaults to before - * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element - * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned. - */ - insertSibling: function(el, where, returnDom){ - var me = this, - rt, - isAfter = (where || 'before').toLowerCase() == 'after', - insertEl; - - if(Ext.isArray(el)){ - insertEl = me; - Ext.each(el, function(e) { - rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom); - if(isAfter){ - insertEl = rt; +Ext.encode = Ext.util.JSON.encode; +/** + * Shorthand for {@link Ext.util.JSON#decode} + * @param {String} json The JSON string + * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid. + * @return {Object} The resulting object + * @member Ext + * @method decode + */ +Ext.decode = Ext.util.JSON.decode; +/** + * @class Ext.EventManager + * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides + * several useful events directly. + * See {@link Ext.EventObject} for more details on normalized event objects. + * @singleton + */ +Ext.EventManager = function(){ + var docReadyEvent, + docReadyProcId, + docReadyState = false, + DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari, + E = Ext.lib.Event, + D = Ext.lib.Dom, + DOC = document, + WINDOW = window, + DOMCONTENTLOADED = "DOMContentLoaded", + COMPLETE = 'complete', + propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/, + /* + * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep + * a reference to them so we can look them up at a later point. + */ + specialElCache = []; + + function getId(el){ + var id = false, + i = 0, + len = specialElCache.length, + skip = false, + o; + + if (el) { + if (el.getElementById || el.navigator) { + // look up the id + for(; i < len; ++i){ + o = specialElCache[i]; + if(o.el === el){ + id = o.id; + break; } - }); - return rt; - } - - el = el || {}; - - if(el.nodeType || el.dom){ - rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom); - if (!returnDom) { - rt = GET(rt); + } + if(!id){ + // for browsers that support it, ensure that give the el the same id + id = Ext.id(el); + specialElCache.push({ + id: id, + el: el + }); + skip = true; } }else{ - if (isAfter && !me.dom.nextSibling) { - rt = DH.append(me.dom.parentNode, el, !returnDom); - } else { - rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom); + id = Ext.id(el); + } + if(!Ext.elCache[id]){ + Ext.Element.addToCache(new Ext.Element(el), id); + if(skip){ + Ext.elCache[id].skipGC = true; } } - return rt; - } - }; -}());/** - * @class Ext.Element - */ -Ext.Element.addMethods(function(){ - // local style camelizing for speed - var propCache = {}, - camelRe = /(-[a-z])/gi, - view = document.defaultView, - propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat', - opacityRe = /alpha\(opacity=(.*)\)/i, - trimRe = /^\s+|\s+$/g, - spacesRe = /\s+/, - wordsRe = /\w/g, - PADDING = "padding", - MARGIN = "margin", - BORDER = "border", - LEFT = "-left", - RIGHT = "-right", - TOP = "-top", - BOTTOM = "-bottom", - WIDTH = "-width", - MATH = Math, - HIDDEN = 'hidden', - ISCLIPPED = 'isClipped', - OVERFLOW = 'overflow', - OVERFLOWX = 'overflow-x', - OVERFLOWY = 'overflow-y', - ORIGINALCLIP = 'originalClip', - // 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 = Ext.Element.data; + } + return id; + } + + /// There is some jquery work around stuff here that isn't needed in Ext Core. + function addListener(el, ename, fn, task, wrap, scope){ + el = Ext.getDom(el); + var id = getId(el), + es = Ext.elCache[id].events, + wfn; + + wfn = E.on(el, ename, wrap); + es[ename] = es[ename] || []; + + /* 0 = Original Function, + 1 = Event Manager Wrapped Function, + 2 = Scope, + 3 = Adapter Wrapped Function, + 4 = Buffered Task + */ + es[ename].push([fn, wrap, scope, wfn, task]); + // this is a workaround for jQuery and should somehow be removed from Ext Core in the future + // without breaking ExtJS. - // private - function camelFn(m, a) { - return a.charAt(1).toUpperCase(); + // workaround for jQuery + if(el.addEventListener && ename == "mousewheel"){ + var args = ["DOMMouseScroll", wrap, false]; + el.addEventListener.apply(el, args); + Ext.EventManager.addListener(WINDOW, 'unload', function(){ + el.removeEventListener.apply(el, args); + }); + } + + // fix stopped mousedowns on the document + if(el == DOC && ename == "mousedown"){ + Ext.EventManager.stoppedMouseDownEvent.addListener(wrap); + } } - function chkCache(prop) { - return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn)); + function doScrollChk(){ + /* Notes: + 'doScroll' will NOT work in a IFRAME/FRAMESET. + The method succeeds but, a DOM query done immediately after -- FAILS. + */ + if(window != top){ + return false; + } + + try{ + DOC.documentElement.doScroll('left'); + }catch(e){ + return false; + } + + fireDocReady(); + return true; } + /** + * @return {Boolean} True if the document is in a 'complete' state (or was determined to + * be true by other means). If false, the state is evaluated again until canceled. + */ + function checkReadyState(e){ - return { - // private ==> used by Fx - adjustWidth : function(width) { - var me = this; - var isNum = (typeof width == "number"); - if(isNum && me.autoBoxAdjust && !me.isBorderBox()){ - width -= (me.getBorderWidth("lr") + me.getPadding("lr")); - } - return (isNum && width < 0) ? 0 : width; - }, + if(Ext.isIE && doScrollChk()){ + return true; + } + if(DOC.readyState == COMPLETE){ + fireDocReady(); + return true; + } + docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2)); + return false; + } - // private ==> used by Fx - adjustHeight : function(height) { - var me = this; - var isNum = (typeof height == "number"); - if(isNum && me.autoBoxAdjust && !me.isBorderBox()){ - height -= (me.getBorderWidth("tb") + me.getPadding("tb")); + var styles; + function checkStyleSheets(e){ + styles || (styles = Ext.query('style, link[rel=stylesheet]')); + if(styles.length == DOC.styleSheets.length){ + fireDocReady(); + return true; + } + docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2)); + return false; + } + + function OperaDOMContentLoaded(e){ + DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false); + checkStyleSheets(); + } + + function fireDocReady(e){ + if(!docReadyState){ + docReadyState = true; //only attempt listener removal once + + if(docReadyProcId){ + clearTimeout(docReadyProcId); } - return (isNum && height < 0) ? 0 : height; - }, + if(DETECT_NATIVE) { + DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false); + } + if(Ext.isIE && checkReadyState.bindIE){ //was this was actually set ?? + DOC.detachEvent('onreadystatechange', checkReadyState); + } + E.un(WINDOW, "load", arguments.callee); + } + if(docReadyEvent && !Ext.isReady){ + Ext.isReady = true; + docReadyEvent.fire(); + docReadyEvent.listeners = []; + } + } - /** - * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out. - * @param {String/Array} className The CSS class to add, or an array of classes - * @return {Ext.Element} this + function initDocReady(){ + docReadyEvent || (docReadyEvent = new Ext.util.Event()); + if (DETECT_NATIVE) { + DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false); + } + /* + * Handle additional (exceptional) detection strategies here */ - addClass : function(className){ - var me = this, - i, - len, - v, - cls = []; - // Separate case is for speed - if (!Ext.isArray(className)) { - if (typeof className == 'string' && !this.hasClass(className)) { - me.dom.className += " " + className; - } + if (Ext.isIE){ + //Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME + //See if page is already loaded + if(!checkReadyState()){ + checkReadyState.bindIE = true; + DOC.attachEvent('onreadystatechange', checkReadyState); } - else { - for (i = 0, len = className.length; i < len; i++) { - v = className[i]; - if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) { - cls.push(v); - } - } - if (cls.length) { - me.dom.className += " " + cls.join(" "); + + }else if(Ext.isOpera ){ + /* Notes: + Opera needs special treatment needed here because CSS rules are NOT QUITE + available after DOMContentLoaded is raised. + */ + + //See if page is already loaded and all styleSheets are in place + (DOC.readyState == COMPLETE && checkStyleSheets()) || + DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false); + + }else if (Ext.isWebKit){ + //Fallback for older Webkits without DOMCONTENTLOADED support + checkReadyState(); + } + // no matter what, make sure it fires on load + E.on(WINDOW, "load", fireDocReady); + } + + 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, task){ + return function(e){ + // create new event object impl so new events don't wipe out properties + task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]); + }; + } + + 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 = (!opt || typeof opt == "boolean") ? {} : opt, + el = Ext.getDom(element), task; + + 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(!Ext){// !window[xname]){ ==> can't we do this? + return; + } + e = Ext.EventObject.setEvent(e); + var t; + if (o.delegate) { + if(!(t = e.getTarget(o.delegate, el))){ + return; } + } else { + t = e.target; } - return me; - }, + if (o.stopEvent) { + e.stopEvent(); + } + if (o.preventDefault) { + e.preventDefault(); + } + if (o.stopPropagation) { + e.stopPropagation(); + } + if (o.normalized === false) { + 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){ + task = new Ext.util.DelayedTask(h); + h = createBuffered(h, o, task); + } + + addListener(el, ename, fn, task, h, scope); + return h; + } + var pub = { /** - * Removes one or more CSS classes from the element. - * @param {String/Array} className The CSS class to remove, or an array of classes - * @return {Ext.Element} this + * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will + * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version. + * @param {String/HTMLElement} el The html element or id to assign the event handler to. + * @param {String} eventName The name of the event to listen for. + * @param {Function} handler The handler function the event invokes. This function is passed + * the following parameters: + * @param {Object} scope (optional) The scope (this reference) in which the handler function is executed. Defaults to the Element. + * @param {Object} options (optional) An object containing handler configuration properties. + * This may contain any of the following properties:
    + *

    See {@link Ext.Element#addListener} for examples of how to use these options.

    */ - removeClass : function(className){ - var me = this, - i, - idx, - len, - cls, - elClasses; - if (!Ext.isArray(className)){ - className = [className]; - } - if (me.dom && me.dom.className) { - elClasses = me.dom.className.replace(trimRe, '').split(spacesRe); - for (i = 0, len = className.length; i < len; i++) { - cls = className[i]; - if (typeof cls == 'string') { - cls = cls.replace(trimRe, ''); - idx = elClasses.indexOf(cls); - if (idx != -1) { - elClasses.splice(idx, 1); + addListener : function(element, eventName, fn, scope, options){ + if(typeof eventName == 'object'){ + 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); } } } - me.dom.className = elClasses.join(" "); + } else { + listen(element, eventName, options, fn, scope); } - return me; }, /** - * Adds one or more CSS classes to this element and removes the same class(es) from all siblings. - * @param {String/Array} className The CSS class to add, or an array of classes - * @return {Ext.Element} this + * 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. This must be a reference to the function passed into the {@link #addListener} call. + * @param {Object} scope If a scope (this reference) was specified when the listener was added, + * then this must refer to the same object. */ - radioClass : function(className){ - var cn = this.dom.parentNode.childNodes, - v, - i, - len; - className = Ext.isArray(className) ? className : [className]; - for (i = 0, len = cn.length; i < len; i++) { - v = cn[i]; - if (v && v.nodeType == 1) { - Ext.fly(v, '_internal').removeClass(className); - } - }; - return this.addClass(className); - }, + removeListener : function(el, eventName, fn, scope){ + el = Ext.getDom(el); + var id = getId(el), + f = el && (Ext.elCache[id].events)[eventName] || [], + wrap, i, l, k, len, fnc; - /** - * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it). - * @param {String} className The CSS class to toggle - * @return {Ext.Element} this - */ - toggleClass : function(className){ - return this.hasClass(className) ? this.removeClass(className) : this.addClass(className); - }, + for (i = 0, len = f.length; i < len; i++) { - /** - * Checks if the specified CSS class exists on this element's DOM node. - * @param {String} className The CSS class to check for - * @return {Boolean} True if the class exists, else false - */ - hasClass : function(className){ - return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1; - }, + /* 0 = Original Function, + 1 = Event Manager Wrapped Function, + 2 = Scope, + 3 = Adapter Wrapped Function, + 4 = Buffered Task + */ + if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) { + if(fnc[4]) { + fnc[4].cancel(); + } + k = fn.tasks && fn.tasks.length; + if(k) { + while(k--) { + fn.tasks[k].cancel(); + } + delete fn.tasks; + } + wrap = fnc[1]; + E.un(el, eventName, E.extAdapter ? fnc[3] : wrap); - /** - * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added. - * @param {String} oldClassName The CSS class to replace - * @param {String} newClassName The replacement CSS class - * @return {Ext.Element} this - */ - replaceClass : function(oldClassName, newClassName){ - return this.removeClass(oldClassName).addClass(newClassName); - }, + // jQuery workaround that should be removed from Ext Core + if(wrap && el.addEventListener && eventName == "mousewheel"){ + el.removeEventListener("DOMMouseScroll", wrap, false); + } - isStyle : function(style, val) { - return this.getStyle(style) == val; + // fix stopped mousedowns on the document + if(wrap && el == DOC && eventName == "mousedown"){ + Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap); + } + + f.splice(i, 1); + if (f.length === 0) { + delete Ext.elCache[id].events[eventName]; + } + for (k in Ext.elCache[id].events) { + return false; + } + Ext.elCache[id].events = {}; + return false; + } + } }, /** - * 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. + * 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. */ - getStyle : function(){ - return view && view.getComputedStyle ? - function(prop){ - var el = this.dom, - v, - cs, - out, - display, - wk = Ext.isWebKit, - display; - - if(el == document){ - return null; - } - prop = chkCache(prop); - // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343 - if(wk && (/marginRight/.test(prop))) { - display = this.getStyle('display'); - el.style.display = 'inline-block'; - } - out = (v = el.style[prop]) ? v : - (cs = view.getComputedStyle(el, "")) ? cs[prop] : null; + removeAll : function(el){ + el = Ext.getDom(el); + var id = getId(el), + ec = Ext.elCache[id] || {}, + es = ec.events || {}, + f, i, len, ename, fn, k, wrap; - // Webkit returns rgb values for transparent. - if(wk){ - if(out == 'rgba(0, 0, 0, 0)'){ - out = 'transparent'; - }else if(display){ - el.style.display = display; + for(ename in es){ + if(es.hasOwnProperty(ename)){ + f = es[ename]; + /* 0 = Original Function, + 1 = Event Manager Wrapped Function, + 2 = Scope, + 3 = Adapter Wrapped Function, + 4 = Buffered Task + */ + for (i = 0, len = f.length; i < len; i++) { + fn = f[i]; + if(fn[4]) { + fn[4].cancel(); } - } - return out; - } : - function(prop){ - var el = this.dom, - m, - cs; - - if(el == document) 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; - } + if(fn[0].tasks && (k = fn[0].tasks.length)) { + while(k--) { + fn[0].tasks[k].cancel(); } + delete fn.tasks; } - return 1; - } - prop = chkCache(prop); - return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null); - }; - }(), + wrap = fn[1]; + E.un(el, ename, E.extAdapter ? fn[3] : wrap); - /** - * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values - * are convert to standard 6 digit hex color. - * @param {String} attr The css attribute - * @param {String} defaultValue The default value to use when a valid color isn't found - * @param {String} prefix (optional) defaults to #. Use an empty string when working with - * color anims. - */ - getColor : function(attr, defaultValue, prefix){ - var v = this.getStyle(attr), - color = (typeof prefix != 'undefined') ? prefix : '#', - h; + // jQuery workaround that should be removed from Ext Core + if(el.addEventListener && wrap && ename == "mousewheel"){ + el.removeEventListener("DOMMouseScroll", wrap, false); + } - if(!v || (/transparent|inherit/.test(v))) { - return defaultValue; + // fix stopped mousedowns on the document + if(wrap && el == DOC && ename == "mousedown"){ + Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap); + } + } + } } - if(/^r/.test(v)){ - Ext.each(v.slice(4, v.length -1).split(','), function(s){ - h = parseInt(s, 10); - color += (h < 16 ? '0' : '') + h.toString(16); - }); - }else{ - v = v.replace('#', ''); - color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v; + if (Ext.elCache[id]) { + Ext.elCache[id].events = {}; } - return(color.length > 5 ? color.toLowerCase() : defaultValue); }, - /** - * 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){ - var tmp, style; - - if (typeof prop != 'object') { - tmp = {}; - tmp[prop] = value; - prop = tmp; - } - for (style in prop) { - value = prop[style]; - style == 'opacity' ? - this.setOpacity(value) : - this.dom.style[chkCache(style)] = value; + getListeners : function(el, eventName) { + el = Ext.getDom(el); + var id = getId(el), + ec = Ext.elCache[id] || {}, + es = ec.events || {}, + results = []; + if (es && es[eventName]) { + return es[eventName]; + } else { + return null; } - return this; }, - /** - * Set the opacity of the element - * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc - * @param {Boolean/Object} animate (optional) a standard Element animation config object or true for - * the default animation ({duration: .35, easing: 'easeIn'}) - * @return {Ext.Element} this - */ - setOpacity : function(opacity, animate){ - var me = this, - s = me.dom.style; + purgeElement : function(el, recurse, eventName) { + el = Ext.getDom(el); + var id = getId(el), + ec = Ext.elCache[id] || {}, + es = ec.events || {}, + i, f, len; + if (eventName) { + if (es && 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); + } + } + }, - if(!animate || !me.anim){ - if(Ext.isIE){ - var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '', - val = s.filter.replace(opacityRe, '').replace(trimRe, ''); + _unload : function() { + var el; + for (el in Ext.elCache) { + Ext.EventManager.removeAll(el); + } + delete Ext.elCache; + delete Ext.Element._flyweights; - s.zoom = 1; - s.filter = val + (val.length > 0 ? ' ' : '') + opac; - }else{ - s.opacity = opacity; + // Abort any outstanding Ajax requests + var c, + conn, + tid, + ajax = Ext.lib.Ajax; + (typeof ajax.conn == 'object') ? conn = ajax.conn : conn = {}; + for (tid in conn) { + c = conn[tid]; + if (c) { + ajax.abort({conn: c, tId: tid}); } - }else{ - me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn'); } - return me; }, - /** - * Clears any opacity settings from this element. Required in some cases for IE. - * @return {Ext.Element} this + * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be + * accessed shorthanded as Ext.onReady(). + * @param {Function} fn The method the event invokes. + * @param {Object} scope (optional) The scope (this reference) in which the handler function executes. Defaults to the browser window. + * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options + * {single: true} be used so that the handler is removed on first invocation. */ - clearOpacity : function(){ - var style = this.dom.style; - if(Ext.isIE){ - if(!Ext.isEmpty(style.filter)){ - style.filter = style.filter.replace(opacityRe, '').replace(trimRe, ''); + onDocumentReady : function(fn, scope, options){ + if (Ext.isReady) { // if it already fired or document.body is present + docReadyEvent || (docReadyEvent = new Ext.util.Event()); + docReadyEvent.addListener(fn, scope, options); + docReadyEvent.fire(); + docReadyEvent.listeners = []; + } else { + if (!docReadyEvent) { + initDocReady(); } - }else{ - style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = ''; + options = options || {}; + options.delay = options.delay || 1; + docReadyEvent.addListener(fn, scope, options); } - return this; }, /** - * Returns the offset height of the element - * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding - * @return {Number} The element's height + * Forces a document ready state transition for the framework. Used when Ext is loaded + * into a DOM structure AFTER initial page load (Google API or other dynamic load scenario. + * Any pending 'onDocumentReady' handlers will be fired (if not already handled). */ - getHeight : function(contentHeight){ - var me = this, - dom = me.dom, - hidden = Ext.isIE && me.isStyle('display', 'none'), - h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0; + fireDocReady : fireDocReady + }; + /** + * Appends an event handler to an element. Shorthand for {@link #addListener}. + * @param {String/HTMLElement} el The html element or id to assign the event handler to + * @param {String} eventName The name of the event to listen for. + * @param {Function} handler The handler function the event invokes. + * @param {Object} scope (optional) (this reference) in which the handler function executes. Defaults to the Element. + * @param {Object} options (optional) An object containing standard {@link #addListener} options + * @member Ext.EventManager + * @method on + */ + pub.on = pub.addListener; + /** + * Removes an event handler from an element. Shorthand for {@link #removeListener}. + * @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. This must be a reference to the function passed into the {@link #on} call. + * @param {Object} scope If a scope (this reference) was specified when the listener was added, + * then this must refer to the same object. + * @member Ext.EventManager + * @method un + */ + pub.un = pub.removeListener; - h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb"); - return h < 0 ? 0 : h; - }, + pub.stoppedMouseDownEvent = new Ext.util.Event(); + return pub; +}(); +/** + * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}. + * @param {Function} fn The method the event invokes. + * @param {Object} scope (optional) The scope (this reference) in which the handler function executes. Defaults to the browser window. + * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options + * {single: true} be used so that the handler is removed on first invocation. + * @member Ext + * @method onReady + */ +Ext.onReady = Ext.EventManager.onDocumentReady; - /** - * Returns the offset width of the element - * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding - * @return {Number} The element's width - */ - getWidth : function(contentWidth){ - var me = this, - dom = me.dom, - hidden = Ext.isIE && me.isStyle('display', 'none'), - w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0; - w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr"); - return w < 0 ? 0 : w; - }, - /** - * Set the width of this Element. - * @param {Mixed} width The new width. This may be one of:
    - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.Element} this - */ - setWidth : function(width, animate){ - var me = this; - width = me.adjustWidth(width); - !animate || !me.anim ? - me.dom.style.width = me.addUnits(width) : - me.anim({width : {to : width}}, me.preanim(arguments, 1)); - return me; - }, +//Initialize doc classes +(function(){ + var initExtCss = function() { + // find the body element + var bd = document.body || document.getElementsByTagName('body')[0]; + if (!bd) { + return false; + } + + var cls = [' ', + Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8')) + : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3') + : Ext.isOpera ? "ext-opera" + : Ext.isWebKit ? "ext-webkit" : ""]; - /** - * Set the height of this Element. - *
    
    -// change the height to 200px and animate with default configuration
    -Ext.fly('elementId').setHeight(200, true);
    +        if (Ext.isSafari) {
    +            cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
    +        } else if(Ext.isChrome) {
    +            cls.push("ext-chrome");
    +        }
     
    -// change the height to 150px and animate with a custom configuration
    -Ext.fly('elId').setHeight(150, {
    -    duration : .5, // animation will have a duration of .5 seconds
    -    // will change the content to "finished"
    -    callback: function(){ this.{@link #update}("finished"); }
    -});
    -         * 
    - * @param {Mixed} height The new height. This may be one of:
    - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.Element} this - */ - setHeight : function(height, animate){ - var me = this; - height = me.adjustHeight(height); - !animate || !me.anim ? - me.dom.style.height = me.addUnits(height) : - me.anim({height : {to : height}}, me.preanim(arguments, 1)); - return me; - }, + if (Ext.isMac) { + cls.push("ext-mac"); + } + if (Ext.isLinux) { + cls.push("ext-linux"); + } - /** - * Gets the width of the border(s) for the specified side(s) - * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example, - * passing 'lr' would get the border left width + the border right width. - * @return {Number} The width of the sides passed added together - */ - getBorderWidth : function(side){ - return this.addStyles(side, borders); - }, + // add to the parent to allow for selectors like ".ext-strict .ext-ie" + if (Ext.isStrict || Ext.isBorderBox) { + var p = bd.parentNode; + if (p) { + Ext.fly(p, '_internal').addClass(((Ext.isStrict && Ext.isIE ) || (!Ext.enableForcedBoxModel && !Ext.isIE)) ? ' ext-strict' : ' ext-border-box'); + } + } + // Forced border box model class applied to all elements. Bypassing javascript based box model adjustments + // in favor of css. This is for non-IE browsers. + if (Ext.enableForcedBoxModel && !Ext.isIE) { + Ext.isForcedBorderBox = true; + cls.push("ext-forced-border-box"); + } + + Ext.fly(bd, '_internal').addClass(cls); + return true; + }; + /* + * Assert Ext.isReady here. If Ext is loaded after the document is ready, none of the native + * DOM onReady events will fire, because they have already passed. + */ + Ext.isReady = initExtCss(); + + if (!Ext.isReady) { + Ext.onReady(initExtCss); + } +})(); + +/** + * Code used to detect certain browser feature/quirks/bugs at startup. + */ +(function(){ + var supports = Ext.apply(Ext.supports, { /** - * Gets the width of the padding(s) for the specified side(s) - * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example, - * passing 'lr' would get the padding left + the padding right. - * @return {Number} The padding of the sides passed added together + * In Webkit, there is an issue with getting the margin right property, see + * https://bugs.webkit.org/show_bug.cgi?id=13343 */ - getPadding : function(side){ - return this.addStyles(side, paddings); - }, - + correctRightMargin: true, + /** - * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove - * @return {Ext.Element} this + * Webkit browsers return rgba(0, 0, 0) when a transparent color is used */ - 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; - }, - + correctTransparentColor: true, + /** - * Return clipping (overflow) to original clipping before {@link #clip} was called - * @return {Ext.Element} this + * IE uses styleFloat, not cssFloat for the float property. */ - 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); + cssFloat: true + }); + + var supportTests = function(){ + var div = document.createElement('div'), + doc = document, + view, + last; + + div.innerHTML = '
    '; + doc.body.appendChild(div); + last = div.lastChild; + + if((view = doc.defaultView)){ + if(view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px'){ + supports.correctRightMargin = false; } - if(o.y){ - me.setStyle(OVERFLOWY, o.y); + if(view.getComputedStyle(last, null).backgroundColor != 'transparent'){ + supports.correctTransparentColor = false; } } - return me; - }, + supports.cssFloat = !!last.style.cssFloat; + doc.body.removeChild(div); + }; + + if (Ext.isReady) { + supportTests(); + } else { + Ext.onReady(supportTests); + } +})(); - // private - addStyles : function(sides, styles){ - var ttlSize = 0, - sidesArr = sides.match(wordsRe), - side, - size, - i, - len = sidesArr.length; - for (i = 0; i < len; i++) { - side = sidesArr[i]; - size = side && parseInt(this.getStyle(styles[side]), 10); - if (size) { - ttlSize += MATH.abs(size); - } - } - return ttlSize; - }, - margins : margins - }; -}() -); /** - * @class Ext.Element + * @class Ext.EventObject + * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject + * wraps the browser's native event-object normalizing cross-browser differences, + * such as which mouse button is clicked, keys pressed, mechanisms to stop + * event-propagation along with a method to prevent default actions from taking place. + *

    For example:

    + *
    
    +function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
    +    e.preventDefault();
    +    var target = e.getTarget(); // same as t (the target HTMLElement)
    +    ...
    +}
    +var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
    +myDiv.on(         // 'on' is shorthand for addListener
    +    "click",      // perform an action on click of myDiv
    +    handleClick   // reference to the action handler
    +);
    +// other methods to do the same:
    +Ext.EventManager.on("myDiv", 'click', handleClick);
    +Ext.EventManager.addListener("myDiv", 'click', handleClick);
    + 
    + * @singleton */ - -// special markup used throughout Ext when box wrapping elements -Ext.Element.boxMarkup = '
    '; - -Ext.Element.addMethods(function(){ - var INTERNAL = "_internal", - pxMatch = /(\d+\.?\d+)px/; - return { - /** - * More flexible version of {@link #setStyle} for setting style properties. - * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or - * a function which returns such a specification. - * @return {Ext.Element} this - */ - applyStyles : function(style){ - Ext.DomHelper.applyStyles(this.dom, style); - return this; +Ext.EventObject = function(){ + var E = Ext.lib.Event, + clickRe = /(dbl)?click/, + // safari keypress events for special keys return bad keycodes + safariKeys = { + 3 : 13, // enter + 63234 : 37, // left + 63235 : 39, // right + 63232 : 38, // up + 63233 : 40, // down + 63276 : 33, // page up + 63277 : 34, // page down + 63272 : 46, // delete + 63273 : 36, // home + 63275 : 35 // end }, + // normalize button clicks + btnMap = Ext.isIE ? {1:0,4:1,2:2} : {0:0,1:1,2:2}; - /** - * Returns an object with properties matching the styles requested. - * For example, el.getStyles('color', 'font-size', 'width') might return - * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}. - * @param {String} style1 A style name - * @param {String} style2 A style name - * @param {String} etc. - * @return {Object} The style object - */ - getStyles : function(){ - var ret = {}; - Ext.each(arguments, function(v) { - ret[v] = this.getStyle(v); - }, - this); - return ret; - }, + Ext.EventObjectImpl = function(e){ + if(e){ + this.setEvent(e.browserEvent || e); + } + }; - // private ==> used by ext full - setOverflow : function(v){ - var dom = this.dom; - if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug - dom.style.overflow = 'hidden'; - (function(){dom.style.overflow = 'auto';}).defer(1); - }else{ - dom.style.overflow = v; + Ext.EventObjectImpl.prototype = { + /** @private */ + setEvent : function(e){ + var me = this; + if(e == me || (e && e.browserEvent)){ // already wrapped + return e; } - }, - - /** - *

    Wraps the specified element with a special 9 element markup/CSS block that renders by default as - * a gray container with a gradient background, rounded corners and a 4-way shadow.

    - *

    This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button}, - * {@link Ext.Panel} when {@link Ext.Panel#frame frame=true}, {@link Ext.Window}). The markup - * is of this form:

    - *
    
    -    Ext.Element.boxMarkup =
    -    '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
    -     <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
    -     <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
    -        * 
    - *

    Example usage:

    - *
    
    -    // Basic box wrap
    -    Ext.get("foo").boxWrap();
    -
    -    // You can also add a custom class and use CSS inheritance rules to customize the box look.
    -    // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
    -    // for how to create a custom box wrap style.
    -    Ext.get("foo").boxWrap().addClass("x-box-blue");
    -        * 
    - * @param {String} class (optional) A base CSS class to apply to the containing wrapper element - * (defaults to 'x-box'). Note that there are a number of CSS rules that are dependent on - * this name to make the overall effect work, so if you supply an alternate base class, make sure you - * also supply all of the necessary rules. - * @return {Ext.Element} The outermost wrapping element of the created box structure. - */ - boxWrap : function(cls){ - cls = cls || 'x-box'; - var el = Ext.get(this.insertHtml("beforeBegin", "
    " + String.format(Ext.Element.boxMarkup, cls) + "
    ")); //String.format('
    '+Ext.Element.boxMarkup+'
    ', cls))); - Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom); - return el; + me.browserEvent = e; + if(e){ + // normalize buttons + me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1); + if(clickRe.test(e.type) && me.button == -1){ + me.button = 0; + } + me.type = e.type; + me.shiftKey = e.shiftKey; + // mac metaKey behaves like ctrlKey + me.ctrlKey = e.ctrlKey || e.metaKey || false; + me.altKey = e.altKey; + // in getKey these will be normalized for the mac + me.keyCode = e.keyCode; + me.charCode = e.charCode; + // cache the target for the delayed and or buffered events + me.target = E.getTarget(e); + // same for XY + me.xy = E.getXY(e); + }else{ + me.button = -1; + me.shiftKey = false; + me.ctrlKey = false; + me.altKey = false; + me.keyCode = 0; + me.charCode = 0; + me.target = null; + me.xy = [0, 0]; + } + return me; }, /** - * Set the size of this Element. If animation is true, both width and height will be animated concurrently. - * @param {Mixed} width The new width. This may be one of:
    - * @param {Mixed} height The new height. This may be one of:
    - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.Element} this + * Stop the event (preventDefault and stopPropagation) */ - setSize : function(width, height, animate){ + stopEvent : function(){ var me = this; - if(typeof width == 'object'){ // in case of object from getSize() - height = width.height; - width = width.width; - } - width = me.adjustWidth(width); - height = me.adjustHeight(height); - if(!animate || !me.anim){ - me.dom.style.width = me.addUnits(width); - me.dom.style.height = me.addUnits(height); - }else{ - me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2)); + if(me.browserEvent){ + if(me.browserEvent.type == 'mousedown'){ + Ext.EventManager.stoppedMouseDownEvent.fire(me); + } + E.stopEvent(me.browserEvent); } - return me; }, /** - * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders - * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements - * if a height has not been set using CSS. - * @return {Number} + * Prevents the browsers default handling of the event. */ - getComputedHeight : function(){ - var me = this, - h = Math.max(me.dom.offsetHeight, me.dom.clientHeight); - if(!h){ - h = parseFloat(me.getStyle('height')) || 0; - if(!me.isBorderBox()){ - h += me.getFrameWidth('tb'); - } + preventDefault : function(){ + if(this.browserEvent){ + E.preventDefault(this.browserEvent); } - return h; }, /** - * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders - * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements - * if a width has not been set using CSS. - * @return {Number} + * Cancels bubbling of the event. */ - getComputedWidth : function(){ - var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth); - if(!w){ - w = parseFloat(this.getStyle('width')) || 0; - if(!this.isBorderBox()){ - w += this.getFrameWidth('lr'); + stopPropagation : function(){ + var me = this; + if(me.browserEvent){ + if(me.browserEvent.type == 'mousedown'){ + Ext.EventManager.stoppedMouseDownEvent.fire(me); } + E.stopPropagation(me.browserEvent); } - return w; }, /** - * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth() - for more information about the sides. - * @param {String} sides + * Gets the character code for the event. * @return {Number} */ - getFrameWidth : function(sides, onlyContentBox){ - return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides)); + getCharCode : function(){ + return this.charCode || this.keyCode; }, /** - * Sets up event handlers to add and remove a css class when the mouse is over this element - * @param {String} className - * @return {Ext.Element} this + * Returns a normalized keyCode for the event. + * @return {Number} The key code */ - addClassOnOver : function(className){ - this.hover( - function(){ - Ext.fly(this, INTERNAL).addClass(className); - }, - function(){ - Ext.fly(this, INTERNAL).removeClass(className); - } - ); - return this; + getKey : function(){ + return this.normalizeKey(this.keyCode || this.charCode); }, - /** - * Sets up event handlers to add and remove a css class when this element has the focus - * @param {String} className - * @return {Ext.Element} this - */ - addClassOnFocus : function(className){ - this.on("focus", function(){ - Ext.fly(this, INTERNAL).addClass(className); - }, this.dom); - this.on("blur", function(){ - Ext.fly(this, INTERNAL).removeClass(className); - }, this.dom); - return this; + // private + normalizeKey: function(k){ + return Ext.isSafari ? (safariKeys[k] || k) : k; }, /** - * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect) - * @param {String} className - * @return {Ext.Element} this + * Gets the x coordinate of the event. + * @return {Number} */ - addClassOnClick : function(className){ - var dom = this.dom; - this.on("mousedown", function(){ - Ext.fly(dom, INTERNAL).addClass(className); - var d = Ext.getDoc(), - fn = function(){ - Ext.fly(dom, INTERNAL).removeClass(className); - d.removeListener("mouseup", fn); - }; - d.on("mouseup", fn); - }); - return this; - }, - - /** - *

    Returns the dimensions of the element available to lay content out in.

    - *

    If the element (or any ancestor element) has CSS style display : none, the dimensions will be zero.

    - * example:
    
    -        var vpSize = Ext.getBody().getViewSize();
    -
    -        // all Windows created afterwards will have a default value of 90% height and 95% width
    -        Ext.Window.override({
    -            width: vpSize.width * 0.9,
    -            height: vpSize.height * 0.95
    -        });
    -        // To handle window resizing you would have to hook onto onWindowResize.
    -        * 
    - * - * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars. - * To obtain the size including scrollbars, use getStyleSize - * - * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc. - */ - - getViewSize : function(){ - var doc = document, - d = this.dom, - isDoc = (d == doc || d == doc.body); - - // If the body, use Ext.lib.Dom - if (isDoc) { - var extdom = Ext.lib.Dom; - return { - width : extdom.getViewWidth(), - height : extdom.getViewHeight() - }; - - // Else use clientHeight/clientWidth - } else { - return { - width : d.clientWidth, - height : d.clientHeight - } - } + getPageX : function(){ + return this.xy[0]; }, /** - *

    Returns the dimensions of the element available to lay content out in.

    - * - * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth. - * To obtain the size excluding scrollbars, use getViewSize - * - * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc. - */ - - getStyleSize : function(){ - var me = this, - w, h, - doc = document, - d = this.dom, - isDoc = (d == doc || d == doc.body), - s = d.style; - - // If the body, use Ext.lib.Dom - if (isDoc) { - var extdom = Ext.lib.Dom; - return { - width : extdom.getViewWidth(), - height : extdom.getViewHeight() - } - } - // Use Styles if they are set - if(s.width && s.width != 'auto'){ - w = parseFloat(s.width); - if(me.isBorderBox()){ - w -= me.getFrameWidth('lr'); - } - } - // Use Styles if they are set - if(s.height && s.height != 'auto'){ - h = parseFloat(s.height); - if(me.isBorderBox()){ - h -= me.getFrameWidth('tb'); - } - } - // Use getWidth/getHeight if style not set. - return {width: w || me.getWidth(true), height: h || me.getHeight(true)}; + * Gets the y coordinate of the event. + * @return {Number} + */ + getPageY : function(){ + return this.xy[1]; }, /** - * Returns the size of the element. - * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding - * @return {Object} An object containing the element's size {width: (element width), height: (element height)} + * Gets the page coordinates of the event. + * @return {Array} The xy values like [x, y] */ - getSize : function(contentSize){ - return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)}; + getXY : function(){ + return this.xy; }, /** - * Forces the browser to repaint this element - * @return {Ext.Element} this + * Gets the target for the event. + * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target + * @param {Number/Mixed} maxDepth (optional) The max depth to + search as a number or element (defaults to 10 || document.body) + * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node + * @return {HTMLelement} */ - repaint : function(){ - var dom = this.dom; - this.addClass("x-repaint"); - setTimeout(function(){ - Ext.fly(dom).removeClass("x-repaint"); - }, 1); - return this; + getTarget : function(selector, maxDepth, returnEl){ + return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target); }, /** - * Disables text selection for this element (normalized across browsers) - * @return {Ext.Element} this + * Gets the related target. + * @return {HTMLElement} */ - unselectable : function(){ - this.dom.unselectable = "on"; - return this.swallowEvent("selectstart", true). - applyStyles("-moz-user-select:none;-khtml-user-select:none;"). - addClass("x-unselectable"); + getRelatedTarget : function(){ + return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null; }, /** - * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed, - * then it returns the calculated width of the sides (see getPadding) - * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides - * @return {Object/Number} + * Normalizes mouse wheel delta across browsers + * @return {Number} The delta */ - getMargins : function(side){ - var me = this, - key, - hash = {t:"top", l:"left", r:"right", b: "bottom"}, - o = {}; - - if (!side) { - for (key in me.margins){ - o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0; - } - return o; - } else { - return me.addStyles.call(me, side, me.margins); + getWheelDelta : function(){ + var e = this.browserEvent; + var delta = 0; + if(e.wheelDelta){ /* IE/Opera. */ + delta = e.wheelDelta/120; + }else if(e.detail){ /* Mozilla case. */ + delta = -e.detail/3; } - } - }; -}()); -/** - * @class Ext.Element - */ -(function(){ -var D = Ext.lib.Dom, - LEFT = "left", - RIGHT = "right", - TOP = "top", - BOTTOM = "bottom", - POSITION = "position", - STATIC = "static", - RELATIVE = "relative", - AUTO = "auto", - ZINDEX = "z-index"; - -Ext.Element.addMethods({ - /** - * Gets the current X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @return {Number} The X position of the element - */ - getX : function(){ - return D.getX(this.dom); - }, - - /** - * Gets the current Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @return {Number} The Y position of the element - */ - getY : function(){ - return D.getY(this.dom); - }, - - /** - * Gets the current position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @return {Array} The XY position of the element - */ - getXY : function(){ - return D.getXY(this.dom); - }, - - /** - * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree and not have display:none to have page coordinates. - * @param {Mixed} element The element to get the offsets from. - * @return {Array} The XY page offsets (e.g. [100, -200]) - */ - getOffsetsTo : function(el){ - var o = this.getXY(), - e = Ext.fly(el, '_internal').getXY(); - return [o[0]-e[0],o[1]-e[1]]; - }, - - /** - * Sets the X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @param {Number} The X position of the element - * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.Element} this - */ - setX : function(x, animate){ - return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1)); - }, - - /** - * Sets the Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @param {Number} The Y position of the element - * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.Element} this - */ - setY : function(y, animate){ - return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1)); - }, - - /** - * Sets the element's left position directly using CSS style (instead of {@link #setX}). - * @param {String} left The left CSS property value - * @return {Ext.Element} this - */ - setLeft : function(left){ - this.setStyle(LEFT, this.addUnits(left)); - return this; - }, - - /** - * Sets the element's top position directly using CSS style (instead of {@link #setY}). - * @param {String} top The top CSS property value - * @return {Ext.Element} this - */ - setTop : function(top){ - this.setStyle(TOP, this.addUnits(top)); - return this; - }, - - /** - * Sets the element's CSS right style. - * @param {String} right The right CSS property value - * @return {Ext.Element} this - */ - setRight : function(right){ - this.setStyle(RIGHT, this.addUnits(right)); - return this; - }, - - /** - * Sets the element's CSS bottom style. - * @param {String} bottom The bottom CSS property value - * @return {Ext.Element} this - */ - setBottom : function(bottom){ - this.setStyle(BOTTOM, this.addUnits(bottom)); - return this; - }, - - /** - * Sets the position of the element in page coordinates, regardless of how the element is positioned. - * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based) - * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.Element} this - */ - setXY : function(pos, animate){ - var me = this; - if(!animate || !me.anim){ - D.setXY(me.dom, pos); - }else{ - me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion'); - } - return me; - }, - - /** - * Sets the position of the element in page coordinates, regardless of how the element is positioned. - * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @param {Number} x X value for new position (coordinates are page-based) - * @param {Number} y Y value for new position (coordinates are page-based) - * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.Element} this - */ - setLocation : function(x, y, animate){ - return this.setXY([x, y], this.animTest(arguments, animate, 2)); - }, - - /** - * Sets the position of the element in page coordinates, regardless of how the element is positioned. - * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). - * @param {Number} x X value for new position (coordinates are page-based) - * @param {Number} y Y value for new position (coordinates are page-based) - * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.Element} this - */ - moveTo : function(x, y, animate){ - return this.setXY([x, y], this.animTest(arguments, animate, 2)); - }, - - /** - * Gets the left X coordinate - * @param {Boolean} local True to get the local css position instead of page coordinate - * @return {Number} - */ - getLeft : function(local){ - return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0; - }, - - /** - * Gets the right X coordinate of the element (element X position + element width) - * @param {Boolean} local True to get the local css position instead of page coordinate - * @return {Number} - */ - getRight : function(local){ - var me = this; - return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0; - }, - - /** - * Gets the top Y coordinate - * @param {Boolean} local True to get the local css position instead of page coordinate - * @return {Number} - */ - getTop : function(local) { - return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0; - }, - - /** - * Gets the bottom Y coordinate of the element (element Y position + element height) - * @param {Boolean} local True to get the local css position instead of page coordinate - * @return {Number} - */ - getBottom : function(local){ - var me = this; - return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0; - }, - - /** - * Initializes positioning on this element. If a desired position is not passed, it will make the - * the element positioned relative IF it is not already positioned. - * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed" - * @param {Number} zIndex (optional) The zIndex to apply - * @param {Number} x (optional) Set the page X position - * @param {Number} y (optional) Set the page Y position - */ - position : function(pos, zIndex, x, y){ - var me = this; - - if(!pos && me.isStyle(POSITION, STATIC)){ - me.setStyle(POSITION, RELATIVE); - } else if(pos) { - me.setStyle(POSITION, pos); - } - if(zIndex){ - me.setStyle(ZINDEX, zIndex); - } - if(x || y) me.setXY([x || false, y || false]); - }, + return delta; + }, - /** - * Clear positioning back to the default when the document was loaded - * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'. - * @return {Ext.Element} this - */ - clearPositioning : function(value){ - value = value || ''; - this.setStyle({ - left : value, - right : value, - top : value, - bottom : value, - "z-index" : "", - position : STATIC + /** + * Returns true if the target of this event is a child of el. Unless the allowEl parameter is set, it will return false if if the target is el. + * Example usage:

    
    +        // Handle click on any child of an element
    +        Ext.getBody().on('click', function(e){
    +            if(e.within('some-el')){
    +                alert('Clicked on a child of some-el!');
    +            }
             });
    -        return this;
    -    },
     
    -    /**
    -    * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
    -    * snapshot before performing an update and then restoring the element.
    -    * @return {Object}
    -    */
    -    getPositioning : function(){
    -        var l = this.getStyle(LEFT);
    -        var t = this.getStyle(TOP);
    -        return {
    -            "position" : this.getStyle(POSITION),
    -            "left" : l,
    -            "right" : l ? "" : this.getStyle(RIGHT),
    -            "top" : t,
    -            "bottom" : t ? "" : this.getStyle(BOTTOM),
    -            "z-index" : this.getStyle(ZINDEX)
    -        };
    -    },
    -    
    -    /**
    -    * Set positioning with an object returned by getPositioning().
    -    * @param {Object} posCfg
    -    * @return {Ext.Element} this
    -     */
    -    setPositioning : function(pc){
    -	    var me = this,
    -	    	style = me.dom.style;
    -	    	
    -        me.setStyle(pc);
    -        
    -        if(pc.right == AUTO){
    -            style.right = "";
    -        }
    -        if(pc.bottom == AUTO){
    -            style.bottom = "";
    +        // Handle click directly on an element, ignoring clicks on child nodes
    +        Ext.getBody().on('click', function(e,t){
    +            if((t.id == 'some-el') && !e.within(t, true)){
    +                alert('Clicked directly on some-el!');
    +            }
    +        });
    +        
    + * @param {Mixed} el The id, DOM element or Ext.Element to check + * @param {Boolean} related (optional) true to test if the related target is within el instead of the target + * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target + * @return {Boolean} + */ + within : function(el, related, allowEl){ + if(el){ + var t = this[related ? "getRelatedTarget" : "getTarget"](); + return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t)); + } + return false; } - - return me; - }, - - /** - * Translates the passed page coordinates into left/top css values for this element - * @param {Number/Array} x The page x or an array containing [x, y] - * @param {Number} y (optional) The page y, required if x is not an array - * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)} - */ - translatePoints : function(x, y){ - y = isNaN(x[1]) ? y : x[1]; - x = isNaN(x[0]) ? x : x[0]; - var me = this, - relative = me.isStyle(POSITION, RELATIVE), - o = me.getXY(), - l = parseInt(me.getStyle(LEFT), 10), - t = parseInt(me.getStyle(TOP), 10); - - l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft); - t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop); + }; - return {left: (x - o[0] + l), top: (y - o[1] + t)}; - }, - - animTest : function(args, animate, i) { - return !!animate && this.preanim ? this.preanim(args, i) : false; - } -}); -})();/** - * @class Ext.Element + return new Ext.EventObjectImpl(); +}(); +/** + * @class Ext */ -Ext.Element.addMethods({ - /** - * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently. - * @param {Object} box The box to fill {x, y, width, height} - * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.Element} this - */ - setBox : function(box, adjust, animate){ - var me = this, - w = box.width, - h = box.height; - if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){ - w -= (me.getBorderWidth("lr") + me.getPadding("lr")); - h -= (me.getBorderWidth("tb") + me.getPadding("tb")); - } - me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2)); - return me; - }, +Ext.ns("Ext.grid", "Ext.list", "Ext.dd", "Ext.tree", "Ext.form", "Ext.menu", + "Ext.state", "Ext.layout", "Ext.app", "Ext.ux", "Ext.chart", "Ext.direct"); /** - * Return an object defining the area of this Element which can be passed to {@link #setBox} to - * set another Element's size/location to match this element. - * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned. - * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y. - * @return {Object} box An object in the format
    
    -{
    -    x: <Element's X position>,
    -    y: <Element's Y position>,
    -    width: <Element's width>,
    -    height: <Element's height>,
    -    bottom: <Element's lower bound>,
    -    right: <Element's rightmost bound>
    -}
    -
    - * The returned object may also be addressed as an Array where index 0 contains the X position - * and index 1 contains the Y position. So the result may also be used for {@link #setXY} - */ - getBox : function(contentBox, local) { - var me = this, - xy, - left, - top, - getBorderWidth = me.getBorderWidth, - getPadding = me.getPadding, - l, - r, - t, - b; - if(!local){ - xy = me.getXY(); - }else{ - left = parseInt(me.getStyle("left"), 10) || 0; - top = parseInt(me.getStyle("top"), 10) || 0; - xy = [left, top]; - } - var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx; - if(!contentBox){ - bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h}; - }else{ - l = getBorderWidth.call(me, "l") + getPadding.call(me, "l"); - r = getBorderWidth.call(me, "r") + getPadding.call(me, "r"); - t = getBorderWidth.call(me, "t") + getPadding.call(me, "t"); - b = getBorderWidth.call(me, "b") + getPadding.call(me, "b"); - bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)}; - } - bx.right = bx.x + bx.width; - bx.bottom = bx.y + bx.height; - return bx; - }, - - /** - * Move this element relative to its current position. - * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down"). - * @param {Number} distance How far to move the element in pixels - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.Element} this - */ - move : function(direction, distance, animate){ - var me = this, - xy = me.getXY(), - x = xy[0], - y = xy[1], - left = [x - distance, y], - right = [x + distance, y], - top = [x, y - distance], - bottom = [x, y + distance], - hash = { - l : left, - left : left, - r : right, - right : right, - t : top, - top : top, - up : top, - b : bottom, - bottom : bottom, - down : bottom - }; - - direction = direction.toLowerCase(); - me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2)); - }, - - /** - * Quick set left and top adding default units - * @param {String} left The left CSS property value - * @param {String} top The top CSS property value - * @return {Ext.Element} this - */ - setLeftTop : function(left, top){ - var me = this, - style = me.dom.style; - style.left = me.addUnits(left); - style.top = me.addUnits(top); - return me; - }, - - /** - * Returns the region of the given element. - * The element must be part of the DOM tree to have a region (display:none or elements not appended return false). - * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data. - */ - getRegion : function(){ - return Ext.lib.Dom.getRegion(this.dom); - }, - - /** - * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently. - * @param {Number} x X value for new position (coordinates are page-based) - * @param {Number} y Y value for new position (coordinates are page-based) - * @param {Mixed} width The new width. This may be one of:
    - * @param {Mixed} height The new height. This may be one of:
    - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.Element} this + * Namespace alloted for extensions to the framework. + * @property ux + * @type Object */ - setBounds : function(x, y, width, height, animate){ - var me = this; - if (!animate || !me.anim) { - me.setSize(width, height); - me.setLocation(x, y); - } else { - me.anim({points: {to: [x, y]}, - width: {to: me.adjustWidth(width)}, - height: {to: me.adjustHeight(height)}}, - me.preanim(arguments, 4), - 'motion'); - } - return me; + +Ext.apply(Ext, function(){ + var E = Ext, + idSeed = 0, + scrollWidth = null; + + return { + /** + * A reusable empty function + * @property + * @type Function + */ + emptyFn : function(){}, + + /** + * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images. + * In older versions of IE, this defaults to "http://extjs.com/s.gif" and you should change this to a URL on your server. + * For other browsers it uses an inline data URL. + * @type String + */ + BLANK_IMAGE_URL : Ext.isIE6 || Ext.isIE7 || Ext.isAir ? + 'http:/' + '/www.extjs.com/s.gif' : + 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', + + extendX : function(supr, fn){ + return Ext.extend(supr, fn(supr.prototype)); + }, + + /** + * Returns the current HTML document object as an {@link Ext.Element}. + * @return Ext.Element The document + */ + getDoc : function(){ + return Ext.get(document); + }, + + /** + * Utility method for validating that a value is numeric, returning the specified default value if it is not. + * @param {Mixed} value Should be a number, but any type will be handled appropriately + * @param {Number} defaultValue The value to return if the original value is non-numeric + * @return {Number} Value, if numeric, else defaultValue + */ + num : function(v, defaultValue){ + v = Number(Ext.isEmpty(v) || Ext.isArray(v) || typeof v == 'boolean' || (typeof v == 'string' && v.trim().length == 0) ? NaN : v); + return isNaN(v) ? defaultValue : v; + }, + + /** + *

    Utility method for returning a default value if the passed value is empty.

    + *

    The value is deemed to be empty if it is

    + * @param {Mixed} value The value to test + * @param {Mixed} defaultValue The value to return if the original value is empty + * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false) + * @return {Mixed} value, if non-empty, else defaultValue + */ + value : function(v, defaultValue, allowBlank){ + return Ext.isEmpty(v, allowBlank) ? defaultValue : v; + }, + + /** + * Escapes the passed string for use in a regular expression + * @param {String} str + * @return {String} + */ + escapeRe : function(s) { + return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1"); + }, + + sequence : function(o, name, fn, scope){ + o[name] = o[name].createSequence(fn, scope); + }, + + /** + * Applies event listeners to elements by selectors when the document is ready. + * The event name is specified with an @ suffix. + *
    
    +Ext.addBehaviors({
    +    // add a listener for click on all anchors in element with id foo
    +    '#foo a@click' : function(e, t){
    +        // do something
         },
     
    -    /**
    -     * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
    -     * @param {Ext.lib.Region} region The region to fill
    -     * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
    -     * @return {Ext.Element} this
    -     */
    -    setRegion : function(region, animate) {
    -        return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
    +    // add the same listener to multiple selectors (separated by comma BEFORE the @)
    +    '#foo a, #bar span.some-class@mouseover' : function(){
    +        // do something
         }
    -});/**
    - * @class Ext.Element
    - */
    -Ext.Element.addMethods({
    -    /**
    -     * Returns true if this element is scrollable.
    -     * @return {Boolean}
    -     */
    -    isScrollable : function(){
    -        var dom = this.dom;
    -        return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
    -    },
    +});
    +         * 
    + * @param {Object} obj The list of behaviors to apply + */ + addBehaviors : function(o){ + if(!Ext.isReady){ + Ext.onReady(function(){ + Ext.addBehaviors(o); + }); + } else { + var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times + parts, + b, + s; + for (b in o) { + if ((parts = b.split('@'))[1]) { // for Object prototype breakers + s = parts[0]; + if(!cache[s]){ + cache[s] = Ext.select(s); + } + cache[s].on(parts[1], o[b]); + } + } + cache = null; + } + }, - /** - * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll(). - * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values. - * @param {Number} value The new scroll value. - * @return {Element} this - */ - scrollTo : function(side, value){ - this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value; - return this; - }, + /** + * Utility method for getting the width of the browser scrollbar. This can differ depending on + * operating system settings, such as the theme or font size. + * @param {Boolean} force (optional) true to force a recalculation of the value. + * @return {Number} The width of the scrollbar. + */ + getScrollBarWidth: function(force){ + if(!Ext.isReady){ + return 0; + } - /** - * 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 = document, - body = doc.body, - docElement = doc.documentElement, - l, - t, - ret; + if(force === true || scrollWidth === null){ + // Append our div, do our calculation and then remove it + var div = Ext.getBody().createChild('
    '), + child = div.child('div', true); + var w1 = child.offsetWidth; + div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll'); + var w2 = child.offsetWidth; + div.remove(); + // Need to add 2 to ensure we leave enough space + scrollWidth = w1 - w2 + 2; + } + return scrollWidth; + }, - if(d == doc || d == body){ - if(Ext.isIE && Ext.isStrict){ - l = docElement.scrollLeft; - t = docElement.scrollTop; - }else{ - l = window.pageXOffset; - t = window.pageYOffset; + + // deprecated + combine : function(){ + var as = arguments, l = as.length, r = []; + for(var i = 0; i < l; i++){ + var a = as[i]; + if(Ext.isArray(a)){ + r = r.concat(a); + }else if(a.length !== undefined && !a.substr){ + r = r.concat(Array.prototype.slice.call(a, 0)); + }else{ + r.push(a); + } } - ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)}; - }else{ - ret = {left: d.scrollLeft, top: d.scrollTop}; - } - return ret; + return r; + }, + + /** + * Copies a set of named properties fom the source object to the destination object. + *

    example:

    
    +ImageComponent = Ext.extend(Ext.BoxComponent, {
    +    initComponent: function() {
    +        this.autoEl = { tag: 'img' };
    +        MyComponent.superclass.initComponent.apply(this, arguments);
    +        this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
         }
    -});/**
    - * @class Ext.Element
    - */
    -Ext.Element.addMethods({
    -    /**
    -     * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
    -     * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
    -     * @param {Number} value The new scroll value
    -     * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
    -     * @return {Element} this
    -     */
    -    scrollTo : function(side, value, animate) {
    -        //check if we're scrolling top or left
    -        var top = /top/i.test(side),
    -            me = this,
    -            dom = me.dom,
    -            prop;
    -        if (!animate || !me.anim) {
    -            // just setting the value, so grab the direction
    -            prop = 'scroll' + (top ? 'Top' : 'Left');
    -            dom[prop] = value;
    -        }
    -        else {
    -            // if scrolling top, we need to grab scrollLeft, if left, scrollTop
    -            prop = 'scroll' + (top ? 'Left' : 'Top');
    -            me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}}, me.preanim(arguments, 2), 'scroll');
    -        }
    -        return me;
    -    },
    -    
    -    /**
    -     * Scrolls this element into view within the passed container.
    -     * @param {Mixed} container (optional) The container element to scroll (defaults to 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.Element} this
    -     */
    -    scrollIntoView : function(container, hscroll) {
    -        var c = Ext.getDom(container) || Ext.getBody().dom,
    -            el = this.dom,
    -            o = this.getOffsetsTo(c),
    -            l = o[0] + c.scrollLeft,
    -            t = o[1] + c.scrollTop,
    -            b = t + el.offsetHeight,
    -            r = l + el.offsetWidth,
    -            ch = c.clientHeight,
    -            ct = parseInt(c.scrollTop, 10),
    -            cl = parseInt(c.scrollLeft, 10),
    -            cb = ct + ch,
    -            cr = cl + c.clientWidth;
    +});
    +         * 
    + * @param {Object} dest The destination object. + * @param {Object} source The source object. + * @param {Array/String} names Either an Array of property names, or a comma-delimited list + * of property names to copy. + * @return {Object} The modified object. + */ + copyTo : function(dest, source, names){ + if(typeof names == 'string'){ + names = names.split(/[,;\s]/); + } + Ext.each(names, function(name){ + if(source.hasOwnProperty(name)){ + dest[name] = source[name]; + } + }, this); + return dest; + }, - if (el.offsetHeight > ch || t < ct) { - c.scrollTop = t; - } - else if (b > cb) { - c.scrollTop = b-ch; - } - // corrects IE, other browsers will ignore - c.scrollTop = c.scrollTop; + /** + * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the + * DOM (if applicable) and calling their destroy functions (if available). This method is primarily + * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of + * {@link Ext.util.Observable} can be passed in. Any number of elements and/or components can be + * passed into this function in a single call as separate arguments. + * @param {Mixed} arg1 An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy + * @param {Mixed} arg2 (optional) + * @param {Mixed} etc... (optional) + */ + destroy : function(){ + Ext.each(arguments, function(arg){ + if(arg){ + if(Ext.isArray(arg)){ + this.destroy.apply(this, arg); + }else if(typeof arg.destroy == 'function'){ + arg.destroy(); + }else if(arg.dom){ + arg.remove(); + } + } + }, this); + }, - if (hscroll !== false) { - if (el.offsetWidth > c.clientWidth || l < cl) { - c.scrollLeft = l; - } - else if (r > cr) { - c.scrollLeft = r - c.clientWidth; + /** + * Attempts to destroy and then remove a set of named properties of the passed object. + * @param {Object} o The object (most likely a Component) who's properties you wish to destroy. + * @param {Mixed} arg1 The name of the property to destroy and remove from the object. + * @param {Mixed} etc... More property names to destroy and remove. + */ + destroyMembers : function(o, arg1, arg2, etc){ + for(var i = 1, a = arguments, len = a.length; i < len; i++) { + Ext.destroy(o[a[i]]); + delete o[a[i]]; } - c.scrollLeft = c.scrollLeft; - } - return this; - }, + }, - // private - scrollChildIntoView : function(child, hscroll) { - Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll); - }, - - /** - * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is - * within this element's scrollable range. - * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down"). - * @param {Number} distance How far to scroll the element in pixels - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Boolean} Returns true if a scroll was triggered or false if the element - * was scrolled as far as it could go. - */ - scroll : function(direction, distance, animate) { - if (!this.isScrollable()) { - return false; - } - var el = this.dom, - l = el.scrollLeft, t = el.scrollTop, - w = el.scrollWidth, h = el.scrollHeight, - cw = el.clientWidth, ch = el.clientHeight, - scrolled = false, v, - hash = { - l: Math.min(l + distance, w-cw), - r: v = Math.max(l - distance, 0), - t: Math.max(t - distance, 0), - b: Math.min(t + distance, h-ch) - }; - hash.d = hash.b; - hash.u = hash.t; - - direction = direction.substr(0, 1); - if ((v = hash[direction]) > -1) { - scrolled = true; - this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2)); - } - return scrolled; - } -});/** - * @class Ext.Element - */ -/** - * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element - * @static - * @type Number - */ -Ext.Element.VISIBILITY = 1; -/** - * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element - * @static - * @type Number - */ -Ext.Element.DISPLAY = 2; + /** + * Creates a copy of the passed Array with falsy values removed. + * @param {Array/NodeList} arr The Array from which to remove falsy values. + * @return {Array} The new, compressed Array. + */ + clean : function(arr){ + var ret = []; + Ext.each(arr, function(v){ + if(!!v){ + ret.push(v); + } + }); + return ret; + }, + + /** + * Creates a copy of the passed Array, filtered to contain only unique values. + * @param {Array} arr The Array to filter + * @return {Array} The new Array containing unique values. + */ + unique : function(arr){ + var ret = [], + collect = {}; + + Ext.each(arr, function(v) { + if(!collect[v]){ + ret.push(v); + } + collect[v] = true; + }); + return ret; + }, -Ext.Element.addMethods(function(){ - var VISIBILITY = "visibility", - DISPLAY = "display", - HIDDEN = "hidden", - OFFSETS = "offsets", - NONE = "none", - ORIGINALDISPLAY = 'originalDisplay', - VISMODE = 'visibilityMode', - ELDISPLAY = Ext.Element.DISPLAY, - data = Ext.Element.data, - getDisplay = function(dom){ - var d = data(dom, ORIGINALDISPLAY); - if(d === undefined){ - data(dom, ORIGINALDISPLAY, d = ''); + /** + * Recursively flattens into 1-d Array. Injects Arrays inline. + * @param {Array} arr The array to flatten + * @return {Array} The new, flattened array. + */ + flatten : function(arr){ + var worker = []; + function rFlatten(a) { + Ext.each(a, function(v) { + if(Ext.isArray(v)){ + rFlatten(v); + }else{ + worker.push(v); + } + }); + return worker; } - return d; + return rFlatten(arr); }, - getVisMode = function(dom){ - var m = data(dom, VISMODE); - if(m === undefined){ - data(dom, VISMODE, m = 1); - } - return m; - }; - return { /** - * The element's default display mode (defaults to "") - * @type String + * Returns the minimum value in the Array. + * @param {Array|NodeList} arr The Array from which to select the minimum value. + * @param {Function} comp (optional) a function to perform the comparision which determines minimization. + * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1 + * @return {Object} The minimum value in the Array. */ - originalDisplay : "", - visibilityMode : 1, + min : function(arr, comp){ + var ret = arr[0]; + comp = comp || function(a,b){ return a < b ? -1 : 1; }; + Ext.each(arr, function(v) { + ret = comp(ret, v) == -1 ? ret : v; + }); + return ret; + }, /** - * 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 + * Returns the maximum value in the Array + * @param {Array|NodeList} arr The Array from which to select the maximum value. + * @param {Function} comp (optional) a function to perform the comparision which determines maximization. + * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1 + * @return {Object} The maximum value in the Array. */ - setVisibilityMode : function(visMode){ - data(this.dom, VISMODE, visMode); - return this; + max : function(arr, comp){ + var ret = arr[0]; + comp = comp || function(a,b){ return a > b ? 1 : -1; }; + Ext.each(arr, function(v) { + ret = comp(ret, v) == 1 ? ret : v; + }); + return ret; }, /** - * Perform custom animation on this element. - *
    - * - * @param {Object} args The animation control args - * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35) - * @param {Function} onComplete (optional) Function to call when animation completes - * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to 'easeOut') - * @param {String} animType (optional) 'run' is the default. Can also be 'color', - * 'motion', or 'scroll' - * @return {Ext.Element} this + * @param {Array|NodeList} arr The Array of items to pluck the value from. + * @param {String} prop The property name to pluck from each element. + * @return {Array} The value from each item in the Array. */ - animate : function(args, duration, onComplete, easing, animType){ - this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType); - return this; + pluck : function(arr, prop){ + var ret = []; + Ext.each(arr, function(v) { + ret.push( v[prop] ); + }); + return ret; }, - /* - * @private Internal animation call + /** + *

    Zips N sets together.

    + *
    
    +// Example 1:
    +Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
    +// Example 2:
    +Ext.zip(
    +    [ "+", "-", "+"],
    +    [  12,  10,  22],
    +    [  43,  15,  96],
    +    function(a, b, c){
    +        return "$" + a + "" + b + "." + c
    +    }
    +); // ["$+12.43", "$-10.15", "$+22.96"]
    +         * 
    + * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values. + * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together. + * @return {Array} The zipped set. */ - anim : function(args, opt, animType, defaultDur, defaultEase, cb){ - animType = animType || 'run'; - opt = opt || {}; - var me = this, - anim = Ext.lib.Anim[animType]( - me.dom, - args, - (opt.duration || defaultDur) || .35, - (opt.easing || defaultEase) || 'easeOut', - function(){ - if(cb) cb.call(me); - if(opt.callback) opt.callback.call(opt.scope || me, me, opt); - }, - me - ); - opt.anim = anim; - return anim; + zip : function(){ + var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }), + arrs = parts[0], + fn = parts[1][0], + len = Ext.max(Ext.pluck(arrs, "length")), + ret = []; + + for (var i = 0; i < len; i++) { + ret[i] = []; + if(fn){ + ret[i] = fn.apply(fn, Ext.pluck(arrs, i)); + }else{ + for (var j = 0, aLen = arrs.length; j < aLen; j++){ + ret[i].push( arrs[j][i] ); + } + } + } + return ret; }, - // private legacy anim prep - preanim : function(a, i){ - return !a[i] ? false : (typeof a[i] == 'object' ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]}); + /** + * This is shorthand reference to {@link Ext.ComponentMgr#get}. + * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id} + * @param {String} id The component {@link Ext.Component#id id} + * @return Ext.Component The Component, undefined if not found, or null if a + * Class was found. + */ + getCmp : function(id){ + return Ext.ComponentMgr.get(id); }, /** - * Checks whether the element is currently visible using both visibility and display properties. - * @return {Boolean} True if the element is currently visible, else false + * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash, + * you may want to set this to true. + * @type Boolean */ - isVisible : function() { - return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE); - }, + useShims: E.isIE6 || (E.isMac && E.isGecko2), + // inpired by a similar function in mootools library /** - * 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 + * Returns the type of object that is passed in. If the object passed in is null or undefined it + * return false otherwise it returns one of the following values:
    + * @param {Mixed} object + * @return {String} */ - setVisible : function(visible, animate){ - var me = this, isDisplay, isVisible, isOffsets, - dom = me.dom; + type : function(o){ + if(o === undefined || o === null){ + return false; + } + if(o.htmlElement){ + return 'element'; + } + var t = typeof o; + if(t == 'object' && o.nodeName) { + switch(o.nodeType) { + case 1: return 'element'; + case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace'; + } + } + if(t == 'object' || t == 'function') { + switch(o.constructor) { + case Array: return 'array'; + case RegExp: return 'regexp'; + case Date: return 'date'; + } + if(typeof o.length == 'number' && typeof o.item == 'function') { + return 'nodelist'; + } + } + return t; + }, - // hideMode string override - if (typeof animate == 'string'){ - isDisplay = animate == DISPLAY; - isVisible = animate == VISIBILITY; - isOffsets = animate == OFFSETS; - animate = false; + intercept : function(o, name, fn, scope){ + o[name] = o[name].createInterceptor(fn, scope); + }, + + // internal + callback : function(cb, scope, args, delay){ + if(typeof cb == 'function'){ + if(delay){ + cb.defer(delay, scope, args || []); + }else{ + cb.apply(scope, args || []); + } + } + } + }; +}()); + +/** + * @class Function + * These functions are available on every Function object (any JavaScript function). + */ +Ext.apply(Function.prototype, { + /** + * Create a combined function call sequence of the original function + the passed function. + * The resulting function returns the results of the original function. + * The passed fcn is called with the parameters of the original function. Example usage: + *
    
    +var sayHi = function(name){
    +    alert('Hi, ' + name);
    +}
    +
    +sayHi('Fred'); // alerts "Hi, Fred"
    +
    +var sayGoodbye = sayHi.createSequence(function(name){
    +    alert('Bye, ' + name);
    +});
    +
    +sayGoodbye('Fred'); // both alerts show
    +
    + * @param {Function} fcn The function to sequence + * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed. + * If omitted, defaults to the scope in which the original function is called or the browser window. + * @return {Function} The new function + */ + createSequence : function(fcn, scope){ + var method = this; + return (typeof fcn != 'function') ? + this : + function(){ + var retval = method.apply(this || window, arguments); + fcn.apply(scope || this || window, arguments); + return retval; + }; + } +}); + + +/** + * @class String + * These functions are available as static methods on the JavaScript String object. + */ +Ext.applyIf(String, { + + /** + * Escapes the passed string for ' and \ + * @param {String} string The string to escape + * @return {String} The escaped string + * @static + */ + escape : function(string) { + return string.replace(/('|\\)/g, "\\$1"); + }, + + /** + * Pads the left side of a string with a specified character. This is especially useful + * for normalizing number and date strings. Example usage: + *
    
    +var s = String.leftPad('123', 5, '0');
    +// s now contains the string: '00123'
    +     * 
    + * @param {String} string The original string + * @param {Number} size The total length of the output string + * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ") + * @return {String} The padded string + * @static + */ + leftPad : function (val, size, ch) { + var result = String(val); + if(!ch) { + ch = " "; + } + while (result.length < size) { + result = ch + result; + } + return result; + } +}); + +/** + * Utility function that allows you to easily switch a string between two alternating values. The passed value + * is compared to the current string, and if they are equal, the other value that was passed in is returned. If + * they are already different, the first value passed in is returned. Note that this method returns the new value + * but does not change the current string. + *
    
    +// alternate sort directions
    +sort = sort.toggle('ASC', 'DESC');
    +
    +// instead of conditional logic:
    +sort = (sort == 'ASC' ? 'DESC' : 'ASC');
    +
    + * @param {String} value The value to compare to the current string + * @param {String} other The new value to use if the string already equals the first value passed in + * @return {String} The new value + */ +String.prototype.toggle = function(value, other){ + return this == value ? other : value; +}; + +/** + * Trims whitespace from either end of a string, leaving spaces within the string intact. Example: + *
    
    +var s = '  foo bar  ';
    +alert('-' + s + '-');         //alerts "- foo bar -"
    +alert('-' + s.trim() + '-');  //alerts "-foo bar-"
    +
    + * @return {String} The trimmed string + */ +String.prototype.trim = function(){ + var re = /^\s+|\s+$/g; + return function(){ return this.replace(re, ""); }; +}(); + +// here to prevent dependency on Date.js +/** + Returns the number of milliseconds between this date and date + @param {Date} date (optional) Defaults to now + @return {Number} The diff in milliseconds + @member Date getElapsed + */ +Date.prototype.getElapsed = function(date) { + return Math.abs((date || new Date()).getTime()-this.getTime()); +}; + + +/** + * @class Number + */ +Ext.applyIf(Number.prototype, { + /** + * Checks whether or not the current number is within a desired range. If the number is already within the + * range it is returned, otherwise the min or max value is returned depending on which side of the range is + * exceeded. Note that this method returns the constrained value but does not change the current number. + * @param {Number} min The minimum number in the range + * @param {Number} max The maximum number in the range + * @return {Number} The constrained value if outside the range, otherwise the current value + */ + constrain : function(min, max){ + return Math.min(Math.max(this, min), max); + } +}); +Ext.lib.Dom.getRegion = function(el) { + return Ext.lib.Region.getRegion(el); +}; Ext.lib.Region = function(t, r, b, l) { + var me = this; + me.top = t; + me[1] = t; + me.right = r; + me.bottom = b; + me.left = l; + me[0] = l; + }; + + Ext.lib.Region.prototype = { + contains : function(region) { + var me = this; + return ( region.left >= me.left && + region.right <= me.right && + region.top >= me.top && + region.bottom <= me.bottom ); + + }, + + getArea : function() { + var me = this; + return ( (me.bottom - me.top) * (me.right - me.left) ); + }, + + intersect : function(region) { + var me = this, + t = Math.max(me.top, region.top), + r = Math.min(me.right, region.right), + b = Math.min(me.bottom, region.bottom), + l = Math.max(me.left, region.left); + + if (b >= t && r >= l) { + return new Ext.lib.Region(t, r, b, l); + } + }, + + union : function(region) { + var me = this, + t = Math.min(me.top, region.top), + r = Math.max(me.right, region.right), + b = Math.max(me.bottom, region.bottom), + l = Math.min(me.left, region.left); + + return new Ext.lib.Region(t, r, b, l); + }, + + constrainTo : function(r) { + var me = this; + me.top = me.top.constrain(r.top, r.bottom); + me.bottom = me.bottom.constrain(r.top, r.bottom); + me.left = me.left.constrain(r.left, r.right); + me.right = me.right.constrain(r.left, r.right); + return me; + }, + + adjust : function(t, l, b, r) { + var me = this; + me.top += t; + me.left += l; + me.right += r; + me.bottom += b; + return me; + } + }; + + Ext.lib.Region.getRegion = function(el) { + var p = Ext.lib.Dom.getXY(el), + t = p[1], + r = p[0] + el.offsetWidth, + b = p[1] + el.offsetHeight, + l = p[0]; + + return new Ext.lib.Region(t, r, b, l); + }; Ext.lib.Point = function(x, y) { + if (Ext.isArray(x)) { + y = x[1]; + x = x[0]; + } + var me = this; + me.x = me.right = me.left = me[0] = x; + me.y = me.top = me.bottom = me[1] = y; + }; + + Ext.lib.Point.prototype = new Ext.lib.Region(); +/** + * @class Ext.DomHelper + */ +Ext.apply(Ext.DomHelper, +function(){ + var pub, + afterbegin = 'afterbegin', + afterend = 'afterend', + beforebegin = 'beforebegin', + beforeend = 'beforeend', + confRe = /tag|children|cn|html$/i; + + // private + function doInsert(el, o, returnElement, pos, sibling, append){ + el = Ext.getDom(el); + var newNode; + if (pub.useDom) { + newNode = createDom(o, null); + if (append) { + el.appendChild(newNode); } else { - isDisplay = getVisMode(this.dom) == ELDISPLAY; - isVisible = !isDisplay; + (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el); } + } else { + newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o)); + } + return returnElement ? Ext.get(newNode, true) : newNode; + } - if (!animate || !me.anim) { - if (isDisplay){ - me.setDisplayed(visible); - } else if (isOffsets){ - if (!visible){ - me.hideModeStyles = { - position: me.getStyle('position'), - top: me.getStyle('top'), - left: me.getStyle('left') - }; + // build as dom + /** @ignore */ + function createDom(o, parentNode){ + var el, + doc = document, + useSet, + attr, + val, + cn; - me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'}); - } else { - me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''}); + if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted + el = doc.createDocumentFragment(); // in one shot using a DocumentFragment + for (var i = 0, l = o.length; i < l; i++) { + createDom(o[i], el); + } + } else if (typeof o == 'string') { // Allow a string as a child spec. + el = doc.createTextNode(o); + } else { + el = doc.createElement( o.tag || 'div' ); + useSet = !!el.setAttribute; // In IE some elements don't have setAttribute + for (var attr in o) { + if(!confRe.test(attr)){ + val = o[attr]; + if(attr == 'cls'){ + el.className = val; + }else{ + if(useSet){ + el.setAttribute(attr, val); + }else{ + el[attr] = val; + } } - }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; + Ext.DomHelper.applyStyles(el, o.style); + + if ((cn = o.children || o.cn)) { + createDom(cn, el); + } else if (o.html) { + el.innerHTML = o.html; + } + } + if(parentNode){ + parentNode.appendChild(el); + } + return el; + } + + pub = { + /** + * Creates a new Ext.Template from the DOM object spec. + * @param {Object} o The DOM object spec (and children) + * @return {Ext.Template} The new template + */ + createTemplate : function(o){ + var html = Ext.DomHelper.createHtml(o); + return new Ext.Template(html); }, + /** True to force the use of DOM instead of html fragments @type Boolean */ + useDom : false, + /** - * Toggles the element's visibility or display, depending on visibility mode. - * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object - * @return {Ext.Element} this + * Creates new DOM element(s) and inserts them before el. + * @param {Mixed} el The context element + * @param {Object/String} o The DOM object spec (and children) or raw HTML blob + * @param {Boolean} returnElement (optional) true to return a Ext.Element + * @return {HTMLElement/Ext.Element} The new node + * @hide (repeat) */ - toggle : function(animate){ - var me = this; - me.setVisible(!me.isVisible(), me.preanim(arguments, 0)); - return me; + insertBefore : function(el, o, returnElement){ + return doInsert(el, o, returnElement, beforebegin); }, /** - * 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 + * Creates new DOM element(s) and inserts them after el. + * @param {Mixed} el The context element + * @param {Object} o The DOM object spec (and children) + * @param {Boolean} returnElement (optional) true to return a Ext.Element + * @return {HTMLElement/Ext.Element} The new node + * @hide (repeat) */ - setDisplayed : function(value) { - if(typeof value == "boolean"){ - value = value ? getDisplay(this.dom) : NONE; - } - this.setStyle(DISPLAY, value); - return this; + insertAfter : function(el, o, returnElement){ + return doInsert(el, o, returnElement, afterend, 'nextSibling'); }, - // 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"); - } - } + /** + * Creates new DOM element(s) and inserts them as the first child of el. + * @param {Mixed} el The context element + * @param {Object/String} o The DOM object spec (and children) or raw HTML blob + * @param {Boolean} returnElement (optional) true to return a Ext.Element + * @return {HTMLElement/Ext.Element} The new node + * @hide (repeat) + */ + insertFirst : function(el, o, returnElement){ + return doInsert(el, o, returnElement, afterbegin, 'firstChild'); }, /** - * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}. - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.Element} this + * Creates new DOM element(s) and appends them to el. + * @param {Mixed} el The context element + * @param {Object/String} o The DOM object spec (and children) or raw HTML blob + * @param {Boolean} returnElement (optional) true to return a Ext.Element + * @return {HTMLElement/Ext.Element} The new node + * @hide (repeat) */ - hide : function(animate){ - // hideMode override - if (typeof animate == 'string'){ - this.setVisible(false, animate); - return this; - } - this.setVisible(false, this.preanim(arguments, 0)); - return this; + append: function(el, o, returnElement){ + return doInsert(el, o, returnElement, beforeend, '', true); }, /** - * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}. - * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object - * @return {Ext.Element} this + * Creates new DOM element(s) without inserting them to the document. + * @param {Object/String} o The DOM object spec (and children) or raw HTML blob + * @return {HTMLElement} The new uninserted node */ - show : function(animate){ - // hideMode override - if (typeof animate == 'string'){ - this.setVisible(true, animate); - return this; - } - this.setVisible(true, this.preanim(arguments, 0)); - return this; - } + createDom: createDom }; + return pub; }()); /** - * @class Ext.Element - */ -Ext.Element.addMethods( -function(){ - var VISIBILITY = "visibility", - DISPLAY = "display", - HIDDEN = "hidden", - NONE = "none", - XMASKED = "x-masked", - XMASKEDRELATIVE = "x-masked-relative", - data = Ext.Element.data; - - return { - /** - * Checks whether the element is currently visible using both visibility and display properties. - * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false) - * @return {Boolean} True if the element is currently visible, else false - */ - isVisible : function(deep) { - var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE), - p = this.dom.parentNode; - if(deep !== true || !vis){ - return vis; - } - while(p && !/^body/i.test(p.tagName)){ - if(!Ext.fly(p, '_isVisible').isVisible()){ - return false; - } - p = p.parentNode; - } - return true; - }, - - /** - * Returns true if display is not "none" - * @return {Boolean} - */ - isDisplayed : function() { - return !this.isStyle(DISPLAY, NONE); - }, - - /** - * 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(Ext.Element.DISPLAY); - if(!Ext.isEmpty(display)){ - data(this.dom, 'originalDisplay', display); - } - return this; - }, - - /** - * 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(!/^body/i.test(dom.tagName) && 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); - } - if(me.isMasked()){ - 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(); - }, - - /** - * Creates an iframe shim for this element to keep selects and other windowed objects from - * showing through. - * @return {Ext.Element} The new shim element - */ - createShim : function(){ - var el = document.createElement('iframe'), - shim; - el.frameBorder = '0'; - el.className = 'ext-shim'; - el.src = Ext.SSL_SECURE_URL; - shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom)); - shim.autoBoxAdjust = false; - return shim; - } - }; -}());/** - * @class Ext.Element + * @class Ext.Template */ -Ext.Element.addMethods({ +Ext.apply(Ext.Template.prototype, { /** - * Convenience method for constructing a KeyMap - * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options: - * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)} - * @param {Function} fn The function to call - * @param {Object} scope (optional) The scope (this reference) in which the specified function is executed. Defaults to this Element. - * @return {Ext.KeyMap} The KeyMap created + * @cfg {Boolean} disableFormats Specify true to disable format + * functions in the template. If the template does not contain + * {@link Ext.util.Format format functions}, setting disableFormats + * to true will reduce {@link #apply} time. Defaults to false. + *
    
    +var t = new Ext.Template(
    +    '<div name="{id}">',
    +        '<span class="{cls}">{name} {value}</span>',
    +    '</div>',
    +    {
    +        compiled: true,      // {@link #compile} immediately
    +        disableFormats: true // reduce {@link #apply} time since no formatting
    +    }
    +);
    +     * 
    + * For a list of available format functions, see {@link Ext.util.Format}. + */ + disableFormats : false, + /** + * See {@link #disableFormats}. + * @type Boolean + * @property disableFormats */ - addKeyListener : function(key, fn, scope){ - var config; - if(typeof key != 'object' || Ext.isArray(key)){ - config = { - key: key, - fn: fn, - scope: scope - }; - }else{ - config = { - key : key.key, - shift : key.shift, - ctrl : key.ctrl, - alt : key.alt, - fn: fn, - scope: scope - }; - } - return new Ext.KeyMap(this, config); - }, /** - * Creates a KeyMap for this element - * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details - * @return {Ext.KeyMap} The KeyMap created + * The regular expression used to match template variables + * @type RegExp + * @property + * @hide repeat doc */ - addKeyMap : function(config){ - return new Ext.KeyMap(this, config); - } -}); -(function(){ - // contants - var NULL = null, - UNDEFINED = undefined, - TRUE = true, - FALSE = false, - SETX = "setX", - SETY = "setY", - SETXY = "setXY", - LEFT = "left", - BOTTOM = "bottom", - TOP = "top", - RIGHT = "right", - HEIGHT = "height", - WIDTH = "width", - POINTS = "points", - HIDDEN = "hidden", - ABSOLUTE = "absolute", - VISIBLE = "visible", - MOTION = "motion", - POSITION = "position", - EASEOUT = "easeOut", - /* - * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element - */ - flyEl = new Ext.Element.Flyweight(), - queues = {}, - getObject = function(o){ - return o || {}; - }, - fly = function(dom){ - flyEl.dom = dom; - flyEl.id = Ext.id(dom); - return flyEl; - }, - /* - * Queueing now stored outside of the element due to closure issues - */ - getQueue = function(id){ - if(!queues[id]){ - queues[id] = []; - } - return queues[id]; - }, - setQueue = function(id, value){ - queues[id] = value; - }; - -//Notifies Element that fx methods are available -Ext.enableFx = TRUE; + re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g, + argsRe : /^\s*['"](.*)["']\s*$/, + compileARe : /\\/g, + compileBRe : /(\r\n|\n)/g, + compileCRe : /'/g, -/** - * @class Ext.Fx - *

    A class to provide basic animation and visual effects support. Note: This class is automatically applied - * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}. - * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx must be - * {@link Ext#enableFx included} in order for the Element effects to work.


    - * - *

    Method Chaining

    - *

    It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that - * they return the Element object itself as the method return value, it is not always possible to mix the two in a single - * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced. - * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason, - * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the - * expected results and should be done with care. Also see {@link #callback}.


    - * - *

    Anchor Options for Motion Effects

    - *

    Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element - * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:

    -
    -Value  Description
    ------  -----------------------------
    -tl     The top left corner
    -t      The center of the top edge
    -tr     The top right corner
    -l      The center of the left edge
    -r      The center of the right edge
    -bl     The bottom left corner
    -b      The center of the bottom edge
    -br     The bottom right corner
    -
    - * Note: some Fx methods accept specific custom config parameters. The options shown in the Config Options - * section below are common options that can be passed to any Fx method unless otherwise noted. - * - * @cfg {Function} callback A function called when the effect is finished. Note that effects are queued internally by the - * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together - * and called in sequence (see note for Method Chaining above), for example:
    
    - * el.slideIn().highlight();
    - * 
    - * The callback is intended for any additional code that should run once a particular effect has completed. The Element - * being operated upon is passed as the first parameter. - * - * @cfg {Object} scope The scope (this reference) in which the {@link #callback} function is executed. Defaults to the browser window. - * - * @cfg {String} easing A valid Ext.lib.Easing value for the effect:

    - * - * @cfg {String} afterCls A css class to apply after the effect - * @cfg {Number} duration The length of time (in seconds) that the effect should last - * - * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between - * 0 and 1 inclusive to configure the ending opacity value. - * - * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes - * @cfg {Boolean} useDisplay Whether to use the display CSS property instead of visibility when hiding Elements (only applies to - * effects that end with the element being visually hidden, ignored otherwise) - * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object - * in the form {width:"100px"}, or a function which returns such a specification that will be applied to the - * Element after the effect finishes. - * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs - * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence - * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects) - */ -Ext.Fx = { - - // private - calls the function taking arguments from the argHash based on the key. Returns the return value of the function. - // this is useful for replacing switch statements (for example). - switchStatements : function(key, fn, argHash){ - return fn.apply(this, argHash[key]); - }, - /** - * Slides the element into view. An anchor point can be optionally passed to set the point of - * origin for the slide effect. This function automatically handles wrapping the element with - * a fixed-size container if needed. See the Fx class overview for valid anchor point options. - * Usage: - *
    
    -// default: slide the element in from the top
    -el.slideIn();
    +     * Returns an HTML fragment of this template with the specified values applied.
    +     * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
    +     * @return {String} The HTML fragment
    +     * @hide repeat doc
    +     */
    +    applyTemplate : function(values){
    +        var me = this,
    +            useF = me.disableFormats !== true,
    +            fm = Ext.util.Format,
    +            tpl = me;
     
    -// custom: slide the element in from the right with a 2-second duration
    -el.slideIn('r', { duration: 2 });
    +        if(me.compiled){
    +            return me.compiled(values);
    +        }
    +        function fn(m, name, format, args){
    +            if (format && useF) {
    +                if (format.substr(0, 5) == "this.") {
    +                    return tpl.call(format.substr(5), values[name], values);
    +                } else {
    +                    if (args) {
    +                        // quoted values are required for strings in compiled templates,
    +                        // but for non compiled we need to strip them
    +                        // quoted reversed for jsmin
    +                        var re = me.argsRe;
    +                        args = args.split(',');
    +                        for(var i = 0, len = args.length; i < len; i++){
    +                            args[i] = args[i].replace(re, "$1");
    +                        }
    +                        args = [values[name]].concat(args);
    +                    } else {
    +                        args = [values[name]];
    +                    }
    +                    return fm[format].apply(fm, args);
    +                }
    +            } else {
    +                return values[name] !== undefined ? values[name] : "";
    +            }
    +        }
    +        return me.html.replace(me.re, fn);
    +    },
     
    -// common config options shown with default values
    -el.slideIn('t', {
    -    easing: 'easeOut',
    -    duration: .5
    -});
    -
    - * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't') - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element + /** + * Compiles the template into an internal function, eliminating the RegEx overhead. + * @return {Ext.Template} this + * @hide repeat doc */ - slideIn : function(anchor, o){ - o = getObject(o); + compile : function(){ var me = this, - dom = me.dom, - st = dom.style, - xy, - r, - b, - wrap, - after, - st, - args, - pt, - bw, - bh; - - anchor = anchor || "t"; + fm = Ext.util.Format, + useF = me.disableFormats !== true, + sep = Ext.isGecko ? "+" : ",", + body; - me.queueFx(o, function(){ - xy = fly(dom).getXY(); - // fix display to visibility - fly(dom).fixDisplay(); - - // restore values after effect - r = fly(dom).getFxRestore(); - b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight}; - b.right = b.x + b.width; - b.bottom = b.y + b.height; - - // fixed size for slide - fly(dom).setWidth(b.width).setHeight(b.height); - - // wrap if needed - wrap = fly(dom).fxWrap(r.pos, o, HIDDEN); - - st.visibility = VISIBLE; - st.position = ABSOLUTE; - - // clear out temp styles after slide and unwrap - function after(){ - fly(dom).fxUnwrap(wrap, r.pos, o); - st.width = r.width; - st.height = r.height; - fly(dom).afterFx(o); - } - - // time to calculate the positions - pt = {to: [b.x, b.y]}; - bw = {to: b.width}; - bh = {to: b.height}; - - function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){ - var ret = {}; - fly(wrap).setWidth(ww).setHeight(wh); - if(fly(wrap)[sXY]){ - fly(wrap)[sXY](sXYval); - } - style[s1] = style[s2] = "0"; - if(w){ - ret.width = w - }; - if(h){ - ret.height = h; - } - if(p){ - ret.points = p; + function fn(m, name, format, args){ + if(format && useF){ + args = args ? ',' + args : ""; + if(format.substr(0, 5) != "this."){ + format = "fm." + format + '('; + }else{ + format = 'this.call("'+ format.substr(5) + '", '; + args = ", values"; } - return ret; - }; - - args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, { - t : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL], - l : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL], - r : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt], - b : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt], - tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt], - bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt], - br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt], - tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt] - }); - - st.visibility = VISIBLE; - fly(wrap).show(); + }else{ + args= ''; format = "(values['" + name + "'] == undefined ? '' : "; + } + return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'"; + } - arguments.callee.anim = fly(wrap).fxanim(args, - o, - MOTION, - .5, - EASEOUT, - after); - }); + // branched to use + in gecko and [].join() in others + if(Ext.isGecko){ + body = "this.compiled = function(values){ return '" + + me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn) + + "';};"; + }else{ + body = ["this.compiled = function(values){ return ['"]; + body.push(me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn)); + body.push("'].join('');};"); + body = body.join(''); + } + eval(body); return me; }, - - /** - * Slides the element out of view. An anchor point can be optionally passed to set the end point - * for the slide effect. When the effect is completed, the element will be hidden (visibility = - * 'hidden') but block elements will still take up space in the document. The element must be removed - * from the DOM using the 'remove' config option if desired. This function automatically handles - * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options. - * Usage: - *
    
    -// default: slide the element out to the top
    -el.slideOut();
    -
    -// custom: slide the element out to the right with a 2-second duration
    -el.slideOut('r', { duration: 2 });
     
    -// common config options shown with default values
    -el.slideOut('t', {
    -    easing: 'easeOut',
    -    duration: .5,
    -    remove: false,
    -    useDisplay: false
    -});
    -
    - * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't') - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ - slideOut : function(anchor, o){ - o = getObject(o); - var me = this, - dom = me.dom, - st = dom.style, - xy = me.getXY(), - wrap, - r, - b, - a, - zero = {to: 0}; - - anchor = anchor || "t"; + // private function used to call members + call : function(fnName, value, allValues){ + return this[fnName](value, allValues); + } +}); +Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; +/** + * @class Ext.util.Functions + * @singleton + */ +Ext.util.Functions = { + /** + * Creates an interceptor function. The passed function is called before the original one. If it returns false, + * the original one is not called. The resulting function returns the results of the original function. + * The passed function is called with the parameters of the original function. Example usage: + *
    
    +var sayHi = function(name){
    +    alert('Hi, ' + name);
    +}
     
    -        me.queueFx(o, function(){
    -            
    -            // restore values after effect
    -            r = fly(dom).getFxRestore(); 
    -            b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
    -            b.right = b.x + b.width;
    -            b.bottom = b.y + b.height;
    -                
    -            // fixed size for slide   
    -            fly(dom).setWidth(b.width).setHeight(b.height);
    +sayHi('Fred'); // alerts "Hi, Fred"
     
    -            // wrap if needed
    -            wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
    -                
    -            st.visibility = VISIBLE;
    -            st.position = ABSOLUTE;
    -            fly(wrap).setWidth(b.width).setHeight(b.height);            
    +// create a new function that validates input without
    +// directly modifying the original function:
    +var sayHiToFriend = Ext.createInterceptor(sayHi, function(name){
    +    return name == 'Brian';
    +});
     
    -            function after(){
    -                o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                
    -                fly(dom).fxUnwrap(wrap, r.pos, o);
    -                st.width = r.width;
    -                st.height = r.height;
    -                fly(dom).afterFx(o);
    -            }            
    -            
    -            function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    
    -                var ret = {};
    -                
    -                style[s1] = style[s2] = "0";
    -                ret[p1] = v1;               
    -                if(p2){
    -                    ret[p2] = v2;               
    -                }
    -                if(p3){
    -                    ret[p3] = v3;
    -                }
    -                
    -                return ret;
    +sayHiToFriend('Fred');  // no alert
    +sayHiToFriend('Brian'); // alerts "Hi, Brian"
    +       
    + * @param {Function} origFn The original function. + * @param {Function} newFn The function to call before the original + * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed. + * If omitted, defaults to the scope in which the original function is called or the browser window. + * @return {Function} The new function + */ + createInterceptor: function(origFn, newFn, scope) { + var method = origFn; + if (!Ext.isFunction(newFn)) { + return origFn; + } + else { + return function() { + var me = this, + args = arguments; + newFn.target = me; + newFn.method = origFn; + return (newFn.apply(scope || me || window, args) !== false) ? + origFn.apply(me || window, args) : + null; }; - - a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, { - t : [st, LEFT, BOTTOM, HEIGHT, zero], - l : [st, RIGHT, TOP, WIDTH, zero], - r : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}], - b : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}], - tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero], - bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}], - br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}], - tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}] - }); - - arguments.callee.anim = fly(wrap).fxanim(a, - o, - MOTION, - .5, - EASEOUT, - after); - }); - return me; + } }, /** - * Fades the element out while slowly expanding it in all directions. When the effect is completed, the - * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. - * The element must be removed from the DOM using the 'remove' config option if desired. - * Usage: - *
    
    -// default
    -el.puff();
    +     * Creates a delegate (callback) that sets the scope to obj.
    +     * Call directly on any function. Example: Ext.createDelegate(this.myFunction, this, [arg1, arg2])
    +     * Will create a function that is automatically scoped to obj so that the this variable inside the
    +     * callback points to obj. Example usage:
    +     * 
    
    +var sayHi = function(name){
    +    // Note this use of "this.text" here.  This function expects to
    +    // execute within a scope that contains a text property.  In this
    +    // example, the "this" variable is pointing to the btn object that
    +    // was passed in createDelegate below.
    +    alert('Hi, ' + name + '. You clicked the "' + this.text + '" button.');
    +}
     
    -// common config options shown with default values
    -el.puff({
    -    easing: 'easeOut',
    -    duration: .5,
    -    remove: false,
    -    useDisplay: false
    +var btn = new Ext.Button({
    +    text: 'Say Hi',
    +    renderTo: Ext.getBody()
     });
    -
    - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ - puff : function(o){ - o = getObject(o); - var me = this, - dom = me.dom, - st = dom.style, - width, - height, - r; - me.queueFx(o, function(){ - width = fly(dom).getWidth(); - height = fly(dom).getHeight(); - fly(dom).clearOpacity(); - fly(dom).show(); +// This callback will execute in the scope of the +// button instance. Clicking the button alerts +// "Hi, Fred. You clicked the "Say Hi" button." +btn.on('click', Ext.createDelegate(sayHi, btn, ['Fred'])); +
    + * @param {Function} fn The function to delegate. + * @param {Object} scope (optional) The scope (this reference) in which the function is executed. + * If omitted, defaults to the browser window. + * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller) + * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding, + * if a number the args are inserted at the specified position + * @return {Function} The new function + */ + createDelegate: function(fn, obj, args, appendArgs) { + if (!Ext.isFunction(fn)) { + return fn; + } + return function() { + var callArgs = args || arguments; + if (appendArgs === true) { + callArgs = Array.prototype.slice.call(arguments, 0); + callArgs = callArgs.concat(args); + } + else if (Ext.isNumber(appendArgs)) { + callArgs = Array.prototype.slice.call(arguments, 0); + // copy arguments first + var applyArgs = [appendArgs, 0].concat(args); + // create method call params + Array.prototype.splice.apply(callArgs, applyArgs); + // splice them in + } + return fn.apply(obj || window, callArgs); + }; + }, - // restore values after effect - r = fly(dom).getFxRestore(); - - function after(){ - o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide(); - fly(dom).clearOpacity(); - fly(dom).setPositioning(r.pos); - st.width = r.width; - st.height = r.height; - st.fontSize = ''; - fly(dom).afterFx(o); - } + /** + * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage: + *
    
    +var sayHi = function(name){
    +    alert('Hi, ' + name);
    +}
     
    -            arguments.callee.anim = fly(dom).fxanim({
    -                    width : {to : fly(dom).adjustWidth(width * 2)},
    -                    height : {to : fly(dom).adjustHeight(height * 2)},
    -                    points : {by : [-width * .5, -height * .5]},
    -                    opacity : {to : 0},
    -                    fontSize: {to : 200, unit: "%"}
    -                },
    -                o,
    -                MOTION,
    -                .5,
    -                EASEOUT,
    -                 after);
    -        });
    -        return me;
    +// executes immediately:
    +sayHi('Fred');
    +
    +// executes after 2 seconds:
    +Ext.defer(sayHi, 2000, this, ['Fred']);
    +
    +// this syntax is sometimes useful for deferring
    +// execution of an anonymous function:
    +Ext.defer(function(){
    +    alert('Anonymous');
    +}, 100);
    +       
    + * @param {Function} fn The function to defer. + * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately) + * @param {Object} scope (optional) The scope (this reference) in which the function is executed. + * If omitted, defaults to the browser window. + * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller) + * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding, + * if a number the args are inserted at the specified position + * @return {Number} The timeout id that can be used with clearTimeout + */ + defer: function(fn, millis, obj, args, appendArgs) { + fn = Ext.util.Functions.createDelegate(fn, obj, args, appendArgs); + if (millis > 0) { + return setTimeout(fn, millis); + } + fn(); + return 0; }, + /** - * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television). - * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still - * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired. - * Usage: - *
    
    -// default
    -el.switchOff();
    +     * Create a combined function call sequence of the original function + the passed function.
    +     * The resulting function returns the results of the original function.
    +     * The passed fcn is called with the parameters of the original function. Example usage:
    +     * 
     
    -// all config options shown with default values
    -el.switchOff({
    -    easing: 'easeIn',
    -    duration: .3,
    -    remove: false,
    -    useDisplay: false
    +var sayHi = function(name){
    +    alert('Hi, ' + name);
    +}
    +
    +sayHi('Fred'); // alerts "Hi, Fred"
    +
    +var sayGoodbye = Ext.createSequence(sayHi, function(name){
    +    alert('Bye, ' + name);
     });
    -
    - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element + +sayGoodbye('Fred'); // both alerts show + + * @param {Function} origFn The original function. + * @param {Function} newFn The function to sequence + * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed. + * If omitted, defaults to the scope in which the original function is called or the browser window. + * @return {Function} The new function */ - switchOff : function(o){ - o = getObject(o); - var me = this, - dom = me.dom, - st = dom.style, - r; + createSequence: function(origFn, newFn, scope) { + if (!Ext.isFunction(newFn)) { + return origFn; + } + else { + return function() { + var retval = origFn.apply(this || window, arguments); + newFn.apply(scope || this || window, arguments); + return retval; + }; + } + } +}; - me.queueFx(o, function(){ - fly(dom).clearOpacity(); - fly(dom).clip(); +/** + * Shorthand for {@link Ext.util.Functions#defer} + * @param {Function} fn The function to defer. + * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately) + * @param {Object} scope (optional) The scope (this reference) in which the function is executed. + * If omitted, defaults to the browser window. + * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller) + * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding, + * if a number the args are inserted at the specified position + * @return {Number} The timeout id that can be used with clearTimeout + * @member Ext + * @method defer + */ - // restore values after effect - r = fly(dom).getFxRestore(); - - function after(){ - o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide(); - fly(dom).clearOpacity(); - fly(dom).setPositioning(r.pos); - st.width = r.width; - st.height = r.height; - fly(dom).afterFx(o); +Ext.defer = Ext.util.Functions.defer; + +/** + * Shorthand for {@link Ext.util.Functions#createInterceptor} + * @param {Function} origFn The original function. + * @param {Function} newFn The function to call before the original + * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed. + * If omitted, defaults to the scope in which the original function is called or the browser window. + * @return {Function} The new function + * @member Ext + * @method defer + */ + +Ext.createInterceptor = Ext.util.Functions.createInterceptor; + +/** + * Shorthand for {@link Ext.util.Functions#createSequence} + * @param {Function} origFn The original function. + * @param {Function} newFn The function to sequence + * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed. + * If omitted, defaults to the scope in which the original function is called or the browser window. + * @return {Function} The new function + * @member Ext + * @method defer + */ + +Ext.createSequence = Ext.util.Functions.createSequence; + +/** + * Shorthand for {@link Ext.util.Functions#createDelegate} + * @param {Function} fn The function to delegate. + * @param {Object} scope (optional) The scope (this reference) in which the function is executed. + * If omitted, defaults to the browser window. + * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller) + * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding, + * if a number the args are inserted at the specified position + * @return {Function} The new function + * @member Ext + * @method defer + */ +Ext.createDelegate = Ext.util.Functions.createDelegate; +/** + * @class Ext.util.Observable + */ +Ext.apply(Ext.util.Observable.prototype, function(){ + // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?) + // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call + // private + function getMethodEvent(method){ + var e = (this.methodEvents = this.methodEvents || + {})[method], returnValue, v, cancel, obj = this; + + if (!e) { + this.methodEvents[method] = e = {}; + e.originalFn = this[method]; + e.methodName = method; + e.before = []; + e.after = []; + + var makeCall = function(fn, scope, args){ + if((v = fn.apply(scope || obj, args)) !== undefined){ + if (typeof v == 'object') { + if(v.returnValue !== undefined){ + returnValue = v.returnValue; + }else{ + returnValue = v; + } + cancel = !!v.cancel; + } + else + if (v === false) { + cancel = true; + } + else { + returnValue = v; + } + } }; - fly(dom).fxanim({opacity : {to : 0.3}}, - NULL, - NULL, - .1, - NULL, - function(){ - fly(dom).clearOpacity(); - (function(){ - fly(dom).fxanim({ - height : {to : 1}, - points : {by : [0, fly(dom).getHeight() * .5]} - }, - o, - MOTION, - 0.3, - 'easeIn', - after); - }).defer(100); - }); - }); - return me; - }, + this[method] = function(){ + var args = Array.prototype.slice.call(arguments, 0), + b; + returnValue = v = undefined; + cancel = false; - /** - * Highlights the Element by setting a color (applies to the background-color by default, but can be - * changed using the "attr" config option) and then fading back to the original color. If no original - * color is available, you should provide the "endColor" config option which will be cleared after the animation. - * Usage: -
    
    -// default: highlight background to yellow
    -el.highlight();
    +                for(var i = 0, len = e.before.length; i < len; i++){
    +                    b = e.before[i];
    +                    makeCall(b.fn, b.scope, args);
    +                    if (cancel) {
    +                        return returnValue;
    +                    }
    +                }
     
    -// custom: highlight foreground text to blue for 2 seconds
    -el.highlight("0000ff", { attr: 'color', duration: 2 });
    +                if((v = e.originalFn.apply(obj, args)) !== undefined){
    +                    returnValue = v;
    +                }
     
    -// common config options shown with default values
    -el.highlight("ffff9c", {
    -    attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
    -    endColor: (current color) or "ffffff",
    -    easing: 'easeIn',
    -    duration: 1
    -});
    -
    - * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c') - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ - highlight : function(color, o){ - o = getObject(o); - var me = this, - dom = me.dom, - attr = o.attr || "backgroundColor", - a = {}, - restore; + for(var i = 0, len = e.after.length; i < len; i++){ + b = e.after[i]; + makeCall(b.fn, b.scope, args); + if (cancel) { + return returnValue; + } + } + return returnValue; + }; + } + return e; + } - me.queueFx(o, function(){ - fly(dom).clearOpacity(); - fly(dom).show(); + return { + // these are considered experimental + // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call + // adds an 'interceptor' called before the original method + beforeMethod : function(method, fn, scope){ + getMethodEvent.call(this, method).before.push({ + fn: fn, + scope: scope + }); + }, - function after(){ - dom.style[attr] = restore; - fly(dom).afterFx(o); - } - restore = dom.style[attr]; - a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"}; - arguments.callee.anim = fly(dom).fxanim(a, - o, - 'color', - 1, - 'easeIn', - after); - }); - return me; - }, + // adds a 'sequence' called after the original method + afterMethod : function(method, fn, scope){ + getMethodEvent.call(this, method).after.push({ + fn: fn, + scope: scope + }); + }, - /** - * Shows a ripple of exploding, attenuating borders to draw attention to an Element. - * Usage: -
    
    -// default: a single light blue ripple
    -el.frame();
    +        removeMethodListener: function(method, fn, scope){
    +            var e = this.getMethodEvent(method);
    +            for(var i = 0, len = e.before.length; i < len; i++){
    +                if(e.before[i].fn == fn && e.before[i].scope == scope){
    +                    e.before.splice(i, 1);
    +                    return;
    +                }
    +            }
    +            for(var i = 0, len = e.after.length; i < len; i++){
    +                if(e.after[i].fn == fn && e.after[i].scope == scope){
    +                    e.after.splice(i, 1);
    +                    return;
    +                }
    +            }
    +        },
     
    -// custom: 3 red ripples lasting 3 seconds total
    -el.frame("ff0000", 3, { duration: 3 });
    +        /**
    +         * Relays selected events from the specified Observable as if the events were fired by this.
    +         * @param {Object} o The Observable whose events this object is to relay.
    +         * @param {Array} events Array of event names to relay.
    +         */
    +        relayEvents : function(o, events){
    +            var me = this;
    +            function createHandler(ename){
    +                return function(){
    +                    return me.fireEvent.apply(me, [ename].concat(Array.prototype.slice.call(arguments, 0)));
    +                };
    +            }
    +            for(var i = 0, len = events.length; i < len; i++){
    +                var ename = events[i];
    +                me.events[ename] = me.events[ename] || true;
    +                o.on(ename, createHandler(ename), me);
    +            }
    +        },
     
    -// common config options shown with default values
    -el.frame("C3DAF9", 1, {
    -    duration: 1 //duration of each individual ripple.
    -    // Note: Easing is not configurable and will be ignored if included
    +        /**
    +         * 

    Enables events fired by this Observable to bubble up an owner hierarchy by calling + * this.getBubbleTarget() if present. There is no implementation in the Observable base class.

    + *

    This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default + * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to + * access the required target more quickly.

    + *

    Example:

    
    +Ext.override(Ext.form.Field, {
    +    //  Add functionality to Field's initComponent to enable the change event to bubble
    +    initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
    +        this.enableBubble('change');
    +    }),
    +
    +    //  We know that we want Field's events to bubble directly to the FormPanel.
    +    getBubbleTarget : function() {
    +        if (!this.formPanel) {
    +            this.formPanel = this.findParentByType('form');
    +        }
    +        return this.formPanel;
    +    }
    +});
    +
    +var myForm = new Ext.formPanel({
    +    title: 'User Details',
    +    items: [{
    +        ...
    +    }],
    +    listeners: {
    +        change: function() {
    +            // Title goes red if form has been modified.
    +            myForm.header.setStyle('color', 'red');
    +        }
    +    }
     });
     
    - * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9'). - * @param {Number} count (optional) The number of ripples to display (defaults to 1) - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ - frame : function(color, count, o){ - o = getObject(o); - var me = this, - dom = me.dom, - proxy, - active; + * @param {String/Array} events The event name to bubble, or an Array of event names. + */ + enableBubble : function(events){ + var me = this; + if(!Ext.isEmpty(events)){ + events = Ext.isArray(events) ? events : Array.prototype.slice.call(arguments, 0); + for(var i = 0, len = events.length; i < len; i++){ + var ename = events[i]; + ename = ename.toLowerCase(); + var ce = me.events[ename] || true; + if (typeof ce == 'boolean') { + ce = new Ext.util.Event(me, ename); + me.events[ename] = ce; + } + ce.bubble = true; + } + } + } + }; +}()); - me.queueFx(o, function(){ - color = color || '#C3DAF9'; - if(color.length == 6){ - color = '#' + color; - } - count = count || 1; - fly(dom).show(); - var xy = fly(dom).getXY(), - b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight}, - queue = function(){ - proxy = fly(document.body || document.documentElement).createChild({ - style:{ - position : ABSOLUTE, - 'z-index': 35000, // yee haw - border : '0px solid ' + color - } - }); - return proxy.queueFx({}, animFn); - }; - - - arguments.callee.anim = { - isAnimated: true, - stop: function() { - count = 0; - proxy.stopFx(); - } - }; - - function animFn(){ - var scale = Ext.isBorderBox ? 2 : 1; - active = proxy.anim({ - top : {from : b.y, to : b.y - 20}, - left : {from : b.x, to : b.x - 20}, - borderWidth : {from : 0, to : 10}, - opacity : {from : 1, to : 0}, - height : {from : b.height, to : b.height + 20 * scale}, - width : {from : b.width, to : b.width + 20 * scale} - },{ - duration: o.duration || 1, - callback: function() { - proxy.remove(); - --count > 0 ? queue() : fly(dom).afterFx(o); - } - }); - arguments.callee.anim = { - isAnimated: true, - stop: function(){ - active.stop(); - } - }; - }; - queue(); - }); - return me; - }, +/** + * Starts capture on the specified Observable. All events will be passed + * to the supplied function with the event name + standard signature of the event + * before the event is fired. If the supplied function returns false, + * the event will not fire. + * @param {Observable} o The Observable to capture events from. + * @param {Function} fn The function to call when an event is fired. + * @param {Object} scope (optional) The scope (this reference) in which the function is executed. Defaults to the Observable firing the event. + * @static + */ +Ext.util.Observable.capture = function(o, fn, scope){ + o.fireEvent = o.fireEvent.createInterceptor(fn, scope); +}; + + +/** + * Sets observability on the passed class constructor.

    + *

    This makes any event fired on any instance of the passed class also fire a single event through + * the class allowing for central handling of events on many instances at once.

    + *

    Usage:

    
    +Ext.util.Observable.observeClass(Ext.data.Connection);
    +Ext.data.Connection.on('beforerequest', function(con, options) {
    +    console.log('Ajax request made to ' + options.url);
    +});
    + * @param {Function} c The class constructor to make observable. + * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}. + * @static + */ +Ext.util.Observable.observeClass = function(c, listeners){ + if(c){ + if(!c.fireEvent){ + Ext.apply(c, new Ext.util.Observable()); + Ext.util.Observable.capture(c.prototype, c.fireEvent, c); + } + if(typeof listeners == 'object'){ + c.on(listeners); + } + return c; + } +}; +/** +* @class Ext.EventManager +*/ +Ext.apply(Ext.EventManager, function(){ + var resizeEvent, + resizeTask, + textEvent, + textSize, + D = Ext.lib.Dom, + propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/, + curWidth = 0, + curHeight = 0, + // note 1: IE fires ONLY the keydown event on specialkey autorepeat + // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat + // (research done by @Jan Wolter at http://unixpapa.com/js/key.html) + useKeydown = Ext.isWebKit ? + Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 : + !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera); + + return { + // private + doResizeEvent: function(){ + var h = D.getViewHeight(), + w = D.getViewWidth(); - /** - * Creates a pause before any subsequent queued effects begin. If there are - * no effects queued after the pause it will have no effect. - * Usage: -
    
    -el.pause(1);
    -
    - * @param {Number} seconds The length of time to pause (in seconds) - * @return {Ext.Element} The Element - */ - pause : function(seconds){ - var dom = this.dom, - t; + //whacky problem in IE where the resize event will fire even though the w/h are the same. + if(curHeight != h || curWidth != w){ + resizeEvent.fire(curWidth = w, curHeight = h); + } + }, - this.queueFx({}, function(){ - t = setTimeout(function(){ - fly(dom).afterFx({}); - }, seconds * 1000); - arguments.callee.anim = { - isAnimated: true, - stop: function(){ - clearTimeout(t); - fly(dom).afterFx({}); - } - }; - }); - return this; - }, + /** + * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds), + * passes new viewport width and height to handlers. + * @param {Function} fn The handler function the window resize event invokes. + * @param {Object} scope The scope (this reference) in which the handler function executes. Defaults to the browser window. + * @param {boolean} options Options object as passed to {@link Ext.Element#addListener} + */ + onWindowResize : function(fn, scope, options){ + if(!resizeEvent){ + resizeEvent = new Ext.util.Event(); + resizeTask = new Ext.util.DelayedTask(this.doResizeEvent); + Ext.EventManager.on(window, "resize", this.fireWindowResize, this); + } + resizeEvent.addListener(fn, scope, options); + }, - /** - * Fade an element in (from transparent to opaque). The ending opacity can be specified - * using the {@link #endOpacity} config option. - * Usage: -
    
    -// default: fade in from opacity 0 to 100%
    -el.fadeIn();
    +       // exposed only to allow manual firing
    +       fireWindowResize : function(){
    +           if(resizeEvent){
    +               resizeTask.delay(100);
    +           }
    +       },
     
    -// custom: fade in from opacity 0 to 75% over 2 seconds
    -el.fadeIn({ endOpacity: .75, duration: 2});
    +       /**
    +        * Adds a listener to be notified when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
    +        * @param {Function} fn      The function the event invokes.
    +        * @param {Object}   scope   The scope (this reference) in which the handler function executes. Defaults to the browser window.
    +        * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
    +        */
    +       onTextResize : function(fn, scope, options){
    +           if(!textEvent){
    +               textEvent = new Ext.util.Event();
    +               var textEl = new Ext.Element(document.createElement('div'));
    +               textEl.dom.className = 'x-text-resize';
    +               textEl.dom.innerHTML = 'X';
    +               textEl.appendTo(document.body);
    +               textSize = textEl.dom.offsetHeight;
    +               setInterval(function(){
    +                   if(textEl.dom.offsetHeight != textSize){
    +                       textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
    +                   }
    +               }, this.textResizeInterval);
    +           }
    +           textEvent.addListener(fn, scope, options);
    +       },
     
    -// common config options shown with default values
    -el.fadeIn({
    -    endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
    -    easing: 'easeOut',
    -    duration: .5
    -});
    -
    - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ - fadeIn : function(o){ - o = getObject(o); - var me = this, - dom = me.dom, - to = o.endOpacity || 1; - - me.queueFx(o, function(){ - fly(dom).setOpacity(0); - fly(dom).fixDisplay(); - dom.style.visibility = VISIBLE; - arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}}, - o, NULL, .5, EASEOUT, function(){ - if(to == 1){ - fly(dom).clearOpacity(); - } - fly(dom).afterFx(o); - }); - }); - return me; - }, + /** + * Removes the passed window resize listener. + * @param {Function} fn The method the event invokes + * @param {Object} scope The scope of handler + */ + removeResizeListener : function(fn, scope){ + if(resizeEvent){ + resizeEvent.removeListener(fn, scope); + } + }, - /** - * Fade an element out (from opaque to transparent). The ending opacity can be specified - * using the {@link #endOpacity} config option. Note that IE may require - * {@link #useDisplay}:true in order to redisplay correctly. - * Usage: -
    
    -// default: fade out from the element's current opacity to 0
    -el.fadeOut();
    +       // private
    +       fireResize : function(){
    +           if(resizeEvent){
    +               resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
    +           }
    +       },
     
    -// custom: fade out from the element's current opacity to 25% over 2 seconds
    -el.fadeOut({ endOpacity: .25, duration: 2});
    +        /**
    +        * The frequency, in milliseconds, to check for text resize events (defaults to 50)
    +        */
    +       textResizeInterval : 50,
     
    -// common config options shown with default values
    -el.fadeOut({
    -    endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
    -    easing: 'easeOut',
    -    duration: .5,
    -    remove: false,
    -    useDisplay: false
    -});
    -
    - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ - fadeOut : function(o){ - o = getObject(o); - var me = this, - dom = me.dom, - style = dom.style, - to = o.endOpacity || 0; - - me.queueFx(o, function(){ - arguments.callee.anim = fly(dom).fxanim({ - opacity : {to : to}}, - o, - NULL, - .5, - EASEOUT, - function(){ - if(to == 0){ - Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? - style.display = "none" : - style.visibility = HIDDEN; - - fly(dom).clearOpacity(); - } - fly(dom).afterFx(o); - }); - }); - return me; - }, + /** + * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL) + */ + ieDeferSrc : false, + + // protected, short accessor for useKeydown + getKeyEvent : function(){ + return useKeydown ? 'keydown' : 'keypress'; + }, - /** - * Animates the transition of an element's dimensions from a starting height/width - * to an ending height/width. This method is a convenience implementation of {@link shift}. - * Usage: -
    
    -// change height and width to 100x100 pixels
    -el.scale(100, 100);
    +       // protected for use inside the framework
    +       // detects whether we should use keydown or keypress based on the browser.
    +       useKeydown: useKeydown
    +   };
    +}());
     
    -// common config options shown with default values.  The height and width will default to
    -// the element's existing values if passed as null.
    -el.scale(
    -    [element's width],
    -    [element's height], {
    -        easing: 'easeOut',
    -        duration: .35
    -    }
    -);
    -
    - * @param {Number} width The new width (pass undefined to keep the original width) - * @param {Number} height The new height (pass undefined to keep the original height) - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ - scale : function(w, h, o){ - this.shift(Ext.apply({}, o, { - width: w, - height: h - })); - return this; - }, +Ext.EventManager.on = Ext.EventManager.addListener; - /** - * Animates the transition of any combination of an element's dimensions, xy position and/or opacity. - * Any of these properties not specified in the config object will not be changed. This effect - * requires that at least one new dimension, position or opacity setting must be passed in on - * the config object in order for the function to have any effect. - * Usage: -
    
    -// slide the element horizontally to x position 200 while changing the height and opacity
    -el.shift({ x: 200, height: 50, opacity: .8 });
     
    -// common config options shown with default values.
    -el.shift({
    -    width: [element's width],
    -    height: [element's height],
    -    x: [element's x position],
    -    y: [element's y position],
    -    opacity: [element's opacity],
    -    easing: 'easeOut',
    -    duration: .35
    -});
    -
    - * @param {Object} options Object literal with any of the Fx config options - * @return {Ext.Element} The Element - */ - shift : function(o){ - o = getObject(o); - var dom = this.dom, - a = {}; - - this.queueFx(o, function(){ - for (var prop in o) { - if (o[prop] != UNDEFINED) { - a[prop] = {to : o[prop]}; - } - } - - a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a; - a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a; - - if (a.x || a.y || a.xy) { - a.points = a.xy || - {to : [ a.x ? a.x.to : fly(dom).getX(), - a.y ? a.y.to : fly(dom).getY()]}; - } +Ext.apply(Ext.EventObjectImpl.prototype, { + /** Key constant @type Number */ + BACKSPACE: 8, + /** Key constant @type Number */ + TAB: 9, + /** Key constant @type Number */ + NUM_CENTER: 12, + /** Key constant @type Number */ + ENTER: 13, + /** Key constant @type Number */ + RETURN: 13, + /** Key constant @type Number */ + SHIFT: 16, + /** Key constant @type Number */ + CTRL: 17, + CONTROL : 17, // legacy + /** Key constant @type Number */ + ALT: 18, + /** Key constant @type Number */ + PAUSE: 19, + /** Key constant @type Number */ + CAPS_LOCK: 20, + /** Key constant @type Number */ + ESC: 27, + /** Key constant @type Number */ + SPACE: 32, + /** Key constant @type Number */ + PAGE_UP: 33, + PAGEUP : 33, // legacy + /** Key constant @type Number */ + PAGE_DOWN: 34, + PAGEDOWN : 34, // legacy + /** Key constant @type Number */ + END: 35, + /** Key constant @type Number */ + HOME: 36, + /** Key constant @type Number */ + LEFT: 37, + /** Key constant @type Number */ + UP: 38, + /** Key constant @type Number */ + RIGHT: 39, + /** Key constant @type Number */ + DOWN: 40, + /** Key constant @type Number */ + PRINT_SCREEN: 44, + /** Key constant @type Number */ + INSERT: 45, + /** Key constant @type Number */ + DELETE: 46, + /** Key constant @type Number */ + ZERO: 48, + /** Key constant @type Number */ + ONE: 49, + /** Key constant @type Number */ + TWO: 50, + /** Key constant @type Number */ + THREE: 51, + /** Key constant @type Number */ + FOUR: 52, + /** Key constant @type Number */ + FIVE: 53, + /** Key constant @type Number */ + SIX: 54, + /** Key constant @type Number */ + SEVEN: 55, + /** Key constant @type Number */ + EIGHT: 56, + /** Key constant @type Number */ + NINE: 57, + /** Key constant @type Number */ + A: 65, + /** Key constant @type Number */ + B: 66, + /** Key constant @type Number */ + C: 67, + /** Key constant @type Number */ + D: 68, + /** Key constant @type Number */ + E: 69, + /** Key constant @type Number */ + F: 70, + /** Key constant @type Number */ + G: 71, + /** Key constant @type Number */ + H: 72, + /** Key constant @type Number */ + I: 73, + /** Key constant @type Number */ + J: 74, + /** Key constant @type Number */ + K: 75, + /** Key constant @type Number */ + L: 76, + /** Key constant @type Number */ + M: 77, + /** Key constant @type Number */ + N: 78, + /** Key constant @type Number */ + O: 79, + /** Key constant @type Number */ + P: 80, + /** Key constant @type Number */ + Q: 81, + /** Key constant @type Number */ + R: 82, + /** Key constant @type Number */ + S: 83, + /** Key constant @type Number */ + T: 84, + /** Key constant @type Number */ + U: 85, + /** Key constant @type Number */ + V: 86, + /** Key constant @type Number */ + W: 87, + /** Key constant @type Number */ + X: 88, + /** Key constant @type Number */ + Y: 89, + /** Key constant @type Number */ + Z: 90, + /** Key constant @type Number */ + CONTEXT_MENU: 93, + /** Key constant @type Number */ + NUM_ZERO: 96, + /** Key constant @type Number */ + NUM_ONE: 97, + /** Key constant @type Number */ + NUM_TWO: 98, + /** Key constant @type Number */ + NUM_THREE: 99, + /** Key constant @type Number */ + NUM_FOUR: 100, + /** Key constant @type Number */ + NUM_FIVE: 101, + /** Key constant @type Number */ + NUM_SIX: 102, + /** Key constant @type Number */ + NUM_SEVEN: 103, + /** Key constant @type Number */ + NUM_EIGHT: 104, + /** Key constant @type Number */ + NUM_NINE: 105, + /** Key constant @type Number */ + NUM_MULTIPLY: 106, + /** Key constant @type Number */ + NUM_PLUS: 107, + /** Key constant @type Number */ + NUM_MINUS: 109, + /** Key constant @type Number */ + NUM_PERIOD: 110, + /** Key constant @type Number */ + NUM_DIVISION: 111, + /** Key constant @type Number */ + F1: 112, + /** Key constant @type Number */ + F2: 113, + /** Key constant @type Number */ + F3: 114, + /** Key constant @type Number */ + F4: 115, + /** Key constant @type Number */ + F5: 116, + /** Key constant @type Number */ + F6: 117, + /** Key constant @type Number */ + F7: 118, + /** Key constant @type Number */ + F8: 119, + /** Key constant @type Number */ + F9: 120, + /** Key constant @type Number */ + F10: 121, + /** Key constant @type Number */ + F11: 122, + /** Key constant @type Number */ + F12: 123, - arguments.callee.anim = fly(dom).fxanim(a, - o, - MOTION, - .35, - EASEOUT, - function(){ - fly(dom).afterFx(o); - }); - }); - return this; - }, + /** @private */ + isNavKeyPress : function(){ + var me = this, + k = this.normalizeKey(me.keyCode); + return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down + k == me.RETURN || + k == me.TAB || + k == me.ESC; + }, - /** - * Slides the element while fading it out of view. An anchor point can be optionally passed to set the - * ending point of the effect. - * Usage: - *
    
    -// default: slide the element downward while fading out
    -el.ghost();
    +   isSpecialKey : function(){
    +       var k = this.normalizeKey(this.keyCode);
    +       return (this.type == 'keypress' && this.ctrlKey) ||
    +       this.isNavKeyPress() ||
    +       (k == this.BACKSPACE) || // Backspace
    +       (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
    +       (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
    +   },
     
    -// custom: slide the element out to the right with a 2-second duration
    -el.ghost('r', { duration: 2 });
    +   getPoint : function(){
    +       return new Ext.lib.Point(this.xy[0], this.xy[1]);
    +   },
     
    -// common config options shown with default values
    -el.ghost('b', {
    -    easing: 'easeOut',
    -    duration: .5,
    -    remove: false,
    -    useDisplay: false
    -});
    -
    - * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b') - * @param {Object} options (optional) Object literal with any of the Fx config options - * @return {Ext.Element} The Element + /** + * Returns true if the control, meta, shift or alt key was pressed during this event. + * @return {Boolean} + */ + hasModifier : function(){ + return ((this.ctrlKey || this.altKey) || this.shiftKey); + } +});/** + * @class Ext.Element + */ +Ext.Element.addMethods({ + /** + * Stops the specified event(s) from bubbling and optionally prevents the default action + * @param {String/Array} eventName an event / array of events to stop from bubbling + * @param {Boolean} preventDefault (optional) true to prevent the default action too + * @return {Ext.Element} this */ - ghost : function(anchor, o){ - o = getObject(o); - var me = this, - dom = me.dom, - st = dom.style, - a = {opacity: {to: 0}, points: {}}, - pt = a.points, - r, - w, - h; - - anchor = anchor || "b"; - - me.queueFx(o, function(){ - // restore values after effect - r = fly(dom).getFxRestore(); - w = fly(dom).getWidth(); - h = fly(dom).getHeight(); - - function after(){ - o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide(); - fly(dom).clearOpacity(); - fly(dom).setPositioning(r.pos); - st.width = r.width; - st.height = r.height; - fly(dom).afterFx(o); + swallowEvent : function(eventName, preventDefault) { + var me = this; + function fn(e) { + e.stopPropagation(); + if (preventDefault) { + e.preventDefault(); } - - pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, { - t : [0, -h], - l : [-w, 0], - r : [w, 0], - b : [0, h], - tl : [-w, -h], - bl : [-w, h], - br : [w, h], - tr : [w, -h] + } + + if (Ext.isArray(eventName)) { + Ext.each(eventName, function(e) { + me.on(e, fn); }); - - arguments.callee.anim = fly(dom).fxanim(a, - o, - MOTION, - .5, - EASEOUT, after); - }); + return me; + } + me.on(eventName, fn); return me; }, /** - * Ensures that all effects queued after syncFx is called on the element are - * run concurrently. This is the opposite of {@link #sequenceFx}. - * @return {Ext.Element} The Element + * Create an event handler on this element such that when the event fires and is handled by this element, + * it will be relayed to another object (i.e., fired again as if it originated from that object instead). + * @param {String} eventName The type of event to relay + * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context + * for firing the relayed event */ - syncFx : function(){ - var me = this; - me.fxDefaults = Ext.apply(me.fxDefaults || {}, { - block : FALSE, - concurrent : TRUE, - stopFx : FALSE + relayEvent : function(eventName, observable) { + this.on(eventName, function(e) { + observable.fireEvent(eventName, e); }); - return me; }, /** - * Ensures that all effects queued after sequenceFx is called on the element are - * run in sequence. This is the opposite of {@link #syncFx}. - * @return {Ext.Element} The Element + * Removes worthless text nodes + * @param {Boolean} forceReclean (optional) By default the element + * keeps track if it has been cleaned 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. */ - sequenceFx : function(){ - var me = this; - me.fxDefaults = Ext.apply(me.fxDefaults || {}, { - block : FALSE, - concurrent : FALSE, - stopFx : FALSE - }); - return me; - }, + clean : function(forceReclean) { + var me = this, + dom = me.dom, + n = dom.firstChild, + ni = -1; - /* @private */ - nextFx : function(){ - var ef = getQueue(this.dom.id)[0]; - if(ef){ - ef.call(this); + if (Ext.Element.data(dom, 'isCleaned') && forceReclean !== true) { + return me; } - }, - /** - * Returns true if the element has any effects actively running or queued, else returns false. - * @return {Boolean} True if element has active effects, else false - */ - hasActiveFx : function(){ - return getQueue(this.dom.id)[0]; + while (n) { + var nx = n.nextSibling; + if (n.nodeType == 3 && !(/\S/.test(n.nodeValue))) { + dom.removeChild(n); + } else { + n.nodeIndex = ++ni; + } + n = nx; + } + + Ext.Element.data(dom, 'isCleaned', true); + return me; }, /** - * Stops any running effects and clears the element's internal effects queue if it contains - * any additional effects that haven't started yet. - * @return {Ext.Element} The Element + * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object + * parameter as {@link Ext.Updater#update} + * @return {Ext.Element} this */ - stopFx : function(finish){ - var me = this, - id = me.dom.id; - if(me.hasActiveFx()){ - var cur = getQueue(id)[0]; - if(cur && cur.anim){ - if(cur.anim.isAnimated){ - setQueue(id, [cur]); //clear - cur.anim.stop(finish !== undefined ? finish : TRUE); - }else{ - setQueue(id, []); - } - } - } - return me; + load : function() { + var updateManager = this.getUpdater(); + updateManager.update.apply(updateManager, arguments); + + return this; }, - /* @private */ - beforeFx : function(o){ - if(this.hasActiveFx() && !o.concurrent){ - if(o.stopFx){ - this.stopFx(); - return TRUE; - } - return FALSE; - } - return TRUE; + /** + * Gets this element's {@link Ext.Updater Updater} + * @return {Ext.Updater} The Updater + */ + getUpdater : function() { + return this.updateManager || (this.updateManager = new Ext.Updater(this)); }, /** - * Returns true if the element is currently blocking so that no other effect can be queued - * until this effect is finished, else returns false if blocking is not set. This is commonly - * used to ensure that an effect initiated by a user action runs to completion prior to the - * same effect being restarted (e.g., firing only one effect even if the user clicks several times). - * @return {Boolean} True if blocking, else false + * Update the innerHTML of this element, optionally searching for and processing scripts + * @param {String} html The new HTML + * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false) + * @param {Function} callback (optional) For async script loading you can be notified when the update completes + * @return {Ext.Element} this */ - hasFxBlock : function(){ - var q = getQueue(this.dom.id); - return q && q[0] && q[0].block; - }, + update : function(html, loadScripts, callback) { + if (!this.dom) { + return this; + } + html = html || ""; - /* @private */ - queueFx : function(o, fn){ - var me = fly(this.dom); - if(!me.hasFxBlock()){ - Ext.applyIf(o, me.fxDefaults); - if(!o.concurrent){ - var run = me.beforeFx(o); - fn.block = o.block; - getQueue(me.dom.id).push(fn); - if(run){ - me.nextFx(); - } - }else{ - fn.call(me); + if (loadScripts !== true) { + this.dom.innerHTML = html; + if (typeof callback == 'function') { + callback(); } + return this; } - return me; - }, - /* @private */ - fxWrap : function(pos, o, vis){ - var dom = this.dom, - wrap, - wrapXY; - if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){ - if(o.fixPosition){ - wrapXY = fly(dom).getXY(); + var id = Ext.id(), + dom = this.dom; + + html += ''; + + Ext.lib.Event.onAvailable(id, function() { + var DOC = document, + hd = DOC.getElementsByTagName("head")[0], + re = /(?:]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig, + srcRe = /\ssrc=([\'\"])(.*?)\1/i, + typeRe = /\stype=([\'\"])(.*?)\1/i, + match, + attrs, + srcMatch, + typeMatch, + el, + s; + + while ((match = re.exec(html))) { + attrs = match[1]; + srcMatch = attrs ? attrs.match(srcRe) : false; + if (srcMatch && srcMatch[2]) { + s = DOC.createElement("script"); + s.src = srcMatch[2]; + typeMatch = attrs.match(typeRe); + if (typeMatch && typeMatch[2]) { + s.type = typeMatch[2]; + } + hd.appendChild(s); + } else if (match[2] && match[2].length > 0) { + if (window.execScript) { + window.execScript(match[2]); + } else { + window.eval(match[2]); + } + } } - var div = document.createElement("div"); - div.style.visibility = vis; - wrap = dom.parentNode.insertBefore(div, dom); - fly(wrap).setPositioning(pos); - if(fly(wrap).isStyle(POSITION, "static")){ - fly(wrap).position("relative"); + + el = DOC.getElementById(id); + if (el) { + Ext.removeNode(el); } - fly(dom).clearPositioning('auto'); - fly(wrap).clip(); - wrap.appendChild(dom); - if(wrapXY){ - fly(wrap).setXY(wrapXY); + + if (typeof callback == 'function') { + callback(); } - } - return wrap; + }); + dom.innerHTML = html.replace(/(?:)((\n|\r|.)*?)(?:<\/script>)/ig, ""); + return this; }, - /* @private */ - fxUnwrap : function(wrap, pos, o){ - var dom = this.dom; - fly(dom).clearPositioning(); - fly(dom).setPositioning(pos); - if(!o.wrap){ - var pn = fly(wrap).dom.parentNode; - pn.insertBefore(dom, wrap); - fly(wrap).remove(); - } + // inherit docs, overridden so we can add removeAnchor + removeAllListeners : function() { + this.removeAnchor(); + Ext.EventManager.removeAll(this.dom); + return this; }, - /* @private */ - getFxRestore : function(){ - var st = this.dom.style; - return {pos: this.getPositioning(), width: st.width, height : st.height}; - }, + /** + * Creates a proxy element of this element + * @param {String/Object} config The class name of the proxy element or a DomHelper config object + * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body) + * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false) + * @return {Ext.Element} The new proxy element + */ + createProxy : function(config, renderTo, matchBox) { + config = (typeof config == 'object') ? config : {tag : "div", cls: config}; - /* @private */ - afterFx : function(o){ - var dom = this.dom, - id = dom.id; - if(o.afterStyle){ - fly(dom).setStyle(o.afterStyle); - } - if(o.afterCls){ - fly(dom).addClass(o.afterCls); - } - if(o.remove == TRUE){ - fly(dom).remove(); - } - if(o.callback){ - o.callback.call(o.scope, fly(dom)); - } - if(!o.concurrent){ - getQueue(id).shift(); - fly(dom).nextFx(); - } - }, + var me = this, + proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) : + Ext.DomHelper.insertBefore(me.dom, config, true); - /* @private */ - fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){ - animType = animType || 'run'; - opt = opt || {}; - var anim = Ext.lib.Anim[animType]( - this.dom, - args, - (opt.duration || defaultDur) || .35, - (opt.easing || defaultEase) || EASEOUT, - cb, - this - ); - opt.anim = anim; - return anim; + if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded + proxy.setBox(me.getBox()); + } + return proxy; } -}; - -// backwards compat -Ext.Fx.resize = Ext.Fx.scale; +}); -//When included, Ext.Fx is automatically applied to Element so that all basic -//effects are available directly via the Element API -Ext.Element.addMethods(Ext.Fx); -})(); +Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater; /** - * @class Ext.CompositeElementLite - *

    This class encapsulates a collection of DOM elements, providing methods to filter - * members, or to perform collective actions upon the whole set.

    - *

    Although they are not listed, this class supports all of the methods of {@link Ext.Element} and - * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.

    - * Example:
    
    -var els = Ext.select("#some-el div.some-class");
    -// or select directly from an existing element
    -var el = Ext.get('some-el');
    -el.select('div.some-class');
    -
    -els.setWidth(100); // all elements become 100 width
    -els.hide(true); // all elements fade out and hide
    -// or
    -els.setWidth(100).hide(true);
    -
    + * @class Ext.Element
      */
    -Ext.CompositeElementLite = function(els, root){
    -    /**
    -     * 

    The Array of DOM elements which this CompositeElement encapsulates. Read-only.

    - *

    This will not usually be accessed in developers' code, but developers wishing - * to augment the capabilities of the CompositeElementLite class may use it when adding - * methods to the class.

    - *

    For example to add the nextAll method to the class to add all - * following siblings of selected elements, the code would be

    -Ext.override(Ext.CompositeElementLite, {
    -    nextAll: function() {
    -        var els = this.elements, i, l = els.length, n, r = [], ri = -1;
    -
    -//      Loop through all elements in this Composite, accumulating
    -//      an Array of all siblings.
    -        for (i = 0; i < l; i++) {
    -            for (n = els[i].nextSibling; n; n = n.nextSibling) {
    -                r[++ri] = n;
    -            }
    -        }
    -
    -//      Add all found siblings to this Composite
    -        return this.add(r);
    -    }
    -});
    - * @type Array - * @property elements - */ - this.elements = []; - this.add(els, root); - this.el = new Ext.Element.Flyweight(); -}; - -Ext.CompositeElementLite.prototype = { - isComposite: true, - - // private - getElement : function(el){ - // Set the shared flyweight dom property to the current element - var e = this.el; - e.dom = el; - e.id = el.id; - return e; - }, - - // private - transformElement : function(el){ - return Ext.getDom(el); - }, - - /** - * Returns the number of elements in this Composite. - * @return Number - */ - getCount : function(){ - return this.elements.length; - }, +Ext.Element.addMethods({ /** - * Adds elements to this Composite object. - * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added. - * @return {CompositeElement} This Composite object. + * 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 */ - add : function(els, root){ - var me = this, - elements = me.elements; - if(!els){ - return this; - } - if(typeof els == "string"){ - els = Ext.Element.selectorFunction(els, root); - }else if(els.isComposite){ - els = els.elements; - }else if(!Ext.isIterable(els)){ - els = [els]; - } - - for(var i = 0, len = els.length; i < len; ++i){ - elements.push(me.transformElement(els[i])); - } - return me; + 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, + vp = me.dom == document.body || me.dom == document, + w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(), + h = s.height || vp ? Ext.lib.Dom.getViewHeight() : 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 * 0.5), r(h * 0.5)], + t : [r(w * 0.5), 0], + l : [0, r(h * 0.5)], + r : [w, r(h * 0.5)], + b : [r(w * 0.5), h], + tl : [0, 0], + bl : [0, h], + br : [w, h], + tr : [w, 0] + }; + + xy = hash[anchor]; + return [xy[0] + extraX, xy[1] + extraY]; }, - invoke : function(fn, args){ - var me = this, - els = me.elements, - len = els.length, - e, - i; + /** + * 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, + scroll = !Ext.isEmpty(monitorScroll), + action = function(){ + Ext.fly(dom).alignTo(el, alignment, offsets, animate); + Ext.callback(callback, Ext.fly(dom)); + }, + anchor = this.getAnchor(); + + // previous listener anchor, remove it + this.removeAnchor(); + Ext.apply(anchor, { + fn: action, + scroll: scroll + }); - for(i = 0; i < len; i++) { - e = els[i]; - if(e){ - Ext.Element.prototype[fn].apply(me.getElement(e), args); - } + Ext.EventManager.onWindowResize(action, null); + + if(scroll){ + Ext.EventManager.on(window, 'scroll', action, null, + {buffer: !isNaN(monitorScroll) ? monitorScroll : 50}); } + action.call(me); // align immediately return me; }, + /** - * Returns a flyweight Element of the dom element object at the specified index - * @param {Number} index - * @return {Ext.Element} + * Remove any anchor to this element. See {@link #anchorTo}. + * @return {Ext.Element} this */ - item : function(index){ + removeAnchor : function(){ var me = this, - el = me.elements[index], - out = null; - - if(el){ - out = me.getElement(el); + anchor = this.getAnchor(); + + if(anchor && anchor.fn){ + Ext.EventManager.removeResizeListener(anchor.fn); + if(anchor.scroll){ + Ext.EventManager.un(window, 'scroll', anchor.fn); + } + delete anchor.fn; } - return out; + return me; }, - - // fixes scope with flyweight - addListener : function(eventName, handler, scope, opt){ - var els = this.elements, - len = els.length, - i, e; - - for(i = 0; iCalls the passed function for each element in this composite.

    - * @param {Function} fn The function to call. The function is passed the following parameters:
      - *
    • el : Element
      The current Element in the iteration. - * This is the flyweight (shared) Ext.Element instance, so if you require a - * a reference to the dom node, use el.dom.
    • - *
    • c : Composite
      This Composite object.
    • - *
    • idx : Number
      The zero-based index in the iteration.
    • - *
    - * @param {Object} scope (optional) The scope (this reference) in which the function is executed. (defaults to the Element) - * @return {CompositeElement} this + * 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 (optional, defaults to "tl-bl?") The position to align to. + * @param {Array} offsets (optional) Offset the positioning by [x, y] + * @return {Array} [x, y] */ - each : function(fn, scope){ + getAlignToXY : function(el, p, o){ + el = Ext.get(el); + + if(!el || !el.dom){ + throw "Element.alignToXY with an element that doesn't exist"; + } + + o = o || [0,0]; + p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase(); + var me = this, - els = me.elements, - len = els.length, - i, e; + d = me.dom, + a1, + a2, + x, + y, + //constrain the aligned el to viewport if necessary + w, + h, + r, + dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie + dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie + p1y, + p1x, + p2y, + p2x, + swapY, + swapX, + doc = document, + 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.alignTo with an invalid alignment " + p; + } + + p1 = m[1]; + p2 = m[2]; + c = !!m[3]; - for(i = 0; i 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 me; - }, - - /** - * Clears this Composite and adds the elements passed. - * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite. - * @return {CompositeElement} this - */ - fill : function(els){ - var me = this; - me.elements = []; - me.add(els); - return me; + return [x,y]; }, /** - * Filters this composite to only elements that match the passed selector. - * @param {String/Function} selector A string CSS selector or a comparison function. - * The comparison function will be called with the following arguments:
      - *
    • el : Ext.Element
      The current DOM element.
    • - *
    • index : Number
      The current index within the collection.
    • + * Aligns this element with another element relative to the specified anchor points. If the other element is the + * document it aligns it to the viewport. + * The position parameter is optional, and can be specified in any one of the following formats: + *
        + *
      • Blank: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").
      • + *
      • One anchor (deprecated): The passed anchor position is used as the target element's anchor point. + * The element being aligned will position its top-left corner (tl) to that point. This method has been + * deprecated in favor of the newer two anchor syntax below.
      • + *
      • Two anchors: If two values from the table below are passed separated by a dash, the first value is used as the + * element's anchor point, and the second value is used as the target's anchor point.
      • *
      - * @return {CompositeElement} this - */ - filter : function(selector){ - var els = [], - me = this, - fn = Ext.isFunction(selector) ? selector - : function(el){ - return el.is(selector); - }; + * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of + * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to + * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than + * that specified in order to enforce the viewport constraints. + * Following are all of the supported anchor positions: +
      +Value  Description
      +-----  -----------------------------
      +tl     The top left corner (default)
      +t      The center of the top edge
      +tr     The top right corner
      +l      The center of the left edge
      +c      In the center of the element
      +r      The center of the right edge
      +bl     The bottom left corner
      +b      The center of the bottom edge
      +br     The bottom right corner
      +
      +Example Usage: +
      
      +// align el to other-el using the default positioning ("tl-bl", non-constrained)
      +el.alignTo("other-el");
       
      -        me.each(function(el, self, i) {
      -            if (fn(el, i) !== false) {
      -                els[els.length] = me.transformElement(el);
      -            }
      -        });
      -        
      -        me.elements = els;
      -        return me;
      -    },
      +// align the top left corner of el with the top right corner of other-el (constrained to viewport)
      +el.alignTo("other-el", "tr?");
       
      -    /**
      -     * Find the index of the passed element within the composite collection.
      -     * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
      -     * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
      +// align the bottom right corner of el with the center left edge of other-el
      +el.alignTo("other-el", "br-l?");
      +
      +// align the center of el with the bottom left corner of other-el and
      +// adjust the x position by -6 pixels (and the y position by 0)
      +el.alignTo("other-el", "c-bl", [-6, 0]);
      +
      + * @param {Mixed} element The element to align to. + * @param {String} position (optional, defaults to "tl-bl?") 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 + * @return {Ext.Element} this */ - indexOf : function(el){ - return this.elements.indexOf(this.transformElement(el)); + alignTo : function(element, position, offsets, animate){ + var me = this; + return me.setXY(me.getAlignToXY(element, position, offsets), + me.preanim && !!animate ? me.preanim(arguments, 3) : false); + }, + + // private ==> used outside of core + adjustForConstraints : function(xy, parent, offsets){ + return this.getConstrainToXY(parent || document, false, offsets, xy) || xy; }, - /** - * Replaces the specified element with the passed element. - * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite - * to replace. - * @param {Mixed} replacement The id of an element or the Element itself. - * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too. - * @return {CompositeElement} this - */ - replaceElement : function(el, replacement, domReplace){ - var index = !isNaN(el) ? el : this.indexOf(el), - d; - if(index > -1){ - replacement = Ext.getDom(replacement); - if(domReplace){ - d = this.elements[index]; - d.parentNode.insertBefore(replacement, d); - Ext.removeNode(d); + // 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){ + el = Ext.get(el); + offsets = offsets ? Ext.applyIf(offsets, os) : os; + + var vw, vh, vx = 0, vy = 0; + if(el.dom == document.body || el.dom == document){ + vw =Ext.lib.Dom.getViewWidth(); + vh = Ext.lib.Dom.getViewHeight(); + }else{ + vw = el.dom.clientWidth; + vh = el.dom.clientHeight; + if(!local){ + var vxy = el.getXY(); + vx = vxy[0]; + vy = vxy[1]; + } } - this.elements.splice(index, 1, replacement); - } - return this; - }, - /** - * Removes all elements. - */ - clear : function(){ - this.elements = []; - } -}; + var s = el.getScroll(); -Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener; + vx += offsets.left + s.left; + vy += offsets.top + s.top; -(function(){ -var fnName, - ElProto = Ext.Element.prototype, - CelProto = Ext.CompositeElementLite.prototype; - -for(fnName in ElProto){ - if(Ext.isFunction(ElProto[fnName])){ - (function(fnName){ - CelProto[fnName] = CelProto[fnName] || function(){ - return this.invoke(fnName, arguments); - }; - }).call(CelProto, fnName); + vw -= offsets.right; + vh -= offsets.bottom; - } -} -})(); + var vr = vx + vw, + vb = vy + vh, + xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]), + x = xy[0], y = xy[1], + offset = this.getConstrainOffset(), + w = this.dom.offsetWidth + offset, + h = this.dom.offsetHeight + offset; -if(Ext.DomQuery){ - Ext.Element.selectorFunction = Ext.DomQuery.select; -} + // only move it if it needs it + var moved = false; -/** - * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods - * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or - * {@link Ext.CompositeElementLite CompositeElementLite} object. - * @param {String/Array} selector The CSS selector or an array of elements - * @param {HTMLElement/String} root (optional) The root element of the query or id of the root - * @return {CompositeElementLite/CompositeElement} - * @member Ext.Element - * @method select - */ -Ext.Element.select = function(selector, root){ - var els; - if(typeof selector == "string"){ - els = Ext.Element.selectorFunction(selector, root); - }else if(selector.length !== undefined){ - els = selector; - }else{ - throw "Invalid selector"; - } - return new Ext.CompositeElementLite(els); -}; -/** - * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods - * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or - * {@link Ext.CompositeElementLite CompositeElementLite} object. - * @param {String/Array} selector The CSS selector or an array of elements - * @param {HTMLElement/String} root (optional) The root element of the query or id of the root - * @return {CompositeElementLite/CompositeElement} - * @member Ext - * @method select - */ -Ext.select = Ext.Element.select; -/** - * @class Ext.CompositeElementLite - */ -Ext.apply(Ext.CompositeElementLite.prototype, { - addElements : function(els, root){ - if(!els){ - return this; - } - if(typeof els == "string"){ - els = Ext.Element.selectorFunction(els, root); - } - var yels = this.elements; - Ext.each(els, function(e) { - yels.push(Ext.get(e)); - }); - return this; - }, + // 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; + }; + }(), + + + +// el = Ext.get(el); +// offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0}); - /** - * Returns the first Element - * @return {Ext.Element} - */ - first : function(){ - return this.item(0); - }, +// var me = this, +// doc = document, +// s = el.getScroll(), +// vxy = el.getXY(), +// vx = offsets.left + s.left, +// vy = offsets.top + s.top, +// vw = -offsets.right, +// vh = -offsets.bottom, +// vr, +// vb, +// xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]), +// x = xy[0], +// y = xy[1], +// w = me.dom.offsetWidth, h = me.dom.offsetHeight, +// moved = false; // only move it if it needs it +// +// +// if(el.dom == doc.body || el.dom == doc){ +// vw += Ext.lib.Dom.getViewWidth(); +// vh += Ext.lib.Dom.getViewHeight(); +// }else{ +// vw += el.dom.clientWidth; +// vh += el.dom.clientHeight; +// if(!local){ +// vx += vxy[0]; +// vy += vxy[1]; +// } +// } - /** - * Returns the last Element - * @return {Ext.Element} - */ - last : function(){ - return this.item(this.getCount()-1); +// // first validate right/bottom +// if(x + w > vx + vw){ +// x = vx + vw - w; +// moved = true; +// } +// if(y + h > vy + vh){ +// y = vy + vh - 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; +// }, + + // private, used internally + getConstrainOffset : function(){ + return 0; }, - + /** - * Returns true if this composite contains the passed element - * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection. - * @return Boolean - */ - contains : function(el){ - return this.indexOf(el) != -1; + * Calculates the x, y to center this element on the screen + * @return {Array} The x, y values [x, y] + */ + getCenterXY : function(){ + return this.getAlignToXY(document, 'c-c'); }, /** - * Removes the specified element(s). - * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite - * or an array of any of those. - * @param {Boolean} removeDom (optional) True to also remove the element from the document - * @return {CompositeElement} this + * Centers the Element in either the viewport, or another Element. + * @param {Mixed} centerIn (optional) The element in which to center the element. */ - removeElement : function(keys, removeDom){ - var me = this, - els = this.elements, - el; - Ext.each(keys, function(val){ - if ((el = (els[val] || els[val = me.indexOf(val)]))) { - if(removeDom){ - if(el.dom){ - el.remove(); - }else{ - Ext.removeNode(el); - } - } - els.splice(val, 1); - } - }); - return this; - } + center : function(centerIn){ + return this.alignTo(centerIn || document, 'c-c'); + } }); /** - * @class Ext.CompositeElement - * @extends Ext.CompositeElementLite - *

      This class encapsulates a collection of DOM elements, providing methods to filter - * members, or to perform collective actions upon the whole set.

      - *

      Although they are not listed, this class supports all of the methods of {@link Ext.Element} and - * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.

      - *

      All methods return this and can be chained.

      - * Usage: -
      
      -var els = Ext.select("#some-el div.some-class", true);
      -// or select directly from an existing element
      -var el = Ext.get('some-el');
      -el.select('div.some-class', true);
      -
      -els.setWidth(100); // all elements become 100 width
      -els.hide(true); // all elements fade out and hide
      -// or
      -els.setWidth(100).hide(true);
      -
      + * @class Ext.Element */ -Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, { - - constructor : function(els, root){ - this.elements = []; - this.add(els, root); - }, - - // private - getElement : function(el){ - // In this case just return it, since we already have a reference to it - return el; - }, - - // private - transformElement : function(el){ - return Ext.get(el); - } - - /** - * Adds elements to this composite. - * @param {String/Array} els A string CSS selector, an array of elements or an element - * @return {CompositeElement} this - */ - - /** - * Returns the Element object at the specified index - * @param {Number} index - * @return {Ext.Element} - */ - +Ext.Element.addMethods({ /** - * Iterates each element in this composite - * calling the supplied function using {@link Ext#each}. - * @param {Function} fn The function to be called with each - * element. If the supplied function returns false, - * iteration stops. This function is called with the following arguments: - *
        - *
      • element : Ext.Element
        The element at the current index - * in the composite
      • - *
      • composite : Object
        This composite.
      • - *
      • index : Number
        The current index within the composite
      • - *
      - * @param {Object} scope (optional) The scope ( reference) in which the specified function is executed. - * Defaults to the element at the current index - * within the composite. - * @return {CompositeElement} this + * 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 {CompositeElement/CompositeElementLite} The composite element */ -}); - -/** - * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods - * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or - * {@link Ext.CompositeElementLite CompositeElementLite} object. - * @param {String/Array} selector The CSS selector or an array of elements - * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object) - * @param {HTMLElement/String} root (optional) The root element of the query or id of the root - * @return {CompositeElementLite/CompositeElement} - * @member Ext.Element - * @method select - */ -Ext.Element.select = function(selector, unique, root){ - var els; - if(typeof selector == "string"){ - els = Ext.Element.selectorFunction(selector, root); - }else if(selector.length !== undefined){ - els = selector; - }else{ - throw "Invalid selector"; + select : function(selector, unique){ + return Ext.Element.select(selector, unique, this.dom); } - - return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els); -}; - -/** - * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods - * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or - * {@link Ext.CompositeElementLite CompositeElementLite} object. - * @param {String/Array} selector The CSS selector or an array of elements - * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object) - * @param {HTMLElement/String} root (optional) The root element of the query or id of the root - * @return {CompositeElementLite/CompositeElement} - * @member Ext - * @method select +});/** + * @class Ext.Element */ -Ext.select = Ext.Element.select;(function(){ - var BEFOREREQUEST = "beforerequest", - REQUESTCOMPLETE = "requestcomplete", - REQUESTEXCEPTION = "requestexception", - UNDEFINED = undefined, - LOAD = 'load', - POST = 'POST', - GET = 'GET', - WINDOW = window; - - /** - * @class Ext.data.Connection - * @extends Ext.util.Observable - *

      The class encapsulates a connection to the page's originating domain, allowing requests to be made - * either to a configured URL, or to a URL specified at request time.

      - *

      Requests made by this class are asynchronous, and will return immediately. No data from - * the server will be available to the statement immediately following the {@link #request} call. - * To process returned data, use a - * success callback - * in the request options object, - * or an {@link #requestcomplete event listener}.

      - *

      File Uploads

      File uploads are not performed using normal "Ajax" techniques, that - * is they are not performed using XMLHttpRequests. Instead the form is submitted in the standard - * manner with the DOM <form> element temporarily modified to have its - * target set to refer - * to a dynamically generated, hidden <iframe> which is inserted into the document - * but removed after the return data has been gathered.

      - *

      The server response is parsed by the browser to create the document for the IFRAME. If the - * server is using JSON to send the return object, then the - * Content-Type header - * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.

      - *

      Characters which are significant to an HTML parser must be sent as HTML entities, so encode - * "<" as "&lt;", "&" as "&amp;" etc.

      - *

      The response text is retrieved from the document, and a fake XMLHttpRequest object - * is created containing a responseText property in order to conform to the - * requirements of event handlers and callbacks.

      - *

      Be aware that file upload packets are sent with the content type multipart/form - * and some server technologies (notably JEE) may require some custom processing in order to - * retrieve parameter names and parameter values from the packet content.

      - *

      Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.

      - * @constructor - * @param {Object} config a configuration object. - */ - Ext.data.Connection = function(config){ - Ext.apply(this, config); - this.addEvents( - /** - * @event beforerequest - * Fires before a network request is made to retrieve a data object. - * @param {Connection} conn This Connection object. - * @param {Object} options The options config object passed to the {@link #request} method. - */ - BEFOREREQUEST, - /** - * @event requestcomplete - * Fires if the request was successfully completed. - * @param {Connection} conn This Connection object. - * @param {Object} response The XHR object containing the response data. - * See The XMLHttpRequest Object - * for details. - * @param {Object} options The options config object passed to the {@link #request} method. - */ - REQUESTCOMPLETE, - /** - * @event requestexception - * Fires if an error HTTP status was returned from the server. - * See HTTP Status Code Definitions - * for details of HTTP status codes. - * @param {Connection} conn This Connection object. - * @param {Object} response The XHR object containing the response data. - * See The XMLHttpRequest Object - * for details. - * @param {Object} options The options config object passed to the {@link #request} method. - */ - REQUESTEXCEPTION - ); - Ext.data.Connection.superclass.constructor.call(this); +Ext.apply(Ext.Element.prototype, function() { + var GETDOM = Ext.getDom, + GET = Ext.get, + DH = Ext.DomHelper; + + return { + /** + * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element + * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those. + * @param {String} where (optional) 'before' or 'after' defaults to before + * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element + * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned. + */ + insertSibling: function(el, where, returnDom){ + var me = this, + rt, + isAfter = (where || 'before').toLowerCase() == 'after', + insertEl; + + if(Ext.isArray(el)){ + insertEl = me; + Ext.each(el, function(e) { + rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom); + if(isAfter){ + insertEl = rt; + } + }); + return rt; + } + + el = el || {}; + + if(el.nodeType || el.dom){ + rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom); + if (!returnDom) { + rt = GET(rt); + } + }else{ + if (isAfter && !me.dom.nextSibling) { + rt = DH.append(me.dom.parentNode, el, !returnDom); + } else { + rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom); + } + } + return rt; + } }; +}());/** + * @class Ext.Element + */ - Ext.extend(Ext.data.Connection, Ext.util.Observable, { - /** - * @cfg {String} url (Optional)

      The default URL to be used for requests to the server. Defaults to undefined.

      - *

      The url config may be a function which returns the URL to use for the Ajax request. The scope - * (this reference) of the function is the scope option passed to the {@link #request} method.

      - */ - /** - * @cfg {Object} extraParams (Optional) An object containing properties which are used as - * extra parameters to each request made by this object. (defaults to undefined) - */ - /** - * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added - * to each request made by this object. (defaults to undefined) - */ - /** - * @cfg {String} method (Optional) The default HTTP method to be used for requests. - * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used; - * otherwise, GET will be used.) - */ - /** - * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000) - */ - timeout : 30000, +// special markup used throughout Ext when box wrapping elements +Ext.Element.boxMarkup = '
      '; + +Ext.Element.addMethods(function(){ + var INTERNAL = "_internal", + pxMatch = /(\d+\.?\d+)px/; + return { /** - * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false) - * @type Boolean + * More flexible version of {@link #setStyle} for setting style properties. + * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or + * a function which returns such a specification. + * @return {Ext.Element} this */ - autoAbort:false, + applyStyles : function(style){ + Ext.DomHelper.applyStyles(this.dom, style); + return this; + }, /** - * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true) - * @type Boolean + * Returns an object with properties matching the styles requested. + * For example, el.getStyles('color', 'font-size', 'width') might return + * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}. + * @param {String} style1 A style name + * @param {String} style2 A style name + * @param {String} etc. + * @return {Object} The style object */ - disableCaching: true, + getStyles : function(){ + var ret = {}; + Ext.each(arguments, function(v) { + ret[v] = this.getStyle(v); + }, + this); + return ret; + }, + + // private ==> used by ext full + setOverflow : function(v){ + var dom = this.dom; + if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug + dom.style.overflow = 'hidden'; + (function(){dom.style.overflow = 'auto';}).defer(1); + }else{ + dom.style.overflow = v; + } + }, + + /** + *

      Wraps the specified element with a special 9 element markup/CSS block that renders by default as + * a gray container with a gradient background, rounded corners and a 4-way shadow.

      + *

      This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button}, + * {@link Ext.Panel} when {@link Ext.Panel#frame frame=true}, {@link Ext.Window}). The markup + * is of this form:

      + *
      
      +    Ext.Element.boxMarkup =
      +    '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
      +     <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
      +     <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
      +        * 
      + *

      Example usage:

      + *
      
      +    // Basic box wrap
      +    Ext.get("foo").boxWrap();
      +
      +    // You can also add a custom class and use CSS inheritance rules to customize the box look.
      +    // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
      +    // for how to create a custom box wrap style.
      +    Ext.get("foo").boxWrap().addClass("x-box-blue");
      +        * 
      + * @param {String} class (optional) A base CSS class to apply to the containing wrapper element + * (defaults to 'x-box'). Note that there are a number of CSS rules that are dependent on + * this name to make the overall effect work, so if you supply an alternate base class, make sure you + * also supply all of the necessary rules. + * @return {Ext.Element} The outermost wrapping element of the created box structure. + */ + boxWrap : function(cls){ + cls = cls || 'x-box'; + var el = Ext.get(this.insertHtml("beforeBegin", "
      " + String.format(Ext.Element.boxMarkup, cls) + "
      ")); //String.format('
      '+Ext.Element.boxMarkup+'
      ', cls))); + Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom); + return el; + }, /** - * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching - * through a cache buster. Defaults to '_dc' - * @type String + * Set the size of this Element. If animation is true, both width and height will be animated concurrently. + * @param {Mixed} width The new width. This may be one of:
        + *
      • A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).
      • + *
      • A String used to set the CSS width style. Animation may not be used. + *
      • A size object in the format {width: widthValue, height: heightValue}.
      • + *
      + * @param {Mixed} height The new height. This may be one of:
        + *
      • A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).
      • + *
      • A String used to set the CSS height style. Animation may not be used.
      • + *
      + * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object + * @return {Ext.Element} this */ - disableCachingParam: '_dc', + setSize : function(width, height, animate){ + var me = this; + if(typeof width == 'object'){ // in case of object from getSize() + height = width.height; + width = width.width; + } + width = me.adjustWidth(width); + height = me.adjustHeight(height); + if(!animate || !me.anim){ + me.dom.style.width = me.addUnits(width); + me.dom.style.height = me.addUnits(height); + }else{ + me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2)); + } + return me; + }, /** - *

      Sends an HTTP request to a remote server.

      - *

      Important: Ajax server requests are asynchronous, and this call will - * return before the response has been received. Process any returned data - * in a callback function.

      - *
      
      -Ext.Ajax.request({
      -   url: 'ajax_demo/sample.json',
      -   success: function(response, opts) {
      -      var obj = Ext.decode(response.responseText);
      -      console.dir(obj);
      -   },
      -   failure: function(response, opts) {
      -      console.log('server-side failure with status code ' + response.status);
      -   }
      -});
      -         * 
      - *

      To execute a callback function in the correct scope, use the scope option.

      - * @param {Object} options An object which may contain the following properties:
        - *
      • url : String/Function (Optional)
        The URL to - * which to send the request, or a function to call which returns a URL string. The scope of the - * function is specified by the scope option. Defaults to the configured - * {@link #url}.
      • - *
      • params : Object/String/Function (Optional)
        - * An object containing properties which are used as parameters to the - * request, a url encoded string or a function to call to get either. The scope of the function - * is specified by the scope option.
      • - *
      • method : String (Optional)
        The HTTP method to use - * for the request. Defaults to the configured method, or if no method was configured, - * "GET" if no parameters are being sent, and "POST" if parameters are being sent. Note that - * the method name is case-sensitive and should be all caps.
      • - *
      • callback : Function (Optional)
        The - * function to be called upon receipt of the HTTP response. The callback is - * called regardless of success or failure and is passed the following - * parameters:
          - *
        • options : Object
          The parameter to the request call.
        • - *
        • success : Boolean
          True if the request succeeded.
        • - *
        • response : Object
          The XMLHttpRequest object containing the response data. - * See http://www.w3.org/TR/XMLHttpRequest/ for details about - * accessing elements of the response.
        • - *
      • - *
      • success : Function (Optional)
        The function - * to be called upon success of the request. The callback is passed the following - * parameters:
          - *
        • response : Object
          The XMLHttpRequest object containing the response data.
        • - *
        • options : Object
          The parameter to the request call.
        • - *
      • - *
      • failure : Function (Optional)
        The function - * to be called upon failure of the request. The callback is passed the - * following parameters:
          - *
        • response : Object
          The XMLHttpRequest object containing the response data.
        • - *
        • options : Object
          The parameter to the request call.
        • - *
      • - *
      • scope : Object (Optional)
        The scope in - * which to execute the callbacks: The "this" object for the callback function. If the url, or params options were - * specified as functions from which to draw values, then this also serves as the scope for those function calls. - * Defaults to the browser window.
      • - *
      • timeout : Number (Optional)
        The timeout in milliseconds to be used for this request. Defaults to 30 seconds.
      • - *
      • form : Element/HTMLElement/String (Optional)
        The <form> - * Element or the id of the <form> to pull parameters from.
      • - *
      • isUpload : Boolean (Optional)
        Only meaningful when used - * with the form option. - *

        True if the form object is a file upload (will be set automatically if the form was - * configured with enctype "multipart/form-data").

        - *

        File uploads are not performed using normal "Ajax" techniques, that is they are not - * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the - * DOM <form> element temporarily modified to have its - * target set to refer - * to a dynamically generated, hidden <iframe> which is inserted into the document - * but removed after the return data has been gathered.

        - *

        The server response is parsed by the browser to create the document for the IFRAME. If the - * server is using JSON to send the return object, then the - * Content-Type header - * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.

        - *

        The response text is retrieved from the document, and a fake XMLHttpRequest object - * is created containing a responseText property in order to conform to the - * requirements of event handlers and callbacks.

        - *

        Be aware that file upload packets are sent with the content type multipart/form - * and some server technologies (notably JEE) may require some custom processing in order to - * retrieve parameter names and parameter values from the packet content.

        - *
      • - *
      • headers : Object (Optional)
        Request - * headers to set for the request.
      • - *
      • xmlData : Object (Optional)
        XML document - * to use for the post. Note: This will be used instead of params for the post - * data. Any params will be appended to the URL.
      • - *
      • jsonData : Object/String (Optional)
        JSON - * data to use as the post. Note: This will be used instead of params for the post - * data. Any params will be appended to the URL.
      • - *
      • disableCaching : Boolean (Optional)
        True - * to add a unique cache-buster param to GET requests.
      • - *

      - *

      The options object may also contain any other property which might be needed to perform - * postprocessing in a callback because it is passed to callback functions.

      - * @return {Number} transactionId The id of the server transaction. This may be used - * to cancel the request. + * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders + * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements + * if a height has not been set using CSS. + * @return {Number} */ - request : function(o){ - var me = this; - if(me.fireEvent(BEFOREREQUEST, me, o)){ - if (o.el) { - if(!Ext.isEmpty(o.indicatorText)){ - me.indicatorText = '
      '+o.indicatorText+"
      "; - } - if(me.indicatorText) { - Ext.getDom(o.el).innerHTML = me.indicatorText; - } - o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) { - Ext.getDom(o.el).innerHTML = response.responseText; - }); + getComputedHeight : function(){ + var me = this, + h = Math.max(me.dom.offsetHeight, me.dom.clientHeight); + if(!h){ + h = parseFloat(me.getStyle('height')) || 0; + if(!me.isBorderBox()){ + h += me.getFrameWidth('tb'); } + } + return h; + }, - var p = o.params, - url = o.url || me.url, - method, - cb = {success: me.handleResponse, - failure: me.handleFailure, - scope: me, - argument: {options: o}, - timeout : o.timeout || me.timeout - }, - form, - serForm; + /** + * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders + * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements + * if a width has not been set using CSS. + * @return {Number} + */ + getComputedWidth : function(){ + var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth); + if(!w){ + w = parseFloat(this.getStyle('width')) || 0; + if(!this.isBorderBox()){ + w += this.getFrameWidth('lr'); + } + } + return w; + }, + /** + * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth() + for more information about the sides. + * @param {String} sides + * @return {Number} + */ + getFrameWidth : function(sides, onlyContentBox){ + return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides)); + }, - if (Ext.isFunction(p)) { - p = p.call(o.scope||WINDOW, o); + /** + * Sets up event handlers to add and remove a css class when the mouse is over this element + * @param {String} className + * @return {Ext.Element} this + */ + addClassOnOver : function(className){ + this.hover( + function(){ + Ext.fly(this, INTERNAL).addClass(className); + }, + function(){ + Ext.fly(this, INTERNAL).removeClass(className); } + ); + return this; + }, - p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p); + /** + * Sets up event handlers to add and remove a css class when this element has the focus + * @param {String} className + * @return {Ext.Element} this + */ + addClassOnFocus : function(className){ + this.on("focus", function(){ + Ext.fly(this, INTERNAL).addClass(className); + }, this.dom); + this.on("blur", function(){ + Ext.fly(this, INTERNAL).removeClass(className); + }, this.dom); + return this; + }, - if (Ext.isFunction(url)) { - url = url.call(o.scope || WINDOW, o); - } + /** + * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect) + * @param {String} className + * @return {Ext.Element} this + */ + addClassOnClick : function(className){ + var dom = this.dom; + this.on("mousedown", function(){ + Ext.fly(dom, INTERNAL).addClass(className); + var d = Ext.getDoc(), + fn = function(){ + Ext.fly(dom, INTERNAL).removeClass(className); + d.removeListener("mouseup", fn); + }; + d.on("mouseup", fn); + }); + return this; + }, - if((form = Ext.getDom(o.form))){ - url = url || form.action; - if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) { - return me.doFormUpload.call(me, o, p, url); - } - serForm = Ext.lib.Ajax.serializeForm(form); - p = p ? (p + '&' + serForm) : serForm; - } + /** + *

      Returns the dimensions of the element available to lay content out in.

      + *

      If the element (or any ancestor element) has CSS style display : none, the dimensions will be zero.

      + * example:
      
      +        var vpSize = Ext.getBody().getViewSize();
       
      -                method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
      +        // all Windows created afterwards will have a default value of 90% height and 95% width
      +        Ext.Window.override({
      +            width: vpSize.width * 0.9,
      +            height: vpSize.height * 0.95
      +        });
      +        // To handle window resizing you would have to hook onto onWindowResize.
      +        * 
      + * + * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars. + * To obtain the size including scrollbars, use getStyleSize + * + * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc. + */ - if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){ - var dcp = o.disableCachingParam || me.disableCachingParam; - url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime())); - } + getViewSize : function(){ + var doc = document, + d = this.dom, + isDoc = (d == doc || d == doc.body); - o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {}); + // If the body, use Ext.lib.Dom + if (isDoc) { + var extdom = Ext.lib.Dom; + return { + width : extdom.getViewWidth(), + height : extdom.getViewHeight() + }; - if(o.autoAbort === true || me.autoAbort) { - me.abort(); - } + // Else use clientHeight/clientWidth + } else { + return { + width : d.clientWidth, + height : d.clientHeight + }; + } + }, - if((method == GET || o.xmlData || o.jsonData) && p){ - url = Ext.urlAppend(url, p); - p = ''; + /** + *

      Returns the dimensions of the element available to lay content out in.

      + * + * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth. + * To obtain the size excluding scrollbars, use getViewSize + * + * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc. + */ + + getStyleSize : function(){ + var me = this, + w, h, + doc = document, + d = this.dom, + isDoc = (d == doc || d == doc.body), + s = d.style; + + // If the body, use Ext.lib.Dom + if (isDoc) { + var extdom = Ext.lib.Dom; + return { + width : extdom.getViewWidth(), + height : extdom.getViewHeight() + }; + } + // Use Styles if they are set + if(s.width && s.width != 'auto'){ + w = parseFloat(s.width); + if(me.isBorderBox()){ + w -= me.getFrameWidth('lr'); } - return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o)); - }else{ - return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null; } + // Use Styles if they are set + if(s.height && s.height != 'auto'){ + h = parseFloat(s.height); + if(me.isBorderBox()){ + h -= me.getFrameWidth('tb'); + } + } + // Use getWidth/getHeight if style not set. + return {width: w || me.getWidth(true), height: h || me.getHeight(true)}; }, /** - * Determine whether this object has a request outstanding. - * @param {Number} transactionId (Optional) defaults to the last transaction - * @return {Boolean} True if there is an outstanding request. + * Returns the size of the element. + * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding + * @return {Object} An object containing the element's size {width: (element width), height: (element height)} */ - isLoading : function(transId){ - return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId; + getSize : function(contentSize){ + return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)}; }, /** - * Aborts any outstanding request. - * @param {Number} transactionId (Optional) defaults to the last transaction + * Forces the browser to repaint this element + * @return {Ext.Element} this */ - abort : function(transId){ - if(transId || this.isLoading()){ - Ext.lib.Ajax.abort(transId || this.transId); - } + repaint : function(){ + var dom = this.dom; + this.addClass("x-repaint"); + setTimeout(function(){ + Ext.fly(dom).removeClass("x-repaint"); + }, 1); + return this; }, - // private - handleResponse : function(response){ - this.transId = false; - var options = response.argument.options; - response.argument = options ? options.argument : null; - this.fireEvent(REQUESTCOMPLETE, this, response, options); - if(options.success){ - options.success.call(options.scope, response, options); - } - if(options.callback){ - options.callback.call(options.scope, options, true, response); - } + /** + * Disables text selection for this element (normalized across browsers) + * @return {Ext.Element} this + */ + unselectable : function(){ + this.dom.unselectable = "on"; + return this.swallowEvent("selectstart", true). + applyStyles("-moz-user-select:none;-khtml-user-select:none;"). + addClass("x-unselectable"); }, - // private - handleFailure : function(response, e){ - this.transId = false; - var options = response.argument.options; - response.argument = options ? options.argument : null; - this.fireEvent(REQUESTEXCEPTION, this, response, options, e); - if(options.failure){ - options.failure.call(options.scope, response, options); - } - if(options.callback){ - options.callback.call(options.scope, options, false, response); + /** + * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed, + * then it returns the calculated width of the sides (see getPadding) + * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides + * @return {Object/Number} + */ + getMargins : function(side){ + var me = this, + key, + hash = {t:"top", l:"left", r:"right", b: "bottom"}, + o = {}; + + if (!side) { + for (key in me.margins){ + o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0; + } + return o; + } else { + return me.addStyles.call(me, side, me.margins); } - }, + } + }; +}()); +/** + * @class Ext.Element + */ +Ext.Element.addMethods({ + /** + * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently. + * @param {Object} box The box to fill {x, y, width, height} + * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically + * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object + * @return {Ext.Element} this + */ + setBox : function(box, adjust, animate){ + var me = this, + w = box.width, + h = box.height; + if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){ + w -= (me.getBorderWidth("lr") + me.getPadding("lr")); + h -= (me.getBorderWidth("tb") + me.getPadding("tb")); + } + me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2)); + return me; + }, - // private - doFormUpload : function(o, ps, url){ - var id = Ext.id(), - doc = document, - frame = doc.createElement('iframe'), - form = Ext.getDom(o.form), - hiddens = [], - hd, - encoding = 'multipart/form-data', - buf = { - target: form.target, - method: form.method, - encoding: form.encoding, - enctype: form.enctype, - action: form.action - }; + /** + * Return an object defining the area of this Element which can be passed to {@link #setBox} to + * set another Element's size/location to match this element. + * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned. + * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y. + * @return {Object} box An object in the format

      
      +{
      +    x: <Element's X position>,
      +    y: <Element's Y position>,
      +    width: <Element's width>,
      +    height: <Element's height>,
      +    bottom: <Element's lower bound>,
      +    right: <Element's rightmost bound>
      +}
      +
      + * The returned object may also be addressed as an Array where index 0 contains the X position + * and index 1 contains the Y position. So the result may also be used for {@link #setXY} + */ + getBox : function(contentBox, local) { + var me = this, + xy, + left, + top, + getBorderWidth = me.getBorderWidth, + getPadding = me.getPadding, + l, + r, + t, + b; + if(!local){ + xy = me.getXY(); + }else{ + left = parseInt(me.getStyle("left"), 10) || 0; + top = parseInt(me.getStyle("top"), 10) || 0; + xy = [left, top]; + } + var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx; + if(!contentBox){ + bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h}; + }else{ + l = getBorderWidth.call(me, "l") + getPadding.call(me, "l"); + r = getBorderWidth.call(me, "r") + getPadding.call(me, "r"); + t = getBorderWidth.call(me, "t") + getPadding.call(me, "t"); + b = getBorderWidth.call(me, "b") + getPadding.call(me, "b"); + bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)}; + } + bx.right = bx.x + bx.width; + bx.bottom = bx.y + bx.height; + return bx; + }, + + /** + * Move this element relative to its current position. + * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down"). + * @param {Number} distance How far to move the element in pixels + * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object + * @return {Ext.Element} this + */ + move : function(direction, distance, animate){ + var me = this, + xy = me.getXY(), + x = xy[0], + y = xy[1], + left = [x - distance, y], + right = [x + distance, y], + top = [x, y - distance], + bottom = [x, y + distance], + hash = { + l : left, + left : left, + r : right, + right : right, + t : top, + top : top, + up : top, + b : bottom, + bottom : bottom, + down : bottom + }; + + direction = direction.toLowerCase(); + me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2)); + }, + + /** + * Quick set left and top adding default units + * @param {String} left The left CSS property value + * @param {String} top The top CSS property value + * @return {Ext.Element} this + */ + setLeftTop : function(left, top){ + var me = this, + style = me.dom.style; + style.left = me.addUnits(left); + style.top = me.addUnits(top); + return me; + }, + + /** + * Returns the region of the given element. + * The element must be part of the DOM tree to have a region (display:none or elements not appended return false). + * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data. + */ + getRegion : function(){ + return Ext.lib.Dom.getRegion(this.dom); + }, + + /** + * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently. + * @param {Number} x X value for new position (coordinates are page-based) + * @param {Number} y Y value for new position (coordinates are page-based) + * @param {Mixed} width The new width. This may be one of:
        + *
      • A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)
      • + *
      • A String used to set the CSS width style. Animation may not be used. + *
      + * @param {Mixed} height The new height. This may be one of:
        + *
      • A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)
      • + *
      • A String used to set the CSS height style. Animation may not be used.
      • + *
      + * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object + * @return {Ext.Element} this + */ + setBounds : function(x, y, width, height, animate){ + var me = this; + if (!animate || !me.anim) { + me.setSize(width, height); + me.setLocation(x, y); + } else { + me.anim({points: {to: [x, y]}, + width: {to: me.adjustWidth(width)}, + height: {to: me.adjustHeight(height)}}, + me.preanim(arguments, 4), + 'motion'); + } + return me; + }, - /* - * Originally this behaviour was modified for Opera 10 to apply the secure URL after - * the frame had been added to the document. It seems this has since been corrected in - * Opera so the behaviour has been reverted, the URL will be set before being added. - */ - Ext.fly(frame).set({ - id: id, - name: id, - cls: 'x-hidden', - src: Ext.SSL_SECURE_URL - }); + /** + * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently. + * @param {Ext.lib.Region} region The region to fill + * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object + * @return {Ext.Element} this + */ + setRegion : function(region, animate) { + return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1)); + } +});/** + * @class Ext.Element + */ +Ext.Element.addMethods({ + /** + * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll(). + * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values. + * @param {Number} value The new scroll value + * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object + * @return {Element} this + */ + scrollTo : function(side, value, animate) { + //check if we're scrolling top or left + var top = /top/i.test(side), + me = this, + dom = me.dom, + prop; + if (!animate || !me.anim) { + // just setting the value, so grab the direction + prop = 'scroll' + (top ? 'Top' : 'Left'); + dom[prop] = value; + } + else { + // if scrolling top, we need to grab scrollLeft, if left, scrollTop + prop = 'scroll' + (top ? 'Left' : 'Top'); + me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}}, me.preanim(arguments, 2), 'scroll'); + } + return me; + }, + + /** + * Scrolls this element into view within the passed container. + * @param {Mixed} container (optional) The container element to scroll (defaults to 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.Element} this + */ + scrollIntoView : function(container, hscroll) { + var c = Ext.getDom(container) || Ext.getBody().dom, + el = this.dom, + o = this.getOffsetsTo(c), + l = o[0] + c.scrollLeft, + t = o[1] + c.scrollTop, + b = t + el.offsetHeight, + r = l + el.offsetWidth, + ch = c.clientHeight, + ct = parseInt(c.scrollTop, 10), + cl = parseInt(c.scrollLeft, 10), + cb = ct + ch, + cr = cl + c.clientWidth; - doc.body.appendChild(frame); + if (el.offsetHeight > ch || t < ct) { + c.scrollTop = t; + } + else if (b > cb) { + c.scrollTop = b-ch; + } + // corrects IE, other browsers will ignore + c.scrollTop = c.scrollTop; - // This is required so that IE doesn't pop the response up in a new window. - if(Ext.isIE){ - document.frames[id].name = id; + 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; + }, + // private + scrollChildIntoView : function(child, hscroll) { + Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll); + }, + + /** + * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is + * within this element's scrollable range. + * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down"). + * @param {Number} distance How far to scroll the element in pixels + * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object + * @return {Boolean} Returns true if a scroll was triggered or false if the element + * was scrolled as far as it could go. + */ + scroll : function(direction, distance, animate) { + if (!this.isScrollable()) { + return false; + } + var el = this.dom, + l = el.scrollLeft, t = el.scrollTop, + w = el.scrollWidth, h = el.scrollHeight, + cw = el.clientWidth, ch = el.clientHeight, + scrolled = false, v, + hash = { + l: Math.min(l + distance, w-cw), + r: v = Math.max(l - distance, 0), + t: Math.max(t - distance, 0), + b: Math.min(t + distance, h-ch) + }; + hash.d = hash.b; + hash.u = hash.t; + + direction = direction.substr(0, 1); + if ((v = hash[direction]) > -1) { + scrolled = true; + this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2)); + } + return scrolled; + } +});/** + * @class Ext.Element + */ +Ext.Element.addMethods( + function() { + var VISIBILITY = "visibility", + DISPLAY = "display", + HIDDEN = "hidden", + NONE = "none", + XMASKED = "x-masked", + XMASKEDRELATIVE = "x-masked-relative", + data = Ext.Element.data; - Ext.fly(form).set({ - target: id, - method: POST, - enctype: encoding, - encoding: encoding, - action: url || buf.action - }); - - // add dynamic params - Ext.iterate(Ext.urlDecode(ps, false), function(k, v){ - hd = doc.createElement('input'); - Ext.fly(hd).set({ - type: 'hidden', - value: v, - name: k - }); - form.appendChild(hd); - hiddens.push(hd); - }); - - function cb(){ - var me = this, - // bogus response object - r = {responseText : '', - responseXML : null, - argument : o.argument}, - doc, - firstChild; - - try{ - doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document; - if(doc){ - if(doc.body){ - if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea - r.responseText = firstChild.value; - }else{ - r.responseText = doc.body.innerHTML; - } - } - //in IE the document may still have a body even if returns XML. - r.responseXML = doc.XMLDocument || doc; + return { + /** + * Checks whether the element is currently visible using both visibility and display properties. + * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false) + * @return {Boolean} True if the element is currently visible, else false + */ + isVisible : function(deep) { + var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE), + p = this.dom.parentNode; + + if (deep !== true || !vis) { + return vis; + } + + while (p && !(/^body/i.test(p.tagName))) { + if (!Ext.fly(p, '_isVisible').isVisible()) { + return false; } + p = p.parentNode; } - catch(e) {} + return true; + }, - Ext.EventManager.removeListener(frame, LOAD, cb, me); + /** + * Returns true if display is not "none" + * @return {Boolean} + */ + isDisplayed : function() { + return !this.isStyle(DISPLAY, NONE); + }, - me.fireEvent(REQUESTCOMPLETE, me, r, o); + /** + * 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(Ext.Element.DISPLAY); + + if (!Ext.isEmpty(display)) { + data(this.dom, 'originalDisplay', display); + } + + return this; + }, - function runCallback(fn, scope, args){ - if(Ext.isFunction(fn)){ - fn.apply(scope, args); - } + /** + * 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 (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) { + me.addClass(XMASKEDRELATIVE); + } + if (el = data(dom, 'maskMsg')) { + el.remove(); + } + if (el = data(dom, 'mask')) { + el.remove(); } - runCallback(o.success, o.scope, [r, o]); - runCallback(o.callback, o.scope, [o, true, r]); + mask = dh.append(dom, {cls : "ext-el-mask"}, true); + data(dom, 'mask', mask); - if(!me.debugUploads){ - setTimeout(function(){Ext.removeNode(frame);}, 100); + 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); } - } - - Ext.EventManager.on(frame, LOAD, cb, this); - form.submit(); + + // ie will not expand full height automatically + if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') { + mask.setSize(undefined, me.getHeight()); + } + + return mask; + }, - Ext.fly(form).set(buf); - Ext.each(hiddens, function(h) { - Ext.removeNode(h); - }); - } - }); -})(); + /** + * 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]); + } + }, -/** - * @class Ext.Ajax - * @extends Ext.data.Connection - *

      The global Ajax request class that provides a simple way to make Ajax requests - * with maximum flexibility.

      - *

      Since Ext.Ajax is a singleton, you can set common properties/events for it once - * and override them at the request function level only if necessary.

      - *

      Common Properties you may want to set are:

        - *
      • {@link #method}

      • - *
      • {@link #extraParams}

      • - *
      • {@link #url}

      • - *
      - *
      
      -// Default headers to pass in every request
      -Ext.Ajax.defaultHeaders = {
      -    'Powered-By': 'Ext'
      -};
      - * 
      - *

      - *

      Common Events you may want to set are:

        - *
      • {@link Ext.data.Connection#beforerequest beforerequest}

      • - *
      • {@link Ext.data.Connection#requestcomplete requestcomplete}

      • - *
      • {@link Ext.data.Connection#requestexception requestexception}

      • - *
      - *
      
      -// Example: show a spinner during all Ajax requests
      -Ext.Ajax.on('beforerequest', this.showSpinner, this);
      -Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
      -Ext.Ajax.on('requestexception', this.hideSpinner, this);
      - * 
      - *

      - *

      An example request:

      - *
      
      -// Basic request
      -Ext.Ajax.{@link Ext.data.Connection#request request}({
      -   url: 'foo.php',
      -   success: someFn,
      -   failure: otherFn,
      -   headers: {
      -       'my-header': 'foo'
      -   },
      -   params: { foo: 'bar' }
      -});
      +            /**
      +             * Returns true if this element is masked
      +             * @return {Boolean}
      +             */
      +            isMasked : function() {
      +                var m = data(this.dom, 'mask');
      +                return m && m.isVisible();
      +            },
       
      -// Simple ajax form submission
      -Ext.Ajax.{@link Ext.data.Connection#request request}({
      -    form: 'some-form',
      -    params: 'foo=bar'
      -});
      - * 
      - *

      - * @singleton + /** + * Creates an iframe shim for this element to keep selects and other windowed objects from + * showing through. + * @return {Ext.Element} The new shim element + */ + createShim : function() { + var el = document.createElement('iframe'), + shim; + + el.frameBorder = '0'; + el.className = 'ext-shim'; + el.src = Ext.SSL_SECURE_URL; + shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom)); + shim.autoBoxAdjust = false; + return shim; + } + }; + }() +);/** + * @class Ext.Element */ -Ext.Ajax = new Ext.data.Connection({ - /** - * @cfg {String} url @hide - */ - /** - * @cfg {Object} extraParams @hide - */ - /** - * @cfg {Object} defaultHeaders @hide - */ - /** - * @cfg {String} method (Optional) @hide - */ - /** - * @cfg {Number} timeout (Optional) @hide - */ +Ext.Element.addMethods({ /** - * @cfg {Boolean} autoAbort (Optional) @hide + * Convenience method for constructing a KeyMap + * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options: + * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)} + * @param {Function} fn The function to call + * @param {Object} scope (optional) The scope (this reference) in which the specified function is executed. Defaults to this Element. + * @return {Ext.KeyMap} The KeyMap created */ + addKeyListener : function(key, fn, scope){ + var config; + if(typeof key != 'object' || Ext.isArray(key)){ + config = { + key: key, + fn: fn, + scope: scope + }; + }else{ + config = { + key : key.key, + shift : key.shift, + ctrl : key.ctrl, + alt : key.alt, + fn: fn, + scope: scope + }; + } + return new Ext.KeyMap(this, config); + }, /** - * @cfg {Boolean} disableCaching (Optional) @hide + * Creates a KeyMap for this element + * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details + * @return {Ext.KeyMap} The KeyMap created */ + addKeyMap : function(config){ + return new Ext.KeyMap(this, config); + } +}); + +//Import the newly-added Ext.Element functions into CompositeElementLite. We call this here because +//Element.keys.js is the last extra Ext.Element include in the ext-all.js build +Ext.CompositeElementLite.importElementMethods();/** + * @class Ext.CompositeElementLite + */ +Ext.apply(Ext.CompositeElementLite.prototype, { + addElements : function(els, root){ + if(!els){ + return this; + } + if(typeof els == "string"){ + els = Ext.Element.selectorFunction(els, root); + } + var yels = this.elements; + Ext.each(els, function(e) { + yels.push(Ext.get(e)); + }); + return this; + }, /** - * @property disableCaching - * True to add a unique cache-buster param to GET requests. (defaults to true) - * @type Boolean - */ - /** - * @property url - * The default URL to be used for requests to the server. (defaults to undefined) - * If the server receives all requests through one URL, setting this once is easier than - * entering it on every request. - * @type String + * Returns the first Element + * @return {Ext.Element} */ + first : function(){ + return this.item(0); + }, + /** - * @property extraParams - * An object containing properties which are used as extra parameters to each request made - * by this object (defaults to undefined). Session information and other data that you need - * to pass with each request are commonly put here. - * @type Object + * Returns the last Element + * @return {Ext.Element} */ + last : function(){ + return this.item(this.getCount()-1); + }, + /** - * @property defaultHeaders - * An object containing request headers which are added to each request made by this object - * (defaults to undefined). - * @type Object + * Returns true if this composite contains the passed element + * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection. + * @return Boolean */ + contains : function(el){ + return this.indexOf(el) != -1; + }, + /** - * @property method - * The default HTTP method to be used for requests. Note that this is case-sensitive and - * should be all caps (defaults to undefined; if not set but params are present will use - * "POST", otherwise will use "GET".) - * @type String - */ + * Removes the specified element(s). + * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite + * or an array of any of those. + * @param {Boolean} removeDom (optional) True to also remove the element from the document + * @return {CompositeElement} this + */ + removeElement : function(keys, removeDom){ + var me = this, + els = this.elements, + el; + Ext.each(keys, function(val){ + if ((el = (els[val] || els[val = me.indexOf(val)]))) { + if(removeDom){ + if(el.dom){ + el.remove(); + }else{ + Ext.removeNode(el); + } + } + els.splice(val, 1); + } + }); + return this; + } +}); +/** + * @class Ext.CompositeElement + * @extends Ext.CompositeElementLite + *

      This class encapsulates a collection of DOM elements, providing methods to filter + * members, or to perform collective actions upon the whole set.

      + *

      Although they are not listed, this class supports all of the methods of {@link Ext.Element} and + * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.

      + *

      All methods return this and can be chained.

      + * Usage: +
      
      +var els = Ext.select("#some-el div.some-class", true);
      +// or select directly from an existing element
      +var el = Ext.get('some-el');
      +el.select('div.some-class', true);
      +
      +els.setWidth(100); // all elements become 100 width
      +els.hide(true); // all elements fade out and hide
      +// or
      +els.setWidth(100).hide(true);
      +
      + */ +Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, { + + constructor : function(els, root){ + this.elements = []; + this.add(els, root); + }, + + // private + getElement : function(el){ + // In this case just return it, since we already have a reference to it + return el; + }, + + // private + transformElement : function(el){ + return Ext.get(el); + } + /** - * @property timeout - * The timeout in milliseconds to be used for requests. (defaults to 30000) - * @type Number - */ + * Adds elements to this composite. + * @param {String/Array} els A string CSS selector, an array of elements or an element + * @return {CompositeElement} this + */ /** - * @property autoAbort - * Whether a new request should abort any pending requests. (defaults to false) - * @type Boolean + * Returns the Element object at the specified index + * @param {Number} index + * @return {Ext.Element} */ - autoAbort : false, /** - * Serialize the passed form into a url encoded string - * @param {String/HTMLElement} form - * @return {String} + * Iterates each element in this composite + * calling the supplied function using {@link Ext#each}. + * @param {Function} fn The function to be called with each + * element. If the supplied function returns false, + * iteration stops. This function is called with the following arguments: + *
        + *
      • element : Ext.Element
        The element at the current index + * in the composite
      • + *
      • composite : Object
        This composite.
      • + *
      • index : Number
        The current index within the composite
      • + *
      + * @param {Object} scope (optional) The scope ( reference) in which the specified function is executed. + * Defaults to the element at the current index + * within the composite. + * @return {CompositeElement} this */ - serializeForm : function(form){ - return Ext.lib.Ajax.serializeForm(form); - } }); + +/** + * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods + * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or + * {@link Ext.CompositeElementLite CompositeElementLite} object. + * @param {String/Array} selector The CSS selector or an array of elements + * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object) + * @param {HTMLElement/String} root (optional) The root element of the query or id of the root + * @return {CompositeElementLite/CompositeElement} + * @member Ext.Element + * @method select + */ +Ext.Element.select = function(selector, unique, root){ + var els; + if(typeof selector == "string"){ + els = Ext.Element.selectorFunction(selector, root); + }else if(selector.length !== undefined){ + els = selector; + }else{ + throw "Invalid selector"; + } + + return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els); +}; + /** + * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods + * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or + * {@link Ext.CompositeElementLite CompositeElementLite} object. + * @param {String/Array} selector The CSS selector or an array of elements + * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object) + * @param {HTMLElement/String} root (optional) The root element of the query or id of the root + * @return {CompositeElementLite/CompositeElement} + * @member Ext + * @method select + */ +Ext.select = Ext.Element.select;/** * @class Ext.Updater * @extends Ext.util.Observable * Provides AJAX-style update capabilities for Element objects. Updater can be used to {@link #update} @@ -10431,7 +11788,7 @@ function() { this.update(this.defaultUrl, null, callback, true); } } - } + }; }()); /** @@ -10664,7 +12021,7 @@ Date.formatCodeToRegex = function(character, currentGroup) { g:0, c:null, s:Ext.escapeRe(character) // treat unrecognised characters as literals - } + }; }; // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often @@ -10958,7 +12315,7 @@ Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')"; t: "this.getDaysInMonth()", L: "(this.isLeapYear() ? 1 : 0)", o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))", - Y: "this.getFullYear()", + Y: "String.leftPad(this.getFullYear(), 4, '0')", y: "('' + this.getFullYear()).substring(2, 4)", a: "(this.getHours() < 12 ? 'am' : 'pm')", A: "(this.getHours() < 12 ? 'AM' : 'PM')", @@ -11018,7 +12375,8 @@ Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')"; s = s || 0; ms = ms || 0; - var dt = new Date(y, m - 1, d, h, i, s, ms); + // Special handling for year < 100 + var dt = new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0); return y == dt.getFullYear() && m == dt.getMonth() + 1 && @@ -11095,7 +12453,7 @@ dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null special = false; code.push("'" + String.escape(ch) + "'"); } else { - code.push(Date.getFormatCode(ch)) + code.push(Date.getFormatCode(ch)); } } Date.formatFunctions[format] = new Function("return " + code.join('+')); @@ -11135,7 +12493,8 @@ dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null // these 2 values alone provide sufficient info to create a full date object // create Date object representing January 1st for the given year - "v = new Date(y, 0, 1, h, i, s, ms);", + // handle years < 100 appropriately + "v = new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);", // then add day of year, checking for Date "rollover" if necessary "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);", @@ -11143,7 +12502,8 @@ dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null "v = null;", // invalid date, so return null "}else{", // plain old Date object - "v = new Date(y, m, d, h, i, s, ms);", + // handle years < 100 properly + "v = new Date(y < 100 ? 100 : y, m, d, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);", "}", "}", "}", @@ -11187,9 +12547,9 @@ dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null } } - Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$"); + Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i'); Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join(''))); - } + }; }(), // private @@ -11216,14 +12576,14 @@ dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null g:0, c:null, s:"(?:" + a.join("|") +")" - } + }; }, l: function() { return { g:0, c:null, s:"(?:" + Date.dayNames.join("|") + ")" - } + }; }, N: { g:0, @@ -11255,7 +12615,7 @@ dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null g:1, c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number s:"(" + Date.monthNames.join("|") + ")" - } + }; }, M: function() { for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names @@ -11297,19 +12657,24 @@ dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year s:"(\\d{1,2})" }, + /** + * In the am/pm parsing routines, we allow both upper and lower case + * even though it doesn't exactly match the spec. It gives much more flexibility + * in being able to specify case insensitive regexes. + */ a: { g:1, - c:"if (results[{0}] == 'am') {\n" + c:"if (/(am)/i.test(results[{0}])) {\n" + "if (!h || h == 12) { h = 0; }\n" + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}", - s:"(am|pm)" + s:"(am|pm|AM|PM)" }, A: { g:1, - c:"if (results[{0}] == 'AM') {\n" + c:"if (/(am)/i.test(results[{0}])) {\n" + "if (!h || h == 12) { h = 0; }\n" + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}", - s:"(AM|PM)" + s:"(AM|PM|am|pm)" }, g: function() { return $f("G"); @@ -11419,7 +12784,7 @@ dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null ")?", ")?" ].join("") - } + }; }, U: { g:1, @@ -11511,7 +12876,7 @@ Ext.apply(Date.prototype, { Wyr = new Date(AWN * ms7d).getUTCFullYear(); return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1; - } + }; }(), /** @@ -11582,7 +12947,7 @@ document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday' var m = this.getMonth(); return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m]; - } + }; }(), /** @@ -11820,7 +13185,7 @@ console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime console.log('Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)= %o', Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)); // strict date parsing with invalid month value console.groupEnd(); -//*/ +*/ /** * @class Ext.util.MixedCollection * @extends Ext.util.Observable @@ -12486,199 +13851,120 @@ mc.add(otherEl); */ Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item; /** - * @class Ext.util.JSON - * Modified version of Douglas Crockford"s json.js that doesn"t - * mess with the Object prototype - * http://www.json.org/js.html - * @singleton + * @class Ext.AbstractManager + * @extends Object + * Base Manager class - extended by ComponentMgr and PluginMgr */ -Ext.util.JSON = new (function(){ - var useHasOwn = !!{}.hasOwnProperty, - isNative = function() { - var useNative = null; - - return function() { - if (useNative === null) { - useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]'; - } +Ext.AbstractManager = Ext.extend(Object, { + typeName: 'type', + + constructor: function(config) { + Ext.apply(this, config || {}); - return useNative; - }; - }(), - pad = function(n) { - return n < 10 ? "0" + n : n; - }, - doDecode = function(json){ - return eval("(" + json + ')'); - }, - doEncode = function(o){ - if(!Ext.isDefined(o) || o === null){ - return "null"; - }else if(Ext.isArray(o)){ - return encodeArray(o); - }else if(Ext.isDate(o)){ - return Ext.util.JSON.encodeDate(o); - }else if(Ext.isString(o)){ - return encodeString(o); - }else if(typeof o == "number"){ - //don't use isNumber here, since finite checks happen inside isNumber - return isFinite(o) ? String(o) : "null"; - }else if(Ext.isBoolean(o)){ - return String(o); - }else { - var a = ["{"], b, i, v; - for (i in o) { - // don't encode DOM objects - if(!o.getElementsByTagName){ - if(!useHasOwn || o.hasOwnProperty(i)) { - v = o[i]; - switch (typeof v) { - case "undefined": - case "function": - case "unknown": - break; - default: - if(b){ - a.push(','); - } - a.push(doEncode(i), ":", - v === null ? "null" : doEncode(v)); - b = true; - } - } - } - } - a.push("}"); - return a.join(""); - } - }, - m = { - "\b": '\\b', - "\t": '\\t', - "\n": '\\n', - "\f": '\\f', - "\r": '\\r', - '"' : '\\"', - "\\": '\\\\' - }, - encodeString = function(s){ - if (/["\\\x00-\x1f]/.test(s)) { - return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) { - var c = m[b]; - if(c){ - return c; - } - c = b.charCodeAt(); - return "\\u00" + - Math.floor(c / 16).toString(16) + - (c % 16).toString(16); - }) + '"'; - } - return '"' + s + '"'; - }, - encodeArray = function(o){ - var a = ["["], b, i, l = o.length, v; - for (i = 0; i < l; i += 1) { - v = o[i]; - switch (typeof v) { - case "undefined": - case "function": - case "unknown": - break; - default: - if (b) { - a.push(','); - } - a.push(v === null ? "null" : Ext.util.JSON.encode(v)); - b = true; - } - } - a.push("]"); - return a.join(""); - }; - + /** + * Contains all of the items currently managed + * @property all + * @type Ext.util.MixedCollection + */ + this.all = new Ext.util.MixedCollection(); + + this.types = {}; + }, + /** - *

      Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression. - * The returned value includes enclosing double quotation marks.

      - *

      The default return format is "yyyy-mm-ddThh:mm:ss".

      - *

      To override this:

      
      -Ext.util.JSON.encodeDate = function(d) {
      -    return d.format('"Y-m-d"');
      -};
      -
      - * @param {Date} d The Date to encode - * @return {String} The string literal to use in a JSON string. + * Returns a component by {@link Ext.Component#id id}. + * For additional details see {@link Ext.util.MixedCollection#get}. + * @param {String} id The component {@link Ext.Component#id id} + * @return Ext.Component The Component, undefined if not found, or null if a + * Class was found. */ - this.encodeDate = function(o){ - return '"' + o.getFullYear() + "-" + - pad(o.getMonth() + 1) + "-" + - pad(o.getDate()) + "T" + - pad(o.getHours()) + ":" + - pad(o.getMinutes()) + ":" + - pad(o.getSeconds()) + '"'; - }; - + get : function(id){ + return this.all.get(id); + }, + /** - * Encodes an Object, Array or other value - * @param {Mixed} o The variable to encode - * @return {String} The JSON string + * Registers an item to be managed + * @param {Mixed} item The item to register */ - this.encode = function() { - var ec; - return function(o) { - if (!ec) { - // setup encoding function on first access - ec = isNative() ? JSON.stringify : doEncode; - } - return ec(o); - }; - }(); - - + register: function(item) { + this.all.add(item); + }, + /** - * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set. - * @param {String} json The JSON string - * @return {Object} The resulting object + * Unregisters a component by removing it from this manager + * @param {Mixed} item The item to unregister */ - this.decode = function() { - var dc; - return function(json) { - if (!dc) { - // setup decoding function on first access - dc = isNative() ? JSON.parse : doDecode; + unregister: function(item) { + this.all.remove(item); + }, + + /** + *

      Registers a new Component constructor, keyed by a new + * {@link Ext.Component#xtype}.

      + *

      Use this method (or its alias {@link Ext#reg Ext.reg}) to register new + * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying + * child Components. + * see {@link Ext.Container#items}

      + * @param {String} xtype The mnemonic string by which the Component class may be looked up. + * @param {Constructor} cls The new Component class. + */ + registerType : function(type, cls){ + this.types[type] = cls; + cls[this.typeName] = type; + }, + + /** + * Checks if a Component type is registered. + * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up + * @return {Boolean} Whether the type is registered. + */ + isRegistered : function(type){ + return this.types[type] !== undefined; + }, + + /** + * Creates and returns an instance of whatever this manager manages, based on the supplied type and config object + * @param {Object} config The config object + * @param {String} defaultType If no type is discovered in the config object, we fall back to this type + * @return {Mixed} The instance of whatever this manager is managing + */ + create: function(config, defaultType) { + var type = config[this.typeName] || config.type || defaultType, + Constructor = this.types[type]; + + if (Constructor == undefined) { + throw new Error(String.format("The '{0}' type has not been registered with this manager", type)); + } + + return new Constructor(config); + }, + + /** + * Registers a function that will be called when a Component with the specified id is added to the manager. This will happen on instantiation. + * @param {String} id The component {@link Ext.Component#id id} + * @param {Function} fn The callback function + * @param {Object} scope The scope (this reference) in which the callback is executed. Defaults to the Component. + */ + onAvailable : function(id, fn, scope){ + var all = this.all; + + all.on("add", function(index, o){ + if (o.id == id) { + fn.call(scope || o, o); + all.un("add", fn, scope); } - return dc(json); - }; - }(); - -})(); -/** - * Shorthand for {@link Ext.util.JSON#encode} - * @param {Mixed} o The variable to encode - * @return {String} The JSON string - * @member Ext - * @method encode - */ -Ext.encode = Ext.util.JSON.encode; -/** - * Shorthand for {@link Ext.util.JSON#decode} - * @param {String} json The JSON string - * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid. - * @return {Object} The resulting object - * @member Ext - * @method decode - */ -Ext.decode = Ext.util.JSON.decode; -/** + }); + } +});/** * @class Ext.util.Format * Reusable data formatting functions * @singleton */ -Ext.util.Format = function(){ - var trimRe = /^\s+|\s+$/g, - stripTagsRE = /<\/?[^>]+>/gi, +Ext.util.Format = function() { + var trimRe = /^\s+|\s+$/g, + stripTagsRE = /<\/?[^>]+>/gi, stripScriptsRe = /(?:)((\n|\r|.)*?)(?:<\/script>)/ig, - nl2brRe = /\r?\n/g; + nl2brRe = /\r?\n/g; return { /** @@ -12688,17 +13974,17 @@ Ext.util.Format = function(){ * @param {Boolean} word True to try to find a common work break * @return {String} The converted text */ - ellipsis : function(value, len, word){ - if(value && value.length > len){ - if(word){ - var vs = value.substr(0, len - 2), + ellipsis : function(value, len, word) { + if (value && value.length > len) { + if (word) { + var vs = value.substr(0, len - 2), index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?')); - if(index == -1 || index < (len - 15)){ + if (index == -1 || index < (len - 15)) { return value.substr(0, len - 3) + "..."; - }else{ + } else { return vs.substr(0, index) + "..."; } - } else{ + } else { return value.substr(0, len - 3) + "..."; } } @@ -12710,7 +13996,7 @@ Ext.util.Format = function(){ * @param {Mixed} value Reference to check * @return {Mixed} Empty string if converted, otherwise the original value */ - undef : function(value){ + undef : function(value) { return value !== undefined ? value : ""; }, @@ -12720,7 +14006,7 @@ Ext.util.Format = function(){ * @param {String} defaultValue The value to insert of it's undefined (defaults to "") * @return {String} */ - defaultValue : function(value, defaultValue){ + defaultValue : function(value, defaultValue) { return value !== undefined && value !== '' ? value : defaultValue; }, @@ -12729,7 +14015,7 @@ Ext.util.Format = function(){ * @param {String} value The string to encode * @return {String} The encoded text */ - htmlEncode : function(value){ + htmlEncode : function(value) { return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&"); }, @@ -12747,7 +14033,7 @@ Ext.util.Format = function(){ * @param {String} value The text to trim * @return {String} The trimmed text */ - trim : function(value){ + trim : function(value) { return String(value).replace(trimRe, ""); }, @@ -12758,7 +14044,7 @@ Ext.util.Format = function(){ * @param {Number} length The length of the substring * @return {String} The substring */ - substr : function(value, start, length){ + substr : function(value, start, length) { return String(value).substr(start, length); }, @@ -12767,7 +14053,7 @@ Ext.util.Format = function(){ * @param {String} value The text to convert * @return {String} The converted text */ - lowercase : function(value){ + lowercase : function(value) { return String(value).toLowerCase(); }, @@ -12776,7 +14062,7 @@ Ext.util.Format = function(){ * @param {String} value The text to convert * @return {String} The converted text */ - uppercase : function(value){ + uppercase : function(value) { return String(value).toUpperCase(); }, @@ -12785,17 +14071,17 @@ Ext.util.Format = function(){ * @param {String} value The text to convert * @return {String} The converted text */ - capitalize : function(value){ + capitalize : function(value) { return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase(); }, // private - call : function(value, fn){ - if(arguments.length > 2){ + call : function(value, fn) { + if (arguments.length > 2) { var args = Array.prototype.slice.call(arguments, 2); args.unshift(value); return eval(fn).apply(window, args); - }else{ + } else { return eval(fn).call(window, value); } }, @@ -12805,7 +14091,7 @@ Ext.util.Format = function(){ * @param {Number/String} value The numeric value to format * @return {String} The formatted currency string */ - usMoney : function(v){ + usMoney : function(v) { v = (Math.round((v-0)*100))/100; v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v); v = String(v); @@ -12817,7 +14103,7 @@ Ext.util.Format = function(){ whole = whole.replace(r, '$1' + ',' + '$2'); } v = whole + sub; - if(v.charAt(0) == '-'){ + if (v.charAt(0) == '-') { return '-$' + v.substr(1); } return "$" + v; @@ -12829,11 +14115,11 @@ Ext.util.Format = function(){ * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y') * @return {String} The formatted date string */ - date : function(v, format){ - if(!v){ + date : function(v, format) { + if (!v) { return ""; } - if(!Ext.isDate(v)){ + if (!Ext.isDate(v)) { v = new Date(Date.parse(v)); } return v.dateFormat(format || "m/d/Y"); @@ -12844,8 +14130,8 @@ Ext.util.Format = function(){ * @param {String} format Any valid date format string * @return {Function} The date formatting function */ - dateRenderer : function(format){ - return function(v){ + dateRenderer : function(format) { + return function(v) { return Ext.util.Format.date(v, format); }; }, @@ -12855,7 +14141,7 @@ Ext.util.Format = function(){ * @param {Mixed} value The text from which to strip tags * @return {String} The stripped text */ - stripTags : function(v){ + stripTags : function(v) { return !v ? v : String(v).replace(stripTagsRE, ""); }, @@ -12864,7 +14150,7 @@ Ext.util.Format = function(){ * @param {Mixed} value The text from which to strip script tags * @return {String} The stripped text */ - stripScripts : function(v){ + stripScripts : function(v) { return !v ? v : String(v).replace(stripScriptsRe, ""); }, @@ -12873,10 +14159,10 @@ Ext.util.Format = function(){ * @param {Number/String} size The numeric value to format * @return {String} The formatted file size */ - fileSize : function(size){ - if(size < 1024) { + fileSize : function(size) { + if (size < 1024) { return size + " bytes"; - } else if(size < 1048576) { + } else if (size < 1048576) { return (Math.round(((size*10) / 1024))/10) + " KB"; } else { return (Math.round(((size*10) / 1048576))/10) + " MB"; @@ -12891,12 +14177,13 @@ Ext.util.Format = function(){ */ math : function(){ var fns = {}; + return function(v, a){ - if(!fns[a]){ + if (!fns[a]) { fns[a] = new Function('v', 'return v ' + a + ';'); } return fns[a](v); - } + }; }(), /** @@ -12932,34 +14219,34 @@ Ext.util.Format = function(){ * @return {String} The formatted number. */ number: function(v, format) { - if(!format){ + if (!format) { return v; } v = Ext.num(v, NaN); - if (isNaN(v)){ + if (isNaN(v)) { return ''; } var comma = ',', - dec = '.', - i18n = false, - neg = v < 0; + dec = '.', + i18n = false, + neg = v < 0; v = Math.abs(v); - if(format.substr(format.length - 2) == '/i'){ + if (format.substr(format.length - 2) == '/i') { format = format.substr(0, format.length - 2); - i18n = true; - comma = '.'; - dec = ','; + i18n = true; + comma = '.'; + dec = ','; } var hasComma = format.indexOf(comma) != -1, - psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec); + psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec); - if(1 < psplit.length){ + if (1 < psplit.length) { v = v.toFixed(psplit[1].length); - }else if(2 < psplit.length){ + } else if(2 < psplit.length) { throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format); - }else{ + } else { v = v.toFixed(0); } @@ -12968,12 +14255,18 @@ Ext.util.Format = function(){ psplit = fnum.split('.'); if (hasComma) { - var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3; - - for (var i = 0; i < j; i += n) { + var cnum = psplit[0], + parr = [], + j = cnum.length, + m = Math.floor(j / 3), + n = cnum.length % 3 || 3, + i; + + for (i = 0; i < j; i += n) { if (i != 0) { n = 3; } + parr[parr.length] = cnum.substr(i, n); m -= 1; } @@ -12995,8 +14288,8 @@ Ext.util.Format = function(){ * @param {String} format Any valid number format string for {@link #number} * @return {Function} The number formatting function */ - numberRenderer : function(format){ - return function(v){ + numberRenderer : function(format) { + return function(v) { return Ext.util.Format.number(v, format); }; }, @@ -13009,7 +14302,7 @@ Ext.util.Format = function(){ * @param {String} singular The singular form of the word * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s") */ - plural : function(v, s, p){ + plural : function(v, s, p) { return v +' ' + (v == 1 ? s : (p ? p : s+'s')); }, @@ -13018,10 +14311,10 @@ Ext.util.Format = function(){ * @param {String} The string value to format. * @return {String} The string with embedded <br/> tags in place of newlines. */ - nl2br : function(v){ + nl2br : function(v) { return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '
      '); } - } + }; }(); /** * @class Ext.XTemplate @@ -13913,8 +15206,8 @@ Ext.KeyNav.prototype = { // private relay : function(e){ - var k = e.getKey(); - var h = this.keyToHandler[k]; + var k = e.getKey(), + h = this.keyToHandler[k]; if(h && this[h]){ if(this.doRelay(e, this[h], h) !== true){ e[this.defaultEventAction](); @@ -13924,7 +15217,7 @@ Ext.KeyNav.prototype = { // private doRelay : function(e, h, hname){ - return h.call(this.scope || this, e); + return h.call(this.scope || this, e, hname); }, // possible handlers @@ -14768,10 +16061,11 @@ editorgrid {@link Ext.grid.EditorGridPanel} flash {@link Ext.FlashComponent} grid {@link Ext.grid.GridPanel} listview {@link Ext.ListView} +multislider {@link Ext.slider.MultiSlider} panel {@link Ext.Panel} progress {@link Ext.ProgressBar} propertygrid {@link Ext.grid.PropertyGrid} -slider {@link Ext.Slider} +slider {@link Ext.slider.SingleSlider} spacer {@link Ext.Spacer} splitbutton {@link Ext.SplitButton} tabpanel {@link Ext.TabPanel} @@ -14808,6 +16102,7 @@ form {@link Ext.form.FormPanel} checkbox {@link Ext.form.Checkbox} checkboxgroup {@link Ext.form.CheckboxGroup} combo {@link Ext.form.ComboBox} +compositefield {@link Ext.form.CompositeField} datefield {@link Ext.form.DateField} displayfield {@link Ext.form.DisplayField} field {@link Ext.form.Field} @@ -16107,7 +17402,7 @@ new Ext.Panel({ if(delay){ this.focusTask = new Ext.util.DelayedTask(this.focus, this, [selectText, false]); this.focusTask.delay(Ext.isNumber(delay) ? delay : 10); - return; + return this; } if(this.rendered && !this.isDestroyed){ this.el.focus(); @@ -16290,7 +17585,13 @@ var isText = t.isXType('textfield'); // true var isBoxSubclass = t.isXType('box'); // true, descended from BoxComponent var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance
    - * @param {String} xtype The xtype to check for this Component + * @param {String/Ext.Component/Class} xtype The xtype to check for this Component. Note that the the component can either be an instance + * or a component class: + *
    
    +var c = new Ext.Component();
    +console.log(c.isXType(c));
    +console.log(c.isXType(Ext.Component)); 
    +
    * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is * the default), or true to check whether this Component is directly of the specified xtype. * @return {Boolean} True if this component descends from the specified xtype, false otherwise. @@ -16345,17 +17646,37 @@ alert(t.getXTypes()); // alerts 'component/box/field/textfield' /** * Find a container above this component at any level by xtype or class - * @param {String/Class} xtype The xtype string for a component, or the class of the component directly + * @param {String/Ext.Component/Class} xtype The xtype to check for this Component. Note that the the component can either be an instance + * or a component class: + * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is + * the default), or true to check whether this Component is directly of the specified xtype. * @return {Ext.Container} The first Container which matches the given xtype or class */ - findParentByType : function(xtype) { - return Ext.isFunction(xtype) ? - this.findParentBy(function(p){ - return p.constructor === xtype; - }) : - this.findParentBy(function(p){ - return p.constructor.xtype === xtype; - }); + findParentByType : function(xtype, shallow){ + return this.findParentBy(function(c){ + return c.isXType(xtype, shallow); + }); + }, + + /** + * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (this) of + * function call will be the scope provided or the current component. The arguments to the function + * will be the args provided or the current component. If the function returns false at any point, + * the bubble is stopped. + * @param {Function} fn The function to call + * @param {Object} scope (optional) The scope of the function (defaults to current node) + * @param {Array} args (optional) The args to call the function with (default to passing the current component) + * @return {Ext.Component} this + */ + bubble : function(fn, scope, args){ + var p = this; + while(p){ + if(fn.apply(scope || p, args || [p]) === false){ + break; + } + p = p.ownerCt; + } + return this; }, // protected @@ -16501,7 +17822,8 @@ myGridPanel.mon(myGridPanel.getSelectionModel(), { } }); -Ext.reg('component', Ext.Component);/** +Ext.reg('component', Ext.Component); +/** * @class Ext.Action *

    An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it * can be usefully shared among multiple components. Actions let you share handlers, configuration options and UI @@ -16771,9 +18093,10 @@ Ext.Action = Ext.extend(Object, { (function(){ Ext.Layer = function(config, existingEl){ config = config || {}; - var dh = Ext.DomHelper; - var cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body; - if(existingEl){ + var dh = Ext.DomHelper, + cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body; + + if (existingEl) { this.dom = Ext.getDom(existingEl); } if(!this.dom){ @@ -17219,19 +18542,23 @@ Ext.extend(Ext.Layer, Ext.Element, { * Create a new Shadow * @param {Object} config The config object */ -Ext.Shadow = function(config){ +Ext.Shadow = function(config) { Ext.apply(this, config); - if(typeof this.mode != "string"){ + if (typeof this.mode != "string") { this.mode = this.defaultMode; } - var o = this.offset, a = {h: 0}; - var rad = Math.floor(this.offset/2); - switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows + var o = this.offset, + a = { + h: 0 + }, + rad = Math.floor(this.offset / 2); + switch (this.mode.toLowerCase()) { + // all this hideous nonsense calculates the various offsets for shadows case "drop": a.w = 0; a.l = a.t = o; a.t -= 1; - if(Ext.isIE){ + if (Ext.isIE) { a.l -= this.offset + rad; a.t -= this.offset + rad; a.w -= rad; @@ -17240,24 +18567,24 @@ Ext.Shadow = function(config){ } break; case "sides": - a.w = (o*2); + a.w = (o * 2); a.l = -o; - a.t = o-1; - if(Ext.isIE){ + a.t = o - 1; + if (Ext.isIE) { a.l -= (this.offset - rad); a.t -= this.offset + rad; a.l += 1; - a.w -= (this.offset - rad)*2; + a.w -= (this.offset - rad) * 2; a.w -= rad + 1; a.h -= 1; } break; case "frame": - a.w = a.h = (o*2); + a.w = a.h = (o * 2); a.l = a.t = -o; a.t += 1; a.h -= 2; - if(Ext.isIE){ + if (Ext.isIE) { a.l -= (this.offset - rad); a.t -= (this.offset - rad); a.l += 1; @@ -17293,23 +18620,23 @@ Ext.Shadow.prototype = { * Displays the shadow under the target element * @param {Mixed} targetEl The id or element under which the shadow should display */ - show : function(target){ + show: function(target) { target = Ext.get(target); - if(!this.el){ + if (!this.el) { this.el = Ext.Shadow.Pool.pull(); - if(this.el.dom.nextSibling != target.dom){ + if (this.el.dom.nextSibling != target.dom) { this.el.insertBefore(target); } } - this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1); - if(Ext.isIE){ - this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")"; + this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10) - 1); + if (Ext.isIE) { + this.el.dom.style.filter = "progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (this.offset) + ")"; } this.realign( - target.getLeft(true), - target.getTop(true), - target.getWidth(), - target.getHeight() + target.getLeft(true), + target.getTop(true), + target.getWidth(), + target.getHeight() ); this.el.dom.style.display = "block"; }, @@ -17317,8 +18644,8 @@ Ext.Shadow.prototype = { /** * Returns true if the shadow is visible, else false */ - isVisible : function(){ - return this.el ? true : false; + isVisible: function() { + return this.el ? true: false; }, /** @@ -17329,25 +18656,32 @@ Ext.Shadow.prototype = { * @param {Number} width The target element width * @param {Number} height The target element height */ - realign : function(l, t, w, h){ - if(!this.el){ + realign: function(l, t, w, h) { + if (!this.el) { return; } - var a = this.adjusts, d = this.el.dom, s = d.style; - var iea = 0; - s.left = (l+a.l)+"px"; - s.top = (t+a.t)+"px"; - var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px"; - if(s.width != sws || s.height != shs){ + var a = this.adjusts, + d = this.el.dom, + s = d.style, + iea = 0, + sw = (w + a.w), + sh = (h + a.h), + sws = sw + "px", + shs = sh + "px", + cn, + sww; + s.left = (l + a.l) + "px"; + s.top = (t + a.t) + "px"; + if (s.width != sws || s.height != shs) { s.width = sws; s.height = shs; - if(!Ext.isIE){ - var cn = d.childNodes; - var sww = Math.max(0, (sw-12))+"px"; + if (!Ext.isIE) { + cn = d.childNodes; + sww = Math.max(0, (sw - 12)) + "px"; cn[0].childNodes[1].style.width = sww; cn[1].childNodes[1].style.width = sww; cn[2].childNodes[1].style.width = sww; - cn[1].style.height = Math.max(0, (sh-12))+"px"; + cn[1].style.height = Math.max(0, (sh - 12)) + "px"; } } }, @@ -17355,8 +18689,8 @@ Ext.Shadow.prototype = { /** * Hides this shadow */ - hide : function(){ - if(this.el){ + hide: function() { + if (this.el) { this.el.dom.style.display = "none"; Ext.Shadow.Pool.push(this.el); delete this.el; @@ -17367,31 +18701,31 @@ Ext.Shadow.prototype = { * Adjust the z-index of this shadow * @param {Number} zindex The new z-index */ - setZIndex : function(z){ + setZIndex: function(z) { this.zIndex = z; - if(this.el){ + if (this.el) { this.el.setStyle("z-index", z); } } }; // Private utility class that manages the internal Shadow cache -Ext.Shadow.Pool = function(){ - var p = []; - var markup = Ext.isIE ? - '

    ' : - '
    '; +Ext.Shadow.Pool = function() { + var p = [], + markup = Ext.isIE ? + '
    ': + '
    '; return { - pull : function(){ + pull: function() { var sh = p.shift(); - if(!sh){ + if (!sh) { sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup)); sh.autoBoxAdjust = false; } return sh; }, - push : function(sh){ + push: function(sh) { p.push(sh); } }; @@ -18670,3868 +20004,5127 @@ layoutConfig: { *
    */ /** - * @cfg {Object} layoutConfig - * This is a config object containing properties specific to the chosen - * {@link #layout} if {@link #layout} - * has been specified as a string.

    + * @cfg {Object} layoutConfig + * This is a config object containing properties specific to the chosen + * {@link #layout} if {@link #layout} + * has been specified as a string.

    + */ + /** + * @cfg {Boolean/Number} bufferResize + * When set to true (50 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer + * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers + * with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to 50. + */ + bufferResize: 50, + + /** + * @cfg {String/Number} activeItem + * A string component id or the numeric index of the component that should be initially activated within the + * container's layout on render. For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first + * item in the container's collection). activeItem only applies to layout styles that can display + * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and + * {@link Ext.layout.FitLayout}). Related to {@link Ext.layout.ContainerLayout#activeItem}. + */ + /** + * @cfg {Object/Array} items + *
    ** IMPORTANT: be sure to {@link #layout specify a layout} if needed ! **
    + *

    A single item, or an array of child Components to be added to this container, + * for example:

    + *
    
    +// specifying a single item
    +items: {...},
    +layout: 'fit',    // specify a layout!
    +
    +// specifying multiple items
    +items: [{...}, {...}],
    +layout: 'anchor', // specify a layout!
    +     * 
    + *

    Each item may be:

    + *
    + *

    Notes:

    + *
    + */ + /** + * @cfg {Object|Function} defaults + *

    This option is a means of applying default settings to all added items whether added through the {@link #items} + * config or via the {@link #add} or {@link #insert} methods.

    + *

    If an added item is a config object, and not an instantiated Component, then the default properties are + * unconditionally applied. If the added item is an instantiated Component, then the default properties are + * applied conditionally so as not to override existing properties in the item.

    + *

    If the defaults option is specified as a function, then the function will be called using this Container as the + * scope (this reference) and passing the added item as the first parameter. Any resulting object + * from that call is then applied to the item as default properties.

    + *

    For example, to automatically apply padding to the body of each of a set of + * contained {@link Ext.Panel} items, you could pass: defaults: {bodyStyle:'padding:15px'}.

    + *

    Usage:

    
    +defaults: {               // defaults are applied to items, not the container
    +    autoScroll:true
    +},
    +items: [
    +    {
    +        xtype: 'panel',   // defaults do not have precedence over
    +        id: 'panel1',     // options in config objects, so the defaults
    +        autoScroll: false // will not be applied here, panel1 will be autoScroll:false
    +    },
    +    new Ext.Panel({       // defaults do have precedence over options
    +        id: 'panel2',     // options in components, so the defaults
    +        autoScroll: false // will be applied here, panel2 will be autoScroll:true.
    +    })
    +]
    +     * 
    + */ + + + /** @cfg {Boolean} autoDestroy + * If true the container will automatically destroy any contained component that is removed from it, else + * destruction must be handled manually (defaults to true). + */ + autoDestroy : true, + + /** @cfg {Boolean} forceLayout + * If true the container will force a layout initially even if hidden or collapsed. This option + * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false). + */ + forceLayout: false, + + /** @cfg {Boolean} hideBorders + * True to hide the borders of each contained component, false to defer to the component's existing + * border settings (defaults to false). + */ + /** @cfg {String} defaultType + *

    The default {@link Ext.Component xtype} of child Components to create in this Container when + * a child item is specified as a raw configuration object, rather than as an instantiated Component.

    + *

    Defaults to 'panel', except {@link Ext.menu.Menu} which defaults to 'menuitem', + * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to 'button'.

    + */ + defaultType : 'panel', + + /** @cfg {String} resizeEvent + * The event to listen to for resizing in layouts. Defaults to 'resize'. + */ + resizeEvent: 'resize', + + /** + * @cfg {Array} bubbleEvents + *

    An array of events that, when fired, should be bubbled to any parent container. + * See {@link Ext.util.Observable#enableBubble}. + * Defaults to ['add', 'remove']. + */ + bubbleEvents: ['add', 'remove'], + + // private + initComponent : function(){ + Ext.Container.superclass.initComponent.call(this); + + this.addEvents( + /** + * @event afterlayout + * Fires when the components in this container are arranged by the associated layout manager. + * @param {Ext.Container} this + * @param {ContainerLayout} layout The ContainerLayout implementation for this container + */ + 'afterlayout', + /** + * @event beforeadd + * Fires before any {@link Ext.Component} is added or inserted into the container. + * A handler can return false to cancel the add. + * @param {Ext.Container} this + * @param {Ext.Component} component The component being added + * @param {Number} index The index at which the component will be added to the container's items collection + */ + 'beforeadd', + /** + * @event beforeremove + * Fires before any {@link Ext.Component} is removed from the container. A handler can return + * false to cancel the remove. + * @param {Ext.Container} this + * @param {Ext.Component} component The component being removed + */ + 'beforeremove', + /** + * @event add + * @bubbles + * Fires after any {@link Ext.Component} is added or inserted into the container. + * @param {Ext.Container} this + * @param {Ext.Component} component The component that was added + * @param {Number} index The index at which the component was added to the container's items collection + */ + 'add', + /** + * @event remove + * @bubbles + * Fires after any {@link Ext.Component} is removed from the container. + * @param {Ext.Container} this + * @param {Ext.Component} component The component that was removed + */ + 'remove' + ); + + /** + * The collection of components in this container as a {@link Ext.util.MixedCollection} + * @type MixedCollection + * @property items + */ + var items = this.items; + if(items){ + delete this.items; + this.add(items); + } + }, + + // private + initItems : function(){ + if(!this.items){ + this.items = new Ext.util.MixedCollection(false, this.getComponentId); + this.getLayout(); // initialize the layout + } + }, + + // private + setLayout : function(layout){ + if(this.layout && this.layout != layout){ + this.layout.setContainer(null); + } + this.layout = layout; + this.initItems(); + layout.setContainer(this); + }, + + afterRender: function(){ + // Render this Container, this should be done before setLayout is called which + // will hook onResize + Ext.Container.superclass.afterRender.call(this); + if(!this.layout){ + this.layout = 'auto'; + } + if(Ext.isObject(this.layout) && !this.layout.layout){ + this.layoutConfig = this.layout; + this.layout = this.layoutConfig.type; + } + if(Ext.isString(this.layout)){ + this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig); + } + this.setLayout(this.layout); + + // If a CardLayout, the active item set + if(this.activeItem !== undefined && this.layout.setActiveItem){ + var item = this.activeItem; + delete this.activeItem; + this.layout.setActiveItem(item); + } + + // If we have no ownerCt, render and size all children + if(!this.ownerCt){ + this.doLayout(false, true); + } + + // This is a manually configured flag set by users in conjunction with renderTo. + // Not to be confused with the flag by the same name used in Layouts. + if(this.monitorResize === true){ + Ext.EventManager.onWindowResize(this.doLayout, this, [false]); + } + }, + + /** + *

    Returns the Element to be used to contain the child Components of this Container.

    + *

    An implementation is provided which returns the Container's {@link #getEl Element}, but + * if there is a more complex structure to a Container, this may be overridden to return + * the element into which the {@link #layout layout} renders child Components.

    + * @return {Ext.Element} The Element to render child Components into. + */ + getLayoutTarget : function(){ + return this.el; + }, + + // private - used as the key lookup function for the items collection + getComponentId : function(comp){ + return comp.getItemId(); + }, + + /** + *

    Adds {@link Ext.Component Component}(s) to this Container.

    + *

    Description : + *

    + *

    Notes : + *

    + * @param {...Object/Array} component + *

    Either one or more Components to add or an Array of Components to add. See + * {@link #items} for additional information.

    + * @return {Ext.Component/Array} The Components that were added. + */ + add : function(comp){ + this.initItems(); + var args = arguments.length > 1; + if(args || Ext.isArray(comp)){ + var result = []; + Ext.each(args ? arguments : comp, function(c){ + result.push(this.add(c)); + }, this); + return result; + } + var c = this.lookupComponent(this.applyDefaults(comp)); + var index = this.items.length; + if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){ + this.items.add(c); + // *onAdded + c.onAdded(this, index); + this.onAdd(c); + this.fireEvent('add', this, c, index); + } + return c; + }, + + onAdd : function(c){ + // Empty template method + }, + + // private + onAdded : function(container, pos) { + //overridden here so we can cascade down, not worth creating a template method. + this.ownerCt = container; + this.initRef(); + //initialize references for child items + this.cascade(function(c){ + c.initRef(); + }); + this.fireEvent('added', this, container, pos); + }, + + /** + * Inserts a Component into this Container at a specified index. Fires the + * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the + * Component has been inserted. + * @param {Number} index The index at which the Component will be inserted + * into the Container's items collection + * @param {Ext.Component} component The child Component to insert.

    + * Ext uses lazy rendering, and will only render the inserted Component should + * it become necessary.

    + * A Component config object may be passed in order to avoid the overhead of + * constructing a real Component object if lazy rendering might mean that the + * inserted Component will not be rendered immediately. To take advantage of + * this 'lazy instantiation', set the {@link Ext.Component#xtype} config + * property to the registered type of the Component wanted.

    + * For a list of all available xtypes, see {@link Ext.Component}. + * @return {Ext.Component} component The Component (or config object) that was + * inserted with the Container's default config values applied. + */ + insert : function(index, comp) { + var args = arguments, + length = args.length, + result = [], + i, c; + + this.initItems(); + + if (length > 2) { + for (i = length - 1; i >= 1; --i) { + result.push(this.insert(index, args[i])); + } + return result; + } + + c = this.lookupComponent(this.applyDefaults(comp)); + index = Math.min(index, this.items.length); + + if (this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false) { + if (c.ownerCt == this) { + this.items.remove(c); + } + this.items.insert(index, c); + c.onAdded(this, index); + this.onAdd(c); + this.fireEvent('add', this, c, index); + } + + return c; + }, + + // private + applyDefaults : function(c){ + var d = this.defaults; + if(d){ + if(Ext.isFunction(d)){ + d = d.call(this, c); + } + if(Ext.isString(c)){ + c = Ext.ComponentMgr.get(c); + Ext.apply(c, d); + }else if(!c.events){ + Ext.applyIf(c, d); + }else{ + Ext.apply(c, d); + } + } + return c; + }, + + // private + onBeforeAdd : function(item){ + if(item.ownerCt){ + item.ownerCt.remove(item, false); + } + if(this.hideBorders === true){ + item.border = (item.border === true); + } + }, + + /** + * Removes a component from this container. Fires the {@link #beforeremove} event before removing, then fires + * the {@link #remove} event after the component has been removed. + * @param {Component/String} component The component reference or id to remove. + * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. + * Defaults to the value of this Container's {@link #autoDestroy} config. + * @return {Ext.Component} component The Component that was removed. + */ + remove : function(comp, autoDestroy){ + this.initItems(); + var c = this.getComponent(comp); + if(c && this.fireEvent('beforeremove', this, c) !== false){ + this.doRemove(c, autoDestroy); + this.fireEvent('remove', this, c); + } + return c; + }, + + onRemove: function(c){ + // Empty template method + }, + + // private + doRemove: function(c, autoDestroy){ + var l = this.layout, + hasLayout = l && this.rendered; + + if(hasLayout){ + l.onRemove(c); + } + this.items.remove(c); + c.onRemoved(); + this.onRemove(c); + if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){ + c.destroy(); + } + if(hasLayout){ + l.afterRemove(c); + } + }, + + /** + * Removes all components from this container. + * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. + * Defaults to the value of this Container's {@link #autoDestroy} config. + * @return {Array} Array of the destroyed components */ + removeAll: function(autoDestroy){ + this.initItems(); + var item, rem = [], items = []; + this.items.each(function(i){ + rem.push(i); + }); + for (var i = 0, len = rem.length; i < len; ++i){ + item = rem[i]; + this.remove(item, autoDestroy); + if(item.ownerCt !== this){ + items.push(item); + } + } + return items; + }, + /** - * @cfg {Boolean/Number} bufferResize - * When set to true (50 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer - * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers - * with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to 50. + * Examines this container's {@link #items} property + * and gets a direct child component of this container. + * @param {String/Number} comp This parameter may be any of the following: + *
    + *

    For additional information see {@link Ext.util.MixedCollection#get}. + * @return Ext.Component The component (if found). */ - bufferResize: 50, + getComponent : function(comp){ + if(Ext.isObject(comp)){ + comp = comp.getItemId(); + } + return this.items.get(comp); + }, + + // private + lookupComponent : function(comp){ + if(Ext.isString(comp)){ + return Ext.ComponentMgr.get(comp); + }else if(!comp.events){ + return this.createComponent(comp); + } + return comp; + }, + + // private + createComponent : function(config, defaultType){ + if (config.render) { + return config; + } + // add in ownerCt at creation time but then immediately + // remove so that onBeforeAdd can handle it + var c = Ext.create(Ext.apply({ + ownerCt: this + }, config), defaultType || this.defaultType); + delete c.initialConfig.ownerCt; + delete c.ownerCt; + return c; + }, /** - * @cfg {String/Number} activeItem - * A string component id or the numeric index of the component that should be initially activated within the - * container's layout on render. For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first - * item in the container's collection). activeItem only applies to layout styles that can display - * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and - * {@link Ext.layout.FitLayout}). Related to {@link Ext.layout.ContainerLayout#activeItem}. + * @private + * We can only lay out if there is a view area in which to layout. + * display:none on the layout target, *or any of its parent elements* will mean it has no view area. */ - /** - * @cfg {Object/Array} items - *

    ** IMPORTANT: be sure to {@link #layout specify a layout} if needed ! **
    - *

    A single item, or an array of child Components to be added to this container, - * for example:

    - *
    
    -// specifying a single item
    -items: {...},
    -layout: 'fit',    // specify a layout!
    +    canLayout : function() {
    +        var el = this.getVisibilityEl();
    +        return el && el.dom && !el.isStyle("display", "none");
    +    },
     
    -// specifying multiple items
    -items: [{...}, {...}],
    -layout: 'anchor', // specify a layout!
    -     * 
    - *

    Each item may be:

    - *
    - *

    Notes:

    - *
    + /** + * Force this container's layout to be recalculated. A call to this function is required after adding a new component + * to an already rendered container, or possibly after changing sizing/position properties of child components. + * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto + * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer) + * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden. + * @return {Ext.Container} this */ + + doLayout : function(shallow, force){ + var rendered = this.rendered, + forceLayout = force || this.forceLayout; + + if(this.collapsed || !this.canLayout()){ + this.deferLayout = this.deferLayout || !shallow; + if(!forceLayout){ + return; + } + shallow = shallow && !this.deferLayout; + } else { + delete this.deferLayout; + } + if(rendered && this.layout){ + this.layout.layout(); + } + if(shallow !== true && this.items){ + var cs = this.items.items; + for(var i = 0, len = cs.length; i < len; i++){ + var c = cs[i]; + if(c.doLayout){ + c.doLayout(false, forceLayout); + } + } + } + if(rendered){ + this.onLayout(shallow, forceLayout); + } + // Initial layout completed + this.hasLayout = true; + delete this.forceLayout; + }, + + onLayout : Ext.emptyFn, + + // private + shouldBufferLayout: function(){ + /* + * Returns true if the container should buffer a layout. + * This is true only if the container has previously been laid out + * and has a parent container that is pending a layout. + */ + var hl = this.hasLayout; + if(this.ownerCt){ + // Only ever buffer if we've laid out the first time and we have one pending. + return hl ? !this.hasLayoutPending() : false; + } + // Never buffer initial layout + return hl; + }, + + // private + hasLayoutPending: function(){ + // Traverse hierarchy to see if any parent container has a pending layout. + var pending = false; + this.ownerCt.bubble(function(c){ + if(c.layoutPending){ + pending = true; + return false; + } + }); + return pending; + }, + + onShow : function(){ + // removes css classes that were added to hide + Ext.Container.superclass.onShow.call(this); + // If we were sized during the time we were hidden, layout. + if(Ext.isDefined(this.deferLayout)){ + delete this.deferLayout; + this.doLayout(true); + } + }, + /** - * @cfg {Object|Function} defaults - *

    This option is a means of applying default settings to all added items whether added through the {@link #items} - * config or via the {@link #add} or {@link #insert} methods.

    - *

    If an added item is a config object, and not an instantiated Component, then the default properties are - * unconditionally applied. If the added item is an instantiated Component, then the default properties are - * applied conditionally so as not to override existing properties in the item.

    - *

    If the defaults option is specified as a function, then the function will be called using this Container as the - * scope (this reference) and passing the added item as the first parameter. Any resulting object - * from that call is then applied to the item as default properties.

    - *

    For example, to automatically apply padding to the body of each of a set of - * contained {@link Ext.Panel} items, you could pass: defaults: {bodyStyle:'padding:15px'}.

    - *

    Usage:

    
    -defaults: {               // defaults are applied to items, not the container
    -    autoScroll:true
    -},
    -items: [
    -    {
    -        xtype: 'panel',   // defaults do not have precedence over
    -        id: 'panel1',     // options in config objects, so the defaults
    -        autoScroll: false // will not be applied here, panel1 will be autoScroll:false
    +     * Returns the layout currently in use by the container.  If the container does not currently have a layout
    +     * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout.
    +     * @return {ContainerLayout} layout The container's layout
    +     */
    +    getLayout : function(){
    +        if(!this.layout){
    +            var layout = new Ext.layout.AutoLayout(this.layoutConfig);
    +            this.setLayout(layout);
    +        }
    +        return this.layout;
         },
    -    new Ext.Panel({       // defaults do have precedence over options
    -        id: 'panel2',     // options in components, so the defaults
    -        autoScroll: false // will be applied here, panel2 will be autoScroll:true.
    -    })
    -]
    -     * 
    + + // private + beforeDestroy : function(){ + var c; + if(this.items){ + while(c = this.items.first()){ + this.doRemove(c, true); + } + } + if(this.monitorResize){ + Ext.EventManager.removeResizeListener(this.doLayout, this); + } + Ext.destroy(this.layout); + Ext.Container.superclass.beforeDestroy.call(this); + }, + + /** + * Cascades down the component/container heirarchy from this component (called first), calling the specified function with + * each component. The scope (this) of + * function call will be the scope provided or the current component. The arguments to the function + * will be the args provided or the current component. If the function returns false at any point, + * the cascade is stopped on that branch. + * @param {Function} fn The function to call + * @param {Object} scope (optional) The scope of the function (defaults to current component) + * @param {Array} args (optional) The args to call the function with (defaults to passing the current component) + * @return {Ext.Container} this */ + cascade : function(fn, scope, args){ + if(fn.apply(scope || this, args || [this]) !== false){ + if(this.items){ + var cs = this.items.items; + for(var i = 0, len = cs.length; i < len; i++){ + if(cs[i].cascade){ + cs[i].cascade(fn, scope, args); + }else{ + fn.apply(scope || cs[i], args || [cs[i]]); + } + } + } + } + return this; + }, + /** + * Find a component under this container at any level by id + * @param {String} id + * @deprecated Fairly useless method, since you can just use Ext.getCmp. Should be removed for 4.0 + * If you need to test if an id belongs to a container, you can use getCmp and findParent*. + * @return Ext.Component + */ + findById : function(id){ + var m = null, + ct = this; + this.cascade(function(c){ + if(ct != c && c.id === id){ + m = c; + return false; + } + }); + return m; + }, - /** @cfg {Boolean} autoDestroy - * If true the container will automatically destroy any contained component that is removed from it, else - * destruction must be handled manually (defaults to true). + /** + * Find a component under this container at any level by xtype or class + * @param {String/Class} xtype The xtype string for a component, or the class of the component directly + * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is + * the default), or true to check whether this Component is directly of the specified xtype. + * @return {Array} Array of Ext.Components */ - autoDestroy : true, + findByType : function(xtype, shallow){ + return this.findBy(function(c){ + return c.isXType(xtype, shallow); + }); + }, - /** @cfg {Boolean} forceLayout - * If true the container will force a layout initially even if hidden or collapsed. This option - * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false). + /** + * Find a component under this container at any level by property + * @param {String} prop + * @param {String} value + * @return {Array} Array of Ext.Components */ - forceLayout: false, + find : function(prop, value){ + return this.findBy(function(c){ + return c[prop] === value; + }); + }, - /** @cfg {Boolean} hideBorders - * True to hide the borders of each contained component, false to defer to the component's existing - * border settings (defaults to false). + /** + * Find a component under this container at any level by a custom function. If the passed function returns + * true, the component will be included in the results. The passed function is called with the arguments (component, this container). + * @param {Function} fn The function to call + * @param {Object} scope (optional) + * @return {Array} Array of Ext.Components */ - /** @cfg {String} defaultType - *

    The default {@link Ext.Component xtype} of child Components to create in this Container when - * a child item is specified as a raw configuration object, rather than as an instantiated Component.

    - *

    Defaults to 'panel', except {@link Ext.menu.Menu} which defaults to 'menuitem', - * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to 'button'.

    + findBy : function(fn, scope){ + var m = [], ct = this; + this.cascade(function(c){ + if(ct != c && fn.call(scope || c, c, ct) === true){ + m.push(c); + } + }); + return m; + }, + + /** + * Get a component contained by this container (alias for items.get(key)) + * @param {String/Number} key The index or id of the component + * @deprecated Should be removed in 4.0, since getComponent does the same thing. + * @return {Ext.Component} Ext.Component */ - defaultType : 'panel', + get : function(key){ + return this.getComponent(key); + } +}); - /** @cfg {String} resizeEvent - * The event to listen to for resizing in layouts. Defaults to 'resize'. +Ext.Container.LAYOUTS = {}; +Ext.reg('container', Ext.Container); +/** + * @class Ext.layout.ContainerLayout + *

    This class is intended to be extended or created via the {@link Ext.Container#layout layout} + * configuration property. See {@link Ext.Container#layout} for additional details.

    + */ +Ext.layout.ContainerLayout = Ext.extend(Object, { + /** + * @cfg {String} extraCls + *

    An optional extra CSS class that will be added to the container. This can be useful for adding + * customized styles to the container or any of its children using standard CSS rules. See + * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.

    + *

    Note: extraCls defaults to '' except for the following classes + * which assign a value by default: + *

    + * To configure the above Classes with an extra CSS class append to the default. For example, + * for ColumnLayout:
    
    +     * extraCls: 'x-column custom-class'
    +     * 
    + *

    + */ + /** + * @cfg {Boolean} renderHidden + * True to hide each contained item on render (defaults to false). */ - resizeEvent: 'resize', /** - * @cfg {Array} bubbleEvents - *

    An array of events that, when fired, should be bubbled to any parent container. - * See {@link Ext.util.Observable#enableBubble}. - * Defaults to ['add', 'remove']. + * A reference to the {@link Ext.Component} that is active. For example,

    
    +     * if(myPanel.layout.activeItem.id == 'item-1') { ... }
    +     * 
    + * activeItem only applies to layout styles that can display items one at a time + * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} + * and {@link Ext.layout.FitLayout}). Read-only. Related to {@link Ext.Container#activeItem}. + * @type {Ext.Component} + * @property activeItem */ - bubbleEvents: ['add', 'remove'], // private - initComponent : function(){ - Ext.Container.superclass.initComponent.call(this); + monitorResize:false, + // private + activeItem : null, - this.addEvents( - /** - * @event afterlayout - * Fires when the components in this container are arranged by the associated layout manager. - * @param {Ext.Container} this - * @param {ContainerLayout} layout The ContainerLayout implementation for this container - */ - 'afterlayout', - /** - * @event beforeadd - * Fires before any {@link Ext.Component} is added or inserted into the container. - * A handler can return false to cancel the add. - * @param {Ext.Container} this - * @param {Ext.Component} component The component being added - * @param {Number} index The index at which the component will be added to the container's items collection - */ - 'beforeadd', - /** - * @event beforeremove - * Fires before any {@link Ext.Component} is removed from the container. A handler can return - * false to cancel the remove. - * @param {Ext.Container} this - * @param {Ext.Component} component The component being removed - */ - 'beforeremove', - /** - * @event add - * @bubbles - * Fires after any {@link Ext.Component} is added or inserted into the container. - * @param {Ext.Container} this - * @param {Ext.Component} component The component that was added - * @param {Number} index The index at which the component was added to the container's items collection - */ - 'add', - /** - * @event remove - * @bubbles - * Fires after any {@link Ext.Component} is removed from the container. - * @param {Ext.Container} this - * @param {Ext.Component} component The component that was removed - */ - 'remove' - ); + constructor : function(config){ + this.id = Ext.id(null, 'ext-layout-'); + Ext.apply(this, config); + }, - /** - * The collection of components in this container as a {@link Ext.util.MixedCollection} - * @type MixedCollection - * @property items - */ - var items = this.items; - if(items){ - delete this.items; - this.add(items); + type: 'container', + + /* Workaround for how IE measures autoWidth elements. It prefers bottom-up measurements + whereas other browser prefer top-down. We will hide all target child elements before we measure and + put them back to get an accurate measurement. + */ + IEMeasureHack : function(target, viewFlag) { + var tChildren = target.dom.childNodes, tLen = tChildren.length, c, d = [], e, i, ret; + for (i = 0 ; i < tLen ; i++) { + c = tChildren[i]; + e = Ext.get(c); + if (e) { + d[i] = e.getStyle('display'); + e.setStyle({display: 'none'}); + } + } + ret = target ? target.getViewSize(viewFlag) : {}; + for (i = 0 ; i < tLen ; i++) { + c = tChildren[i]; + e = Ext.get(c); + if (e) { + e.setStyle({display: d[i]}); + } + } + return ret; + }, + + // Placeholder for the derived layouts + getLayoutTargetSize : Ext.EmptyFn, + + // private + layout : function(){ + var ct = this.container, target = ct.getLayoutTarget(); + if(!(this.hasLayout || Ext.isEmpty(this.targetCls))){ + target.addClass(this.targetCls); } + this.onLayout(ct, target); + ct.fireEvent('afterlayout', ct, this); }, // private - initItems : function(){ - if(!this.items){ - this.items = new Ext.util.MixedCollection(false, this.getComponentId); - this.getLayout(); // initialize the layout - } + onLayout : function(ct, target){ + this.renderAll(ct, target); }, // private - setLayout : function(layout){ - if(this.layout && this.layout != layout){ - this.layout.setContainer(null); + isValidParent : function(c, target){ + return target && c.getPositionEl().dom.parentNode == (target.dom || target); + }, + + // private + renderAll : function(ct, target){ + var items = ct.items.items, i, c, len = items.length; + for(i = 0; i < len; i++) { + c = items[i]; + if(c && (!c.rendered || !this.isValidParent(c, target))){ + this.renderItem(c, i, target); + } } - this.layout = layout; - this.initItems(); - layout.setContainer(this); }, - afterRender: function(){ - // Render this Container, this should be done before setLayout is called which - // will hook onResize - Ext.Container.superclass.afterRender.call(this); - if(!this.layout){ - this.layout = 'auto'; + /** + * @private + * Renders the given Component into the target Element. If the Component is already rendered, + * it is moved to the provided target instead. + * @param {Ext.Component} c The Component to render + * @param {Number} position The position within the target to render the item to + * @param {Ext.Element} target The target Element + */ + renderItem : function(c, position, target){ + if (c) { + if (!c.rendered) { + c.render(target, position); + this.configureItem(c); + } else if (!this.isValidParent(c, target)) { + if (Ext.isNumber(position)) { + position = target.dom.childNodes[position]; + } + + target.dom.insertBefore(c.getPositionEl().dom, position || null); + c.container = target; + this.configureItem(c); + } } - if(Ext.isObject(this.layout) && !this.layout.layout){ - this.layoutConfig = this.layout; - this.layout = this.layoutConfig.type; + }, + + // private. + // Get all rendered items to lay out. + getRenderedItems: function(ct){ + var t = ct.getLayoutTarget(), cti = ct.items.items, len = cti.length, i, c, items = []; + for (i = 0; i < len; i++) { + if((c = cti[i]).rendered && this.isValidParent(c, t) && c.shouldLayout !== false){ + items.push(c); + } + }; + return items; + }, + + /** + * @private + * Applies extraCls and hides the item if renderHidden is true + */ + configureItem: function(c){ + if (this.extraCls) { + var t = c.getPositionEl ? c.getPositionEl() : c; + t.addClass(this.extraCls); } - if(Ext.isString(this.layout)){ - this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig); + + // If we are forcing a layout, do so *before* we hide so elements have height/width + if (c.doLayout && this.forceLayout) { + c.doLayout(); } - this.setLayout(this.layout); + if (this.renderHidden && c != this.activeItem) { + c.hide(); + } + }, - // If a CardLayout, the active item set - if(this.activeItem !== undefined){ - var item = this.activeItem; + onRemove: function(c){ + if(this.activeItem == c){ delete this.activeItem; - this.layout.setActiveItem(item); } + if(c.rendered && this.extraCls){ + var t = c.getPositionEl ? c.getPositionEl() : c; + t.removeClass(this.extraCls); + } + }, - // If we have no ownerCt, render and size all children - if(!this.ownerCt){ - this.doLayout(false, true); + afterRemove: function(c){ + if(c.removeRestore){ + c.removeMode = 'container'; + delete c.removeRestore; } + }, - // This is a manually configured flag set by users in conjunction with renderTo. - // Not to be confused with the flag by the same name used in Layouts. - if(this.monitorResize === true){ - Ext.EventManager.onWindowResize(this.doLayout, this, [false]); + // private + onResize: function(){ + var ct = this.container, + b; + if(ct.collapsed){ + return; + } + if(b = ct.bufferResize && ct.shouldBufferLayout()){ + if(!this.resizeTask){ + this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this); + this.resizeBuffer = Ext.isNumber(b) ? b : 50; + } + ct.layoutPending = true; + this.resizeTask.delay(this.resizeBuffer); + }else{ + this.runLayout(); } }, - /** - *

    Returns the Element to be used to contain the child Components of this Container.

    - *

    An implementation is provided which returns the Container's {@link #getEl Element}, but - * if there is a more complex structure to a Container, this may be overridden to return - * the element into which the {@link #layout layout} renders child Components.

    - * @return {Ext.Element} The Element to render child Components into. - */ - getLayoutTarget : function(){ - return this.el; + runLayout: function(){ + var ct = this.container; + this.layout(); + ct.onLayout(); + delete ct.layoutPending; }, - // private - used as the key lookup function for the items collection - getComponentId : function(comp){ - return comp.getItemId(); + // private + setContainer : function(ct){ + /** + * This monitorResize flag will be renamed soon as to avoid confusion + * with the Container version which hooks onWindowResize to doLayout + * + * monitorResize flag in this context attaches the resize event between + * a container and it's layout + */ + if(this.monitorResize && ct != this.container){ + var old = this.container; + if(old){ + old.un(old.resizeEvent, this.onResize, this); + } + if(ct){ + ct.on(ct.resizeEvent, this.onResize, this); + } + } + this.container = ct; }, /** - *

    Adds {@link Ext.Component Component}(s) to this Container.

    - *

    Description : - *

    - *

    Notes : - *

    - * @param {...Object/Array} component - *

    Either one or more Components to add or an Array of Components to add. See - * {@link #items} for additional information.

    - * @return {Ext.Component/Array} The Components that were added. + * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations + * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result) + * @param {Number|String} v The encoded margins + * @return {Object} An object with margin sizes for top, right, bottom and left */ - add : function(comp){ - this.initItems(); - var args = arguments.length > 1; - if(args || Ext.isArray(comp)){ - var result = []; - Ext.each(args ? arguments : comp, function(c){ - result.push(this.add(c)); - }, this); - return result; + parseMargins : function(v){ + if (Ext.isNumber(v)) { + v = v.toString(); } - var c = this.lookupComponent(this.applyDefaults(comp)); - var index = this.items.length; - if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){ - this.items.add(c); - // *onAdded - c.onAdded(this, index); - this.onAdd(c); - this.fireEvent('add', this, c, index); + var ms = v.split(' '), + len = ms.length; + + if (len == 1) { + ms[1] = ms[2] = ms[3] = ms[0]; + } else if(len == 2) { + ms[2] = ms[0]; + ms[3] = ms[1]; + } else if(len == 3) { + ms[3] = ms[1]; } - return c; - }, - - onAdd : function(c){ - // Empty template method - }, - - // private - onAdded : function(container, pos) { - //overridden here so we can cascade down, not worth creating a template method. - this.ownerCt = container; - this.initRef(); - //initialize references for child items - this.cascade(function(c){ - c.initRef(); - }); - this.fireEvent('added', this, container, pos); + + return { + top :parseInt(ms[0], 10) || 0, + right :parseInt(ms[1], 10) || 0, + bottom:parseInt(ms[2], 10) || 0, + left :parseInt(ms[3], 10) || 0 + }; }, /** - * Inserts a Component into this Container at a specified index. Fires the - * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the - * Component has been inserted. - * @param {Number} index The index at which the Component will be inserted - * into the Container's items collection - * @param {Ext.Component} component The child Component to insert.

    - * Ext uses lazy rendering, and will only render the inserted Component should - * it become necessary.

    - * A Component config object may be passed in order to avoid the overhead of - * constructing a real Component object if lazy rendering might mean that the - * inserted Component will not be rendered immediately. To take advantage of - * this 'lazy instantiation', set the {@link Ext.Component#xtype} config - * property to the registered type of the Component wanted.

    - * For a list of all available xtypes, see {@link Ext.Component}. - * @return {Ext.Component} component The Component (or config object) that was - * inserted with the Container's default config values applied. + * The {@link Ext.Template Ext.Template} used by Field rendering layout classes (such as + * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped, + * labeled and styled form Field. A default Template is supplied, but this may be + * overriden to create custom field structures. The template processes values returned from + * {@link Ext.layout.FormLayout#getTemplateArgs}. + * @property fieldTpl + * @type Ext.Template */ - insert : function(index, comp){ - this.initItems(); - var a = arguments, len = a.length; - if(len > 2){ - var result = []; - for(var i = len-1; i >= 1; --i) { - result.push(this.insert(index, a[i])); + fieldTpl: (function() { + var t = new Ext.Template( + '
    ', + '', + '
    ', + '
    ', + '
    ' + ); + t.disableFormats = true; + return t.compile(); + })(), + + /* + * Destroys this layout. This is a template method that is empty by default, but should be implemented + * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes. + * @protected + */ + destroy : function(){ + // Stop any buffered layout tasks + if(this.resizeTask && this.resizeTask.cancel){ + this.resizeTask.cancel(); + } + if(!Ext.isEmpty(this.targetCls)){ + var target = this.container.getLayoutTarget(); + if(target){ + target.removeClass(this.targetCls); } - return result; } - var c = this.lookupComponent(this.applyDefaults(comp)); - index = Math.min(index, this.items.length); - if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){ - if(c.ownerCt == this){ - this.items.remove(c); + } +});/** + * @class Ext.layout.AutoLayout + *

    The AutoLayout is the default layout manager delegated by {@link Ext.Container} to + * render any child Components when no {@link Ext.Container#layout layout} is configured into + * a {@link Ext.Container Container}.. AutoLayout provides only a passthrough of any layout calls + * to any child containers.

    + */ +Ext.layout.AutoLayout = Ext.extend(Ext.layout.ContainerLayout, { + type: 'auto', + + monitorResize: true, + + onLayout : function(ct, target){ + Ext.layout.AutoLayout.superclass.onLayout.call(this, ct, target); + var cs = this.getRenderedItems(ct), len = cs.length, i, c; + for(i = 0; i < len; i++){ + c = cs[i]; + if (c.doLayout){ + // Shallow layout children + c.doLayout(true); } - this.items.insert(index, c); - c.onAdded(this, index); - this.onAdd(c); - this.fireEvent('add', this, c, index); } - return c; - }, + } +}); +Ext.Container.LAYOUTS['auto'] = Ext.layout.AutoLayout; +/** + * @class Ext.layout.FitLayout + * @extends Ext.layout.ContainerLayout + *

    This is a base class for layouts that contain a single item that automatically expands to fill the layout's + * container. This class is intended to be extended or created via the layout:'fit' {@link Ext.Container#layout} + * config, and should generally not need to be created directly via the new keyword.

    + *

    FitLayout does not have any direct config options (other than inherited ones). To fit a panel to a container + * using FitLayout, simply set layout:'fit' on the container and add a single panel to it. If the container has + * multiple panels, only the first one will be displayed. Example usage:

    + *
    
    +var p = new Ext.Panel({
    +    title: 'Fit Layout',
    +    layout:'fit',
    +    items: {
    +        title: 'Inner Panel',
    +        html: '<p>This is the inner panel content</p>',
    +        border: false
    +    }
    +});
    +
    + */ +Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, { // private - applyDefaults : function(c){ - var d = this.defaults; - if(d){ - if(Ext.isFunction(d)){ - d = d.call(this, c); - } - if(Ext.isString(c)){ - c = Ext.ComponentMgr.get(c); - Ext.apply(c, d); - }else if(!c.events){ - Ext.applyIf(c, d); - }else{ - Ext.apply(c, d); - } + monitorResize:true, + + type: 'fit', + + getLayoutTargetSize : function() { + var target = this.container.getLayoutTarget(); + if (!target) { + return {}; } - return c; + // Style Sized (scrollbars not included) + return target.getStyleSize(); }, // private - onBeforeAdd : function(item){ - if(item.ownerCt){ - item.ownerCt.remove(item, false); + onLayout : function(ct, target){ + Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target); + if(!ct.collapsed){ + this.setItemSize(this.activeItem || ct.items.itemAt(0), this.getLayoutTargetSize()); } - if(this.hideBorders === true){ - item.border = (item.border === true); + }, + + // private + setItemSize : function(item, size){ + if(item && size.height > 0){ // display none? + item.setSize(size); } + } +}); +Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/** + * @class Ext.layout.CardLayout + * @extends Ext.layout.FitLayout + *

    This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be + * visible at any given time. This layout style is most commonly used for wizards, tab implementations, etc. + * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config, + * and should generally not need to be created directly via the new keyword.

    + *

    The CardLayout's focal method is {@link #setActiveItem}. Since only one panel is displayed at a time, + * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of + * the next panel to display. The layout itself does not provide a user interface for handling this navigation, + * so that functionality must be provided by the developer.

    + *

    In the following example, a simplistic wizard setup is demonstrated. A button bar is added + * to the footer of the containing panel to provide navigation buttons. The buttons will be handled by a + * common navigation routine -- for this example, the implementation of that routine has been ommitted since + * it can be any type of custom logic. Note that other uses of a CardLayout (like a tab control) would require a + * completely different implementation. For serious implementations, a better approach would be to extend + * CardLayout to provide the custom functionality needed. Example usage:

    + *
    
    +var navHandler = function(direction){
    +    // This routine could contain business logic required to manage the navigation steps.
    +    // It would call setActiveItem as needed, manage navigation button state, handle any
    +    // branching logic that might be required, handle alternate actions like cancellation
    +    // or finalization, etc.  A complete wizard implementation could get pretty
    +    // sophisticated depending on the complexity required, and should probably be
    +    // done as a subclass of CardLayout in a real-world implementation.
    +};
    +
    +var card = new Ext.Panel({
    +    title: 'Example Wizard',
    +    layout:'card',
    +    activeItem: 0, // make sure the active item is set on the container config!
    +    bodyStyle: 'padding:15px',
    +    defaults: {
    +        // applied to each contained panel
    +        border:false
         },
    +    // just an example of one possible navigation scheme, using buttons
    +    bbar: [
    +        {
    +            id: 'move-prev',
    +            text: 'Back',
    +            handler: navHandler.createDelegate(this, [-1]),
    +            disabled: true
    +        },
    +        '->', // greedy spacer so that the buttons are aligned to each side
    +        {
    +            id: 'move-next',
    +            text: 'Next',
    +            handler: navHandler.createDelegate(this, [1])
    +        }
    +    ],
    +    // the panels (or "cards") within the layout
    +    items: [{
    +        id: 'card-0',
    +        html: '<h1>Welcome to the Wizard!</h1><p>Step 1 of 3</p>'
    +    },{
    +        id: 'card-1',
    +        html: '<p>Step 2 of 3</p>'
    +    },{
    +        id: 'card-2',
    +        html: '<h1>Congratulations!</h1><p>Step 3 of 3 - Complete</p>'
    +    }]
    +});
    +
    + */ +Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, { + /** + * @cfg {Boolean} deferredRender + * True to render each contained item at the time it becomes active, false to render all contained items + * as soon as the layout is rendered (defaults to false). If there is a significant amount of content or + * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to + * true might improve performance. + */ + deferredRender : false, /** - * Removes a component from this container. Fires the {@link #beforeremove} event before removing, then fires - * the {@link #remove} event after the component has been removed. - * @param {Component/String} component The component reference or id to remove. - * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. - * Defaults to the value of this Container's {@link #autoDestroy} config. - * @return {Ext.Component} component The Component that was removed. + * @cfg {Boolean} layoutOnCardChange + * True to force a layout of the active item when the active card is changed. Defaults to false. */ - remove : function(comp, autoDestroy){ - this.initItems(); - var c = this.getComponent(comp); - if(c && this.fireEvent('beforeremove', this, c) !== false){ - this.doRemove(c, autoDestroy); - this.fireEvent('remove', this, c); - } - return c; - }, + layoutOnCardChange : false, + + /** + * @cfg {Boolean} renderHidden @hide + */ + // private + renderHidden : true, + + type: 'card', + + /** + * Sets the active (visible) item in the layout. + * @param {String/Number} item The string component id or numeric index of the item to activate + */ + setActiveItem : function(item){ + var ai = this.activeItem, + ct = this.container; + item = ct.getComponent(item); + + // Is this a valid, different card? + if(item && ai != item){ + + // Changing cards, hide the current one + if(ai){ + ai.hide(); + if (ai.hidden !== true) { + return false; + } + ai.fireEvent('deactivate', ai); + } - onRemove: function(c){ - // Empty template method - }, + var layout = item.doLayout && (this.layoutOnCardChange || !item.rendered); - // private - doRemove: function(c, autoDestroy){ - var l = this.layout, - hasLayout = l && this.rendered; + // Change activeItem reference + this.activeItem = item; - if(hasLayout){ - l.onRemove(c); - } - this.items.remove(c); - c.onRemoved(); - this.onRemove(c); - if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){ - c.destroy(); - } - if(hasLayout){ - l.afterRemove(c); - } - }, + // The container is about to get a recursive layout, remove any deferLayout reference + // because it will trigger a redundant layout. + delete item.deferLayout; - /** - * Removes all components from this container. - * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. - * Defaults to the value of this Container's {@link #autoDestroy} config. - * @return {Array} Array of the destroyed components - */ - removeAll: function(autoDestroy){ - this.initItems(); - var item, rem = [], items = []; - this.items.each(function(i){ - rem.push(i); - }); - for (var i = 0, len = rem.length; i < len; ++i){ - item = rem[i]; - this.remove(item, autoDestroy); - if(item.ownerCt !== this){ - items.push(item); + // Show the new component + item.show(); + + this.layout(); + + if(layout){ + item.doLayout(); } + item.fireEvent('activate', item); } - return items; }, + // private + renderAll : function(ct, target){ + if(this.deferredRender){ + this.renderItem(this.activeItem, undefined, target); + }else{ + Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target); + } + } +}); +Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout; +/** + * @class Ext.layout.AnchorLayout + * @extends Ext.layout.ContainerLayout + *

    This is a layout that enables anchoring of contained elements relative to the container's dimensions. + * If the container is resized, all anchored items are automatically rerendered according to their + * {@link #anchor} rules.

    + *

    This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout} + * config, and should generally not need to be created directly via the new keyword.

    + *

    AnchorLayout does not have any direct config options (other than inherited ones). By default, + * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the + * container using the AnchorLayout can supply an anchoring-specific config property of anchorSize. + * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating + * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring + * logic if necessary. For example:

    + *
    
    +var viewport = new Ext.Viewport({
    +    layout:'anchor',
    +    anchorSize: {width:800, height:600},
    +    items:[{
    +        title:'Item 1',
    +        html:'Content 1',
    +        width:800,
    +        anchor:'right 20%'
    +    },{
    +        title:'Item 2',
    +        html:'Content 2',
    +        width:300,
    +        anchor:'50% 30%'
    +    },{
    +        title:'Item 3',
    +        html:'Content 3',
    +        width:600,
    +        anchor:'-100 50%'
    +    }]
    +});
    + * 
    + */ +Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, { /** - * Examines this container's {@link #items} property - * and gets a direct child component of this container. - * @param {String/Number} comp This parameter may be any of the following: - *